#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import pandas as pd
import numpy as np
from .....utils import path
from logging import info, debug
# JvP 20210517: added import statements, MODULE_NAME module level logger
import logging
import sys
import subprocess
from .....utils.dates import date_range
from .....utils.check.errclass import CifRuntimeError
MODULE_NAME = __name__[__name__.index(
'TM5'):] if 'TM5' in __name__ else __name__
logger = logging.getLogger(MODULE_NAME)
# Original fetch function by A. Berchet
[docs]
def fetch_AB(
ref_dir,
ref_file,
input_interval,
target_dir,
tracer=None,
component=None,
**kwargs
):
"""Link individual TM5 meteo files to the working directory (original implementation).
Superseded by :func:`fetch` below, which copies the whole meteo
directory tree instead. For each month spanned by ``input_interval``
and each of a fixed set of file categories/prefixes (albedo/sr/veg
under ``an0tr1``; blh/ci/cp under ``h06h18tr1``; cld/convec/mfw under
``h06h18tr3``), links the individual source file (named via
``component.dir_<filedir>``) into ``target_dir``. A missing source
file is silently skipped (only debug-logged).
Args:
ref_dir: directory holding the TM5 meteo subdirectories.
ref_file: unused (kept for interface consistency).
input_interval: mapping of period-start dates to the list of
dates within each period; only its dates are used, expanded
to the first of each covered month.
target_dir: directory where matching files are symlinked.
tracer: unused (kept for interface consistency).
component: object exposing ``dir_an0tr1``/``dir_h06h18tr1``/
``dir_h06h18tr3`` attributes giving the sub-path (under
``ref_dir``) of each file category.
Returns:
tuple: ``(list_files, list_dates)``, both dicts keyed by the keys
of ``input_interval`` and mapping to empty lists — this
implementation does not report the dates/files it actually
linked.
"""
info(f"Copying meteo files from {ref_dir} to {target_dir}")
# File directories and prefixes
filedirs = {"an0tr1": ["albedo_%Y%m%d_00p01.nc",
"sr_%Y%m%d_00p01.nc",
"veg_%Y%m%d_00p01.nc"],
"h06h18tr1": ["blh_%Y%m%d_00p01.nc",
"ci_%Y%m%d_00p01.nc",
"cp_%Y%m%d_00p01.nc"],
"h06h18tr3": ["cld_%Y%m%d_00p03.nc",
"convec_%Y%m%d_00p03.nc",
"mfw_%Y%m%d_00p03.nc"]}
# Create the sub-directory to store meteo files
path.init_dir(target_dir)
# Loop over dates and file types
all_dates = (
pd.DatetimeIndex(
[dd for di in input_interval for dd in input_interval[di]])
+ pd.offsets.MonthBegin(0)).unique()
for date in all_dates:
for filedir in filedirs:
for filetype in filedirs[filedir]:
source = date.strftime(f"{ref_dir}/{getattr(component, f'dir_{filedir}')}/{filetype}")
target = date.strftime(
f"{target_dir}/{filetype}")
try:
path.link(source, target)
except OSError:
debug(f"Could not find file: {source}")
list_files = {datei: [] for datei in input_interval}
list_dates = {datei: [] for datei in input_interval}
return list_files, list_dates
# New fetch function by J.C.A. van Peet
[docs]
def fetch(ref_dir, ref_file, input_interval, target_dir,
tracer=None, component=None, **kwargs):
"""Link the whole TM5 meteo directory tree to the working directory.
Clears ``target_dir``, then bulk-copies the ``ref_dir`` tree into it
with ``cp -Rsn`` (directory names are recreated, files become
symlinks; ``-n`` avoids errors on already-existing targets).
Note:
Version history: 2.0 (17-05-2021, J.C.A. van Peet) replaced the
original per-file linking implementation (``fetch_AB`` above,
1.0, 28-04-2021, A. Berchet) with this bulk directory copy.
Args:
ref_dir: directory holding the TM5 meteo directory tree to copy.
ref_file: unused (kept for interface consistency).
input_interval: 2-element sequence ``(ddi, ddf)`` giving the
requested date range.
target_dir: directory where the meteo tree is copied as symlinks;
cleared before copying.
tracer: unused (kept for interface consistency).
component: unused (kept for interface consistency).
Returns:
tuple: ``(list_files, list_dates)``. ``list_files`` is a dict
with a single key (``ddi``) mapping to an empty list. ``list_dates``
maps that same key to an array of consecutive monthly
``[start, end]`` date pairs spanning ``(ddi, ddf)``.
Raises:
CifRuntimeError: if the ``cp -Rsn`` subprocess exits with a
non-zero return code.
"""
# Set the name of this function
PROG_NAME = MODULE_NAME+".fetch"
# Local logger
logger = logging.getLogger(PROG_NAME)
logger.setLevel(logging.DEBUG)
# Print debug statements
logger.debug("")
logger.debug("*"*30)
logger.debug(PROG_NAME+" => DEBUG:")
logger.debug(" ref_dir = %s", ref_dir)
logger.debug(" ref_file = %s", ref_file)
logger.debug(" input_interval = %s", input_interval)
logger.debug(" target_dir = %s", target_dir)
logger.debug(" tracer = %s", tracer)
logger.debug(" component = %s", component)
logger.debug(" kwargs = %s", kwargs)
logger.debug("*"*30)
logger.debug("")
# First clean the target directory
if os.path.isdir(target_dir):
path.remove(f"{target_dir}/*")
# Copy the meteo directory with 'cp -as' so that the files are copied
# as symbolic links, while retaining the original directory structure.
# The -n option prevents error messages if the target file already exists.
with subprocess.Popen(f'cp -Rsn {ref_dir}/* {target_dir}', shell=True, cwd='.',
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
universal_newlines=True, bufsize=1) as p:
for line in p.stdout:
logger.debug(line.strip())
# end with
#
# Check return code
if (p.returncode != 0):
print("")
print(f"{PROG_NAME} => SOME ERROR IN CMD!")
print(f" p.returncode = {p.returncode}")
print(" Computer says no!")
print("")
raise CifRuntimeError
# end if
# logger.debug("")
# logger.debug("*"*30)
# logger.debug("JvP: Computer says no!")
# logger.debug("*"*30)
# logger.debug("")
# try:
# raise RuntimeError
# except RuntimeError as e:
# #logger.exception("OOPS!")
# logger.critical(e, exc_info=True)
# #raise # => Will display the traceback on screen a second time
# sys.exit() # => Just exit.
# end try
# Apparently, this function is supposed to return two lists:
# list_files and list_dates. Not sure what the format is though,
# I should ask Antoine at some point...
ddi, ddf = input_interval
list_files = {ddi: []}
list_dates = {ddi:
np.append(
date_range(ddi, ddf, period="1MS")[:-1, np.newaxis],
date_range(ddi, ddf, period="1MS")[1:, np.newaxis],
axis=1)
}
return list_files, list_dates
# end function fetch