Source code for pycif.plugins.models.chimere.io.inputs.make_fluxes

import filecmp
import copy
import os
import shutil
import pathlib
import numpy as np
import pandas as pd
import xarray as xr
import datetime
from logging import debug
from netCDF4 import Dataset

from ......utils import path
from ......utils.hdf5 import _hdf5_lock
from .utils import replace_dates


[docs] def make_fluxes(self, datastore, runsubdir, datei, mode): """Make AEMISSIONS.nc and BEMISSIONS.nc files for CHIMERE. Use chemical scheme to check which species is needed and either take it from the datastore (i.e. when defined in the control vector), or take it from prescribed emissions Args: self (pycif.utils.classes.fluxes.Flux): Flux plugin with all attributes datastore (dict): information on flux species runsubdir (str): directory to the current run nho (int): number of hours in the run mode (str): running mode: 'fwd', 'tl' or 'adj' """ # Replace name when batch ensemble computing datastore = { (trid[0], trid[1].replace("__sample#", "")): datastore[trid] for trid in datastore if trid[0] in ["flux", "bioflux"] } # List of dates for which emissions are needed list_dates = pd.date_range(datei, periods=self.nhours + 1, freq="h") # Getting the right emissions # Loop on all anthropogenic and biogenic species # If in datastore, take data, otherwise, link to original A/B EMISSIONS list_trid = [("flux", spec) for spec in self.chemistry.emis_species.attributes] \ + [("bioflux", spec) for spec in self.chemistry.bio_species.attributes] data2dump = {} data2dump_tl = {} for trid in list_trid: spec = trid[1] emis_type = trid[0] # Update ref_trid if in ensemble mode ref_trid = (emis_type, spec) perturbed_from_reference = False if hasattr(self, "perturbed_species"): if spec in self.perturbed_species: ref_trcr = self.perturbed_species[spec] ref_trid = (emis_type, ref_trcr) # If spec not explicitly defined in datastore, # fetch general component information if available if trid in datastore: pass elif trid in [(k[0], k[1].replace("__sample#", "")) for k in datastore]: trid_ind = [ (k[0], k[1].replace("__sample#", "")) for k in datastore ].index(trid) trid = list(datastore.keys())[trid_ind] # If trid comes from an unperturbed input, but in ensemble mode # needs to fetch info from the reference species elif ref_trid in datastore: trid = ref_trid perturbed_from_reference = True elif trid not in datastore and (emis_type, "") in datastore: trid = (emis_type, "") else: continue # Bio or anthro file if emis_type == "flux": file_emisout = f"{runsubdir}/AEMISSIONS.nc" file_emisincrout = f"{runsubdir}/AEMISSIONS.increment.nc" else: file_emisout = f"{runsubdir}/BEMISSIONS.nc" file_emisincrout = f"{runsubdir}/BEMISSIONS.increment.nc" tracer = datastore[trid] tracer_data = tracer["data"][datei] # If no data is provided, just copy from original file if "spec" not in tracer_data: dirorig = tracer["dirorig"] fileorig = tracer["fileorig"] fileemis = datei.strftime(f"{dirorig}/{fileorig}") # If does not exist, just link linked = False if not os.path.isfile(file_emisout): path.link(fileemis, file_emisout) linked = True # Otherwise, check for difference if not linked or perturbed_from_reference: # First check that files are binary identical if not filecmp.cmp(fileemis, file_emisout): # Now check that spec emissions are correct with _hdf5_lock: with Dataset(file_emisout, "r") as fout: with Dataset(fileemis, "r") as fin: emisin = fin.variables[spec][:] overwrite_variable = True if spec in fout.variables: emisout = fin.variables[spec][:] overwrite_variable = ~np.all(emisin == emisout) emisin = xr.DataArray( emisin, coords={"time": list_dates}, dims=("time", "lev", "lat", "lon"), ) # If emission file is still a link, should be copied # to be able to modify it locally if overwrite_variable: if os.path.islink(file_emisout): file_orig = pathlib.Path(file_emisout).resolve() os.unlink(file_emisout) shutil.copy(file_orig, file_emisout) # Now saves to buffer variable before saving data2dump[spec] = copy.deepcopy(emisin) # # Now writes the new value for the corresponding species # flx_plg.write(spec, file_emisout, emisin) # Repeat operations for tangent linear if mode == "tl": # If does not exist, copy if not os.path.isfile(file_emisincrout): shutil.copy(fileemis, file_emisincrout) # Set variable to 0 if exists with _hdf5_lock: with Dataset(file_emisincrout, "a") as fout: if spec in fout.variables: fout.variables[spec][:] = 0.0 continue # Otherwise, add new species at zero flx_incr = xr.DataArray( np.zeros( ( len(list_dates), self.nlevemis if emis_type == "flux" else self.nlevemis_bio, self.domain.nlat, self.domain.nlon, ) ), coords={"time": list_dates}, dims=("time", "lev", "lat", "lon"), ) # Now saves to buffer variable before saving data2dump_tl[spec] = copy.deepcopy(flx_incr) # flx_plg.write(spec, file_emisincrout, flx_incr) else: # Replace existing link by copy of original file to modify it path.copyfromlink(file_emisout) # Put in dataset and write to input flx_fwd = tracer_data["spec"] # flx_plg.write(spec, file_emisout, flx_fwd) data2dump[spec] = copy.deepcopy(flx_fwd) if mode == "tl": path.copyfromlink(file_emisincrout) flx_tl = tracer_data.get("incr", 0.0 * flx_fwd) # flx_plg.write(spec, file_emisincrout, flx_tl) data2dump_tl[spec] = copy.deepcopy(flx_tl) emis_type_ref = emis_type flx_plg = self.flux if emis_type == "flux" else self.bioflux # Now dump buffer data if any if len(data2dump) > 0: flx_plg.write( list(data2dump.keys()), file_emisout, data2dump, comp_type=emis_type_ref ) # Now dump buffer data if any if len(data2dump_tl) > 0: flx_plg.write( list(data2dump_tl.keys()), file_emisincrout, data2dump_tl, comp_type=emis_type_ref ) # Check that the dates are consistent with what CHIMERE expects replace_dates(file_emisout, list_dates, self.ignore_input_dates) if mode == "tl": replace_dates(file_emisincrout, list_dates, self.ignore_input_dates)