Source code for pycif.plugins.datastreams.fluxes.dalecbethy.fetch
import datetime
import os
import pandas as pd
import xarray as xr
from .....utils import path
from .....utils.check.errclass import CifError
from .....utils.hdf5 import _hdf5_lock
[docs]
def fetch(ref_dir, ref_file, input_interval, target_dir, tracer=None, **kwargs):
"""Fetch a D&B forcing file and list the period(s) it covers.
D&B writes its whole forcing to a single NetCDF file: it is linked once
into ``target_dir``, and, if the file has a ``time`` coordinate (D&B's
hourly *dynamic* forcing, e.g. ``dynforcing.nc``), it is read to list
the hourly sub-periods overlapping ``input_interval``. Time-invariant
*static* forcing files (e.g. ``staticforcing.nc``, see
``src/ncread_forcing.f90::ncread_static_forcing``) have no ``time``
coordinate: they are instead listed as a single period covering the
whole of ``input_interval``.
Args:
ref_dir (str): directory where the forcing file is found.
ref_file (str): name of the forcing file.
input_interval (list): simulation interval, as a list of the two
bounding dates.
target_dir (str): directory where a link to the forcing file is
created.
tracer: the tracer Plugin, giving access to ``name``.
**kwargs: unused, kept for interface compatibility.
Returns:
(dict, dict): ``list_files`` and ``list_dates``, both with a single
key (the first covered hour, or ``input_interval``'s start for
static forcing), following the same ``{key: [...per-period...]}``
convention as other fluxes plugins (see e.g.
``pycif.plugins.datastreams.fluxes.chimere.fetch.fetch``).
Raises:
CifError: if the forcing file is not found, or (dynamic forcing
only) has no ``time`` record overlapping ``input_interval``.
"""
file = os.path.join(ref_dir, ref_file)
if not os.path.isfile(file):
raise CifError(
f"Could not find the D&B forcing file for tracer "
f"{tracer.name}:\n - ref_dir: {ref_dir}\n - ref_file: {ref_file}"
)
with _hdf5_lock:
with xr.open_dataset(file) as ds:
has_time = "time" in ds
date_i, date_f = input_interval
if has_time:
with _hdf5_lock:
with xr.open_dataset(file) as ds:
times = pd.DatetimeIndex(ds["time"].values)
hours = times[(times > date_i) & (times <= date_f)]
if hours.empty:
raise CifError(
f"D&B dynamic forcing file '{file}' has no 'time' record "
f"within the requested interval {input_interval} for tracer "
f"{tracer.name}."
)
ddi = hours[0].to_pydatetime() - datetime.timedelta(hours=1)
list_dates = {
ddi: [
[hh.to_pydatetime() - datetime.timedelta(hours=1), hh.to_pydatetime()]
for hh in hours
]
}
list_files = {ddi: len(hours) * [file]}
else:
# Static forcing: time-invariant, so a single period covering the
# whole requested interval is enough.
list_dates = {date_i: [[date_i, date_f]]}
list_files = {date_i: [file]}
# Fetching
target_file = os.path.join(target_dir, os.path.basename(file))
path.link(file, target_file)
return list_files, list_dates