Source code for vortex_cen.tasks.safran.reanalysis

# -*- coding:Utf-8 -*-
"""
reanalysis.py
--------------

.. autoclass:: SafranReanalysis
   :no-members:
   :class-doc-from: class
   :show-inheritance:
"""

__all__ = []

import vortex
from vortex.syntax.stdattrs import nativefmt

from vortex_cen.tasks.research_task_base import _CenResearchTask


[docs] class SafranReanalysis(_CenResearchTask): """ **Task : SafranReanalysis** SAFRAN reanalysis. Reference : https://essd.copernicus.org/articles/14/1707/2022/ Associated opensource dataset : https://doi.org/10.25326/37#v2020.2 **Input:** - Guess : daily packed files containing all lead time of a given 0H run of ARPEGE / PERAP - Observations : Packed SAFRAN-readable surface observation files (R, S and T files) - listem : List of SAFRAN massifs - listeml : List of coordinates of the SAFRAN massifs - Listeo : list of potential observation sites to assimilate - NORELmt : Monthly mean precipitation value - rsclim / icrccm : Climatological values - ADAPT/ANALYSE/EBAUCHE/IMPRESS/MELANGE/SORTIES : Safran namelists - carpost.tar : Files describing the output "postes" - safrane : Safran executable for synoptic interpolation of the guess on the Safran geometry - syrpluie / syrmRR : Safran executables for precipitation spatio-temporal precipitation interpolation - sytist : Safran executable for hourly interpolation and the creation of FORCING files **Output:** - FORCING_massifs.nc : Ensemble of forcing files on the "flat" massif geometry - FORCING_postes.nc : Ensemble of forcing files on the "postes" geometry - listings_safran : output safran execution listings - liste_obs : List of assimilated observations **Mandatory Configuration Variables:** * ``datebegin`` First rundate of the guess (hour must be '00') * ``dateend`` Last run date of the guess (hour must be '00') * ``xpid`` Experiment id. Do not use experiment ids with 4 letters. * ``geometry`` Geometry of the simulation. This must be a valid geometry in your '$HOME/.vortexrc/geometries.ini' file. * ``guess_geometry`` Geometry of SAFRAN guess files. type: dict * ``obs_geometry`` geometry of the observation files. type: dict * ``uenv`` Name of the UEnv containing all SAFRAN constant input files and executables * ``ntasks`` Number of parallel tasks to allocate to the execution. type: int or dict[geometry] * ``nnodes`` Number of nodes to allocate to the execution. type: int or dict[geometry] * ``execution`` Type of SAFRAN execution. type: str, choices: analysis, forecast, reanalysis, reforecast * ``assim`` Allow assimilation of observations. type: bool **Optional Configuration Variables:** * ``obs_xpid`` Experiment identifier of the observation files. type: str, default: *xpid* * ``obs_vapp`` *vapp* level of the observation files. type: str, default: *vapp* * ``obs_vconf`` *vconf* level of the observation files. type: str, default: *vconf* * ``obs_user`` Name of the producer of the observation files. type: str * ``diff_xpid`` Experiment identifier of the reference files for reproducibility check. type: str * ``diff_user`` Name of the producer of the reference files for reproducibility check. type: str """ def __init__(self, **kw): MANDATORY_CONFIGURATION_VARIABLES = [ "datebegin", "dateend", "xpid", "geometry", "guess_geometry+help=Geometry of SAFRAN guess files;type=dict", "obs_geometry+help=geometry of the observation files;type=dict", "uenv+help=Name of the UEnv containing all SAFRAN constant input files and executables", "ntasks", "nnodes", "execution+help=Type of SAFRAN execution;type=str;choices=analysis, forecast, reanalysis, reforecast", "assim+help=Allow assimilation of observations;type=bool", ] OPTIONAL_CONFIGURATION_VARIABLES = [ "obs_xpid+help=Experiment identifier of the observation files;type=str;default=*xpid*", "obs_vapp+help=*vapp* level of the observation files;type=str;default=*vapp*", "obs_vconf+help=*vconf* level of the observation files;type=str;default=*vconf*", "obs_user+help=Name of the producer of the observation files;type=str", "diff_xpid+help=Experiment identifier of the reference files for reproducibility check;type=str", "diff_user+help=Name of the producer of the reference files for reproducibility check;type=str", ] super().__init__(**kw) self.update_attributes(MANDATORY_CONFIGURATION_VARIABLES, OPTIONAL_CONFIGURATION_VARIABLES) def get_local_inputs(self): pass def launch_algo(self, algo): pass def get_remote_inputs(self): def untar_hook(t, rh): sh = t.sh target = rh.container.localpath() with sh.cdcontext(sh.path.dirname(target)): tarname = sh.path.basename(target) if sh.is_tarfile(tarname): sh.untar(tarname) t = self.ticket rundate = self.conf.datebegin list_dates = self.get_list_seasons(self.conf.datebegin, self.conf.dateend) for rundate in list_dates: datebegin = rundate dateend = rundate.replace(year = rundate.year + 1) # dateend = rundate + Period(years=1) season = datebegin.nivologyseason y1 = datebegin.year y2 = dateend.year self.sh.title(f'Input Guess - {y1:d}/{y2:d}') tb01 = vortex.input( role = 'Ebauche', kind = 'packedguess', local = '{0:s}_{1:s}/guess{2:s}.tar'.format(datebegin.ymd6h, dateend.ymd6h, season), namespace = 's2m.archive.fr', geometry = self.conf.guess_geometry[self.conf.geometry.area], cumul = self.conf.get('cumul', 6), nativefmt = 'tar', model = 'safran', source = 'era5', date = dateend.ymdh, datebegin = datebegin.ymdh, dateend = dateend.ymdh, now = True, fatal = True, ), print(t.prompt, 'tb01 =', tb01) print() if self.conf.assim: self.sh.title(f'Input Observations - {y1:d}/{y2:d}') tb02 = vortex.input( role = 'Observations', # part = 'all', vapp = self.conf.get("obs_vapp", self.conf.vapp), vconf = self.conf.get("obs_vconf", self.conf.vconf), geometry = self.conf.obs_geometry[self.conf.geometry.area], kind = 'packedobs', local = '{0:s}_{1:s}/rs{2:s}.tar'.format(datebegin.ymd6h, dateend.ymd6h, season), experiment = self.conf.get('obs_xpid', self.conf.xpid), username = self.conf.get('obs_user', None), namespace = 'vortex.multi.fr', date = dateend.ymdh, datebegin = datebegin.ymdh, dateend = dateend.ymdh, model = 'safran', source = 'surfaceobs', namebuild = 'flat@cen', block = 'observations', nativefmt = 'tar', now = True, fatal = True, # Untar is not automatic for resrouces from the "ArchiveStore", unlike # for resources coming from the "Finder" store hook_autohook1 = (untar_hook, ), ) print(t.prompt, 'tb02 =', tb02) print() self.sh.title('Input listem') tb07 = vortex.input( role = 'ListeMassif', genv = self.conf.uenv, gdomain = '[geometry:domain]', geometry = self.conf.geometry, kind = 'listem', nativefmt = "ascii", model = 'safran', local = 'listem', ) print(t.prompt, 'tb07 =', tb07) print() self.sh.title('Input listeml') tb08 = vortex.input( role = 'ListeLimitesMassif', genv = self.conf.uenv, gdomain = '[geometry:domain]', geometry = self.conf.geometry, kind = 'listeml', nativefmt="ascii", model = 'safran', local = 'listeml', ) print(t.prompt, 'tb08 =', tb08) print() self.sh.title('Input listeo') tb09 = vortex.input( role = 'ListePost', genv = self.conf.uenv, gdomain = '[geometry:domain]', geometry = self.conf.geometry, kind = 'listeo', nativefmt="ascii", model = 'safran', local = 'listeo', ) print(t.prompt, 'tb09 =', tb09) print() self.sh.title('Input NORELot') tb09 = vortex.input( role = 'MoyennesMensuellesRR', genv = self.conf.uenv, gdomain = '[geometry:domain]', geometry = self.conf.geometry, kind = 'NORELot', nativefmt="ascii", model = 'safran', local = 'NORELot', fatal = False, ) print(t.prompt, 'tb09 =', tb09) print() self.sh.title('Input surfz') tb09 = vortex.input( role = 'SurfZ', genv = self.conf.uenv, gdomain = '[geometry:domain]', geometry = self.conf.geometry, kind = 'surfz', model = 'safran', local = 'surfz', nativefmt = 'ascii', ) print(t.prompt, 'tb09 =', tb09) print() self.sh.title('Input CARPOST') tb09 = vortex.input( role = 'carac_post', genv = self.conf.uenv, gdomain = '[geometry:domain]', geometry = self.conf.geometry, kind = 'carpost', model = 'safran', nativefmt='ascii', local = 'carpost.tar', ) print(t.prompt, 'tb09 =', tb09) print() self.sh.title('Input Blacklist') tb12 = vortex.input( role = 'BlackList', genv = self.conf.uenv, gdomain = '[geometry:domain]', geometry = self.conf.geometry, kind = 'blacklist', model = 'safran', local = 'BLACK', nativefmt='ascii', fatal = False, ) print(t.prompt, 'tb12 =', tb12) print() self.sh.title('Input NORELmt') tb08 = vortex.input( role = 'NormalesClimTT', genv = self.conf.uenv, gdomain = '[geometry:domain]', geometry = self.conf.geometry, kind = 'NORELmt', model = 'safran', nativefmt='ascii', local = 'NORELmt', fatal = False, ) print(t.prompt, 'tb08 =', tb08) print() self.sh.title('Input rsclim') tb11 = vortex.input( role = 'Clim', genv = self.conf.uenv, gdomain = '[geometry:domain]', geometry = self.conf.geometry, kind = 'rsclim', model = 'safran', nativefmt='ascii', local = 'rsclim.don', fatal = False, ) print(t.prompt, 'tb11 =', tb11) print() self.sh.title('Input icrccm') tb12 = vortex.input( role = 'Clim', genv = self.conf.uenv, gdomain = '[geometry:domain]', geometry = self.conf.geometry, kind = 'icrccm', model = 'safran', nativefmt='ascii', local = 'icrccm.don', fatal = False, ) print(t.prompt, 'tb12 =', tb12) print() self.sh.title('Input Namelists') tb13 = vortex.input( source = 'namelist_[local::lower]_[geometry:domain]', geometry = self.conf.geometry, genv = self.conf.uenv, kind = 'namelist', model = 'safran', local = ['SORTIES', 'ANALYSE', 'ADAPT', 'MELANGE', 'OBSERVR', 'OBSERVA', 'EBAUCHE'], fatal = False, ) print(t.prompt, 'tb13 =', tb13) print() self.sh.title('Executable safrane') self.safrane = vortex.executable( role = 'Binary', genv = self.conf.uenv, kind = 'safrane', local = 'safrane', model = 'safran', ) print(t.prompt, 'tb17 =', self.safrane) print() self.sh.title('Executable syrpluie') self.syrpluie = vortex.executable( role = 'Binary', genv = self.conf.uenv, kind = 'syrpluie', local = 'syrpluie', model = 'safran', ) print(t.prompt, 'tb18 =', self.syrpluie) print() if self.conf.assim: self.sh.title('Executable sypluie') self.sypluie = vortex.executable( role = 'Binary', genv = self.conf.uenv, kind = 'sypluie', local = 'sypluie', model = 'safran', ) print(t.prompt, 'tb18_b =', self.sypluie) print() self.sh.title('Executable syvapr') self.syvapr = vortex.executable( role = 'Binary', genv = self.conf.uenv, kind = 'syvapr', local = 'syvapr', model = 'safran', ) print(t.prompt, 'tb19 =', self.syvapr) print() self.sh.title('Executable syvafi') self.syvafi = vortex.executable( role = 'Binary', genv = self.conf.uenv, kind = 'syvafi', local = 'syvafi', model = 'safran', ) print(t.prompt, 'tb20 =', self.syvafi) print() else: self.sh.title('Executable syrmRR') self.syrmrr = vortex.executable( role = 'Binary', genv = self.conf.uenv, kind = 'syrmrr', local = 'syrmRR', model = 'safran', ) print(t.prompt, 'tb13 =', self.syrmrr) print() self.sh.title('Executable sytist') self.sytist = vortex.executable( role = 'Binary', genv = self.conf.uenv, kind = 'sytist', local = 'sytist', model = 'safran', ) print(t.prompt, 'tb21 =', self.sytist) print() def algo(self): t = self.ticket # NB : La date des executions est fixée à J-1 car l'analyse SAFRAN va de J-1 6h à J 6H self.sh.title('Algo SAFRANE') tb22 = tbalgo1 = vortex.task( engine = 'blind', # vortex2 # engine = 's2m', # vortex1 kind = 'safrane', datebegin = self.conf.datebegin.ymd6h, dateend = self.conf.dateend.ymd6h, ntasks = self.conf.ntasks, execution = self.conf.execution, ) print(t.prompt, 'tb22 =', tb22) print() self.component_runner(tbalgo1, self.safrane) # if self.conf.execution == 'analysis': # Cas d'une execution où l'on veut utiliser les rr ARPEGE comme guess # A lancer avec un job_name=with_rr_arpege (cf fichier de conf) # WARNING : si execution="analysis" l'algo component définit les # observations comme des fichiers "communs", ce qui n'est pas le # cas. # Les obs sont néanmoins bien assimilées car si les fichiers d'obs ne sont pas # au chemin indiqué dans les fichiers OP* (ce qui dépend de la variable "execution"), # SAFRAN les cherche dans le répertoire courrant. self.sh.title('Algo SYRPLUIE') tb23 = tbalgo2 = vortex.task( engine = 'blind', # vortex2 # engine = 's2m', # vortex1 kind = 'syrpluie', datebegin = self.conf.datebegin.ymd6h, dateend = self.conf.dateend.ymd6h, ntasks = self.conf.ntasks, execution = self.conf.execution, ) print(t.prompt, 'tb23 =', tb23) print() self.component_runner(tbalgo2, self.syrpluie) if self.conf.assim: self.sh.title('Algo SYPLUIE') tb23 = tbalgo3 = vortex.task( engine = 'blind', # vortex2 # engine = 's2m', # vortex1 kind = 'sypluie', datebegin = self.conf.datebegin.ymd6h, dateend = self.conf.dateend.ymd6h, ntasks = self.conf.ntasks, execution = self.conf.execution, ) print(t.prompt, 'tb23 =', tb23) print() self.component_runner(tbalgo3, self.sypluie) self.sh.title('Algo SYVAPR') tb24 = tbalgo4 = vortex.task( engine = 'blind', # vortex2 # engine = 's2m', # vortex1 kind = 'syvapr', datebegin = self.conf.datebegin.ymd6h, dateend = self.conf.dateend.ymd6h, ntasks = self.conf.ntasks, execution = self.conf.execution, ) print(t.prompt, 'tb24 =', tb24) print() self.component_runner(tbalgo4, self.syvapr) self.sh.title('Algo SYVAFI') tb25 = tbalgo5 = vortex.task( engine = 'blind', # vortex2 # engine = 's2m', # vortex1 kind = 'syvafi', datebegin = self.conf.datebegin.ymd6h, dateend = self.conf.dateend.ymd6h, ntasks = self.conf.ntasks, execution = self.conf.execution, ) print(t.prompt, 'tb25 =', tb25) print() self.component_runner(tbalgo5, self.syvafi) else: self.sh.title('Algo SYRMRR') tb17 = tbalgo3 = vortex.task( engine = 'blind', kind = 'syrmrr', datebegin = self.conf.datebegin.ymd6h, dateend = self.conf.dateend.ymd6h, ntasks = self.conf.ntasks, execution = self.conf.execution, ) print(t.prompt, 'tb17 =', tb17) print() self.component_runner(tbalgo3, self.syrmrr) self.sh.title('Algo SYTIST') tb26 = tbalgo6 = vortex.task( engine = 'blind', # vortex2 # engine = 's2m', # vortex1 kind = 'sytist', datebegin = self.conf.datebegin.ymd6h, dateend = self.conf.dateend.ymd6h, ntasks = self.conf.ntasks, execution = self.conf.execution, #metadata = 'StandardSAFRAN', ) print(t.prompt, 'tb26 =', tb26) print() self.component_runner(tbalgo6, self.sytist) def put_outputs(self): t = self.ticket rundate = self.conf.datebegin list_dates = self.get_list_seasons(self.conf.datebegin, self.conf.dateend) for rundate in list_dates: datebegin = rundate dateend = min(datebegin.replace(year=datebegin.year + 1), self.conf.dateend) self.sh.title('Output forcing massifs') tb27 = vortex.output( role = 'Ana_massifs', kind = 'MeteorologicalForcing', cutoff = 'assimilation', local = '[datebegin::ymd6h]_[dateend::ymd6h]/' 'FORCING_massif_[datebegin::ymd6h]_[dateend::ymd6h].nc', experiment = self.conf.xpid, block = 'safran/massifs', geometry = self.conf.geometry, nativefmt = 'netcdf', datebegin = datebegin.ymd6h, dateend = dateend.ymd6h, date = dateend.ymd6h, namespace = 'vortex.multi.fr', namebuild = 'flat@cen', ), print(t.prompt, 'tb27 =', tb27) print() self.sh.title('Output forcing postes') tb27 = vortex.output( role = 'Ana_postes', kind = 'MeteorologicalForcing', cutoff = 'assimilation', local = '[datebegin::ymd6h]_[dateend::ymd6h]/' 'FORCING_postes_[datebegin::ymd6h]_[dateend::ymd6h].nc', experiment = self.conf.xpid, block = 'safran/postes', geometry = self.conf.geometry, nativefmt = 'netcdf', datebegin = datebegin.ymd6h, dateend = dateend.ymd6h, date = dateend.ymd6h, namespace = 'vortex.multi.fr', namebuild = 'flat@cen', ), print(t.prompt, 'tb28 =', tb27) print() self.sh.title('Output liste observations') tb29 = vortex.output( role = 'Liste_obs', block = 'safran/liste_obs', experiment = self.conf.xpid, geometry = self.conf.geometry, cutoff = 'assimilation', nativefmt = 'tar', model = 'safran', kind = 'listobs', datebegin = datebegin.ymd6h, dateend = dateend.ymd6h, date = dateend.ymd6h, local = '[datebegin::ymd6h]_[dateend::ymd6h]/' 'liste_obs_[datebegin::ymd6h]_[dateend::ymd6h].tar.gz', namespace = 'vortex.multi.fr', namebuild = 'flat@cen', ) print(t.prompt, 'tb32 =', tb29) print() self.sh.title('Output listings') tb31 = vortex.output( role = 'Listing', block = 'safran/listing', experiment = self.conf.xpid, cutoff = 'assimilation', geometry = self.conf.geometry, kind = 'packedlisting', datebegin = datebegin.ymd6h, dateend = dateend.ymd6h, date = dateend.ymd6h, local = '[datebegin::ymd6h]_[dateend::ymd6h]/' 'listings_safran_[datebegin::ymdh]_[dateend::ymdh].tar.gz', nativefmt = 'tar', model = 'safran', namespace = 'vortex.multi.fr', namebuild = 'flat@cen', ) print(t.prompt, 'tb31 =', tb31) print() def diff(self): output = self.ticket.context.sequence.effective_outputs(role='Ana_massifs') for out in output: filename = out.rh.container.filename datebegin = out.rh.resource.datebegin dateend = out.rh.resource.dateend geometry = out.rh.resource.geometry self.sh.title('Output diff') diff = vortex.diff( kind = 'MeteorologicalForcing', datebegin = datebegin, dateend = dateend, geometry = geometry, experiment = self.conf.diff_xpid, username = self.conf.get('diff_user', None), block = 'safran/massifs', namebuild = 'flat@cen', local = filename, vortex1 = self.conf.get("diff_vortex1", False), ), print(self.ticket.prompt, 'diff =', diff) print()