import datetime
from logging import info
import os
from pathlib import Path
import shutil
import subprocess
import numpy as np
from ..chimere.flushrun import flush_rundir
from ..chimere.io.outputs.fetch_end import make_aend, make_end
from ..chimere.io.outputs.make_aout import make_aout
from ..chimere.io.outputs.read_sim import (
read_obs,
readend,
readmeteo,
write_obs,
write_sim,
)
from ..chimere.utils import check_inputs
[docs]
def run(
self,
runsubdir,
mode,
workdir,
ddi,
nbproc=1,
do_simu=True,
approx_transf=False,
ref_fwd_dir="",
overlap=False,
**kwargs,
):
"""Run the CHIMERE model in forward mode
Args:
self: the model Plugin
runsubdir (str): working directory for the current run
mode (str): forward or backward
workdir (str): pyCIF working directory
do_simu (bool): re-run or not existing simulation
"""
# Check mode
if mode not in ["fwd", "tl", "adj"]:
raise ValueError(f"Unknown mode {mode} for CHIMERE execution")
# Resetting observation flag for updating obs.txt
self.iniobs[ddi] = False
self.reset_obs[ddi] = True
if not do_simu:
# if mode in ["fwd", "tl"]:
# # Keeps the running directory in memory for later adjoint
# # simulations
# self.adj_refdir = "{}/../".format(runsubdir)
return
# Cleaning the directory
mod_file = Path(runsubdir, "mod.txt")
if mod_file.is_file():
mod_file.unlink()
nho = self.nho
list_spec_stop = self.list_spec_stop
list_thld_stop = self.list_thld_stop
actual_run = self.runsimu[ddi]
if actual_run and not approx_transf:
# In adjoint mode, setting sensitivities to zero if overlap period
if mode == "adj" and overlap:
obs_scheme = read_obs(runsubdir, mode="adj")
write_obs(obs_scheme, runsubdir)
# Check that inputs have been correctly created
check_inputs(self, runsubdir, mode)
# env for number of procs and time tracking
env = {**os.environ.copy()}
if hasattr(self, "nproc"):
env.update({"ACC_NUM_CORES": str(self.nproc)})
# Running the model
info(f"Running sub-simulation in {runsubdir}")
with open(Path(runsubdir, "chimere.log"), "w") as log:
if self.keep_log_stderr:
info("Writing standard error in prof.log")
prof = open(Path(runsubdir, "prof.log"), "w")
env.update({"NVCOMPILER_ACC_TIME": "TRUE"})
else:
prof = subprocess.PIPE
info("ENV ENV ENV" + str(env))
process = subprocess.Popen(
["./chimere.e"],
cwd=runsubdir,
env=env,
stdout=log,
stderr=prof,
)
_, stderr = process.communicate()
stderr = stderr.decode("utf-8")
if self.keep_log_stderr:
prof.close()
if not Path(runsubdir, "all_good").is_file():
info("Exception in CHIMERE")
if not self.keep_log_stderr:
info(stderr)
raise RuntimeError(f"CHIMERE did not run properly in {runsubdir}")
elif stderr:
if not self.keep_log_stderr:
with open(Path(runsubdir, "chimere.errlog"), "w") as f:
f.write(stderr)
if mode in ["fwd", "tl"]:
if list_spec_stop:
# stopORmore activated
# generally in case of computing response fucntions
# test to stop if concentrations are all small enough
list_spec_end, conc_end = readend("{runsubdir}/end.nc")
# conversion from molec.cm-3 to ppb: /(airm[0,l,i,j]*1e-9)
airm = readmeteo("{}/METEO.nc".format(runsubdir), ["airm"])[0]
nspresc = self.chemistry.nprspecies
stopcalc = True
for spec in list_spec_end[: len(list_spec_end) - nspresc]:
if spec in list_spec_stop:
conc_end_ppb = np.divide(
conc_end[list_spec_end.index(spec)], airm[int(nho), :, :, :]
)
conc_end_ppb = 1.0e9 * conc_end_ppb
maxspec = np.amax(conc_end_ppb)
if maxspec > list_thld_stop[list_spec_stop.index(spec)]:
stopcalc = False
if stopcalc:
info("STOPPING calculations because concentrations small enough ")
# put all posterior dates to False for runsimu
for dd in self.runsimu:
if dd > ddi:
self.runsimu[dd] = False
# Putting all simulated concentrations to zero if overlap period
if overlap:
obs_scheme = read_obs(runsubdir)
write_sim(obs_scheme, runsubdir)
else:
info(
f"NOT running sub-simulation in {runsubdir} "
"due to choice of stopORmore function "
"or approximating operator"
)
# Mimic all_good
Path(runsubdir, "all_good").touch()
# Make artificial outputs
if mode in ["fwd", "tl"]:
# Mimic mod.txt (with 0 for the simulation)
obs_scheme = read_obs(runsubdir)
write_sim(obs_scheme, runsubdir)
# Make empty end.nc for safe linking
# For stopped simulation, just make random link
if not actual_run:
Path(runsubdir, "end.nc").touch()
# Use end concentrations from reference forward directory
else:
make_end(self, "{}/end.nc".format(runsubdir), ddi, ref_fwd_dir)
else:
# Make empty aend.nc for safe linking
make_aend(self, "{}/aend.nc".format(runsubdir), ddi)
# Mimick empty aout files
make_aout(self, runsubdir, ddi)
# Process here initial conditions for the next simulation
date = datetime.datetime.strptime(os.path.basename(runsubdir), "%Y-%m-%d_%H-%M")
if mode in ["fwd", "tl"]:
prefix = "end"
elif mode == "adj":
prefix = "aend"
else:
raise ValueError(f"Unknown mode {mode} for CHIMERE execution")
source = f"{prefix}.nc"
target = date.strftime(f"{prefix}.%Y%m%d%H.{nho}.nc")
shutil.move(
Path(runsubdir, source),
Path(runsubdir).parent.joinpath("chain", target),
)
# if mode in ["fwd", "tl"]:
# # Keeps the running directory in memory for later adjoint simulations
# self.adj_refdir = "{}/../".format(runsubdir)
# Clean run subdirectory to avoid over-loading disks
if self.force_clean_run:
flush_rundir(runsubdir, mode)