import numpy as np
from logging import info
# Mandatory hourly variables of 'input/dynforcing.nc', see
# src/ncread_forcing.f90::ncread_dynamic_forcing. Shared with
# pycif.plugins.models.dalecbethy.io.inputs.make_forcing, which rebuilds this
# same file from these components' data-store entries.
meteo_components = [
"swrad",
"temperature",
"precipitation",
"lwdown",
"soil_temperature",
]
# Mandatory/optional variables of 'input/staticforcing.nc', see
# src/ncread_forcing.f90::ncread_static_forcing (one canonical name kept per
# variable, even where the Fortran reader also accepts an alias). Shared with
# pycif.plugins.models.dalecbethy.io.inputs.make_forcing, which rebuilds this
# same file from these components' data-store entries.
static_components = [
"condition_simulate",
"pft",
"pft_fraction",
"lon",
"lat",
"soil_depth",
"soil_texture_class",
"B_r",
"soil_brightness_class",
"elevation",
"vegetation_fraction",
]
[docs]
def ini_mapper(model, general_mapper={}, backup_comps={},
transforms_order=[], ref_transform="",
transform_name="", all_transforms=None, **kwargs):
"""Build the data-flow mapper for the D&B bottom-up CO2 flux model.
Declares:
* **Inputs**:
- D&B's own control vector: the prior parameters read from
``input/{core,sif,lvod,slope}-params.csv`` (``src/prior.f90``),
following the same ``("<model>_param", <component>)`` convention as
``satwetch4``'s ``("satwetch4_model_param", "k"/"q10")`` inputs.
- The time-invariant land-surface/PFT fields read from
``input/staticforcing.nc`` (``src/ncread_forcing.f90::
ncread_static_forcing``), as ``("dalecbethy_static", <component>)``.
- The hourly meteorological forcing read from
``input/dynforcing.nc`` (``src/ncread_forcing.f90::
ncread_dynamic_forcing``), as ``("meteo", <component>)``, following
the same convention as ``chimere``'s/``satwetch4``'s ``("meteo", ...)``
inputs.
* **Outputs** -- the NEE (net ecosystem exchange) flux computed by D&B
(``src/obsop.f90``/``src/timeloop.f90``, dumped to
``diagout/dalec-bethy_{hourly,daily}-output_*.nc``, variable ``nee``,
see ``src/ncwrite_output.f90``), following the same
``("flux", <component>)`` convention as ``satwetch4``'s
``("flux", "CH4_wetlands")`` output.
.. warning::
TODO for a D&B developer:
1. Both the dynamic (meteo) and static forcing declared here are
wired through :func:`~.io.native2inputs.native2inputs`/
:func:`~.io.inputs.make_forcing.make_meteo`/
:func:`~.io.inputs.make_forcing.make_static`, which (re)write
``input/dynforcing.nc``/``input/staticforcing.nc`` from the
datastore instead of linking the original files (see
:func:`~.io.inputs.make_auxiliary.make_auxiliary`, which no
longer links either of them).
2. The four ``("dalecbethy_param", <kind>)`` inputs are
scalar-per-PFT values (one row per ``varname``/``PFT`` pair, see
the parameters CSV files), not gridded fields, so the
``gridded_netcdf`` transport used by ``satwetch4`` for
``model_param_k``/``model_param_q10`` likely needs dedicated
handling in :func:`~.io.native2inputs.native2inputs` rather than
applying as-is.
Args:
model: dalecbethy plugin instance with all date arrays set.
general_mapper (dict): unused.
backup_comps (dict): unused.
transforms_order (list): unused.
ref_transform (str): unused.
transform_name (str): unused.
all_transforms: unused.
**kwargs: unused.
Returns:
dict: mapper with ``inputs`` and ``outputs``.
"""
input_intervals = {
ddi: np.append(
model.input_dates[ddi][:-1, np.newaxis],
model.input_dates[ddi][1:, np.newaxis],
axis=1)
for ddi in model.input_dates}
# Static forcing fields are time-invariant: read once, at the start of
# the simulation window, following the same convention as CHIMERE's
# ("inicond", s) inputs (dict_ini), which are also only needed at
# model.datei.
static_intervals = {model.datei: np.array([[model.datei, model.datef]])}
output_intervals = {
ddi: np.append(
model.tstep_dates[ddi][:-1, np.newaxis],
model.tstep_dates[ddi][1:, np.newaxis],
axis=1)
for ddi in model.tstep_dates}
default_input_dict = {
"input_dates": input_intervals,
"force_dump": True,
"domain": model.domain,
"sampled": False,
"sparse_data": False,
"force_loadin": False,
}
default_static_dict = {
"input_dates": static_intervals,
"force_dump": True,
"domain": model.domain,
"sampled": False,
"sparse_data": False,
"force_loadin": False,
}
mapper = {
"inputs": {
# **{("dalecbethy_param", kind): default_input_dict
# for kind in ("core", "sif", "lvod", "slope")},
**{("dalecbethy_static", comp): default_static_dict
for comp in static_components},
**{("meteo", comp): default_input_dict
for comp in meteo_components},
},
"outputs": {
("flux", comp): {
"input_dates": output_intervals,
"force_loadout": True,
"domain": model.domain,
"sampled": False,
"sparse_data": False,
}
for comp in model.output_components
},
}
info("The D&B model was initialized with the following mapper:")
info("\nInputs:\n\n")
for key in mapper["inputs"]:
info(f"\t{key}: \t {mapper['inputs'][key]}\n")
info("\n\n\nOutputs:\n\n")
for key in mapper["outputs"]:
info(f"\t{key}: \t {mapper['outputs'][key]}\n")
return mapper