Source code for pycif.plugins.models.chimere_acc.run

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)