Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature #2283 time looping consolidation #2311

Merged
merged 56 commits into from
Aug 30, 2023
Merged
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
3c74be4
added helper function to clean up logic to add new dictionaries that …
georgemccabe Aug 8, 2023
60662e4
fix incorrect variable name
georgemccabe Aug 8, 2023
927d631
log error if wrapper was not loaded properly to help debug issue
georgemccabe Aug 8, 2023
ffe1f23
improve logging to print input dir/template once at beginning to avoi…
georgemccabe Aug 8, 2023
fee61a2
make wrappers LoopTimes (RuntimeFreq using run all times) to remove r…
georgemccabe Aug 8, 2023
89a34d8
fix function name in test, ci-run-all-diff
georgemccabe Aug 9, 2023
a3f6c70
set self.app_name if unset for all wrappers to prevent errors when cr…
georgemccabe Aug 9, 2023
5673a61
set self.app_name if unset for all wrappers to prevent errors when cr…
georgemccabe Aug 9, 2023
d82c5e8
change all CompareGridded (GridStat, PointStat, EnsembleStat, MODE, M…
georgemccabe Aug 9, 2023
39e8582
add test for usage wrapper, ci-run-all-diff
georgemccabe Aug 9, 2023
6dbc6aa
fix call to RegridDataPlane from ExtractTiles wrapper
georgemccabe Aug 9, 2023
82f04e4
changed more wrappers to use consistent time looping, ci-run-all-cases
georgemccabe Aug 9, 2023
469bbe5
formatting
georgemccabe Aug 9, 2023
83e0286
more formatting
georgemccabe Aug 9, 2023
9cb8b7e
remove unnessary check for skip times because it is called upstream, …
georgemccabe Aug 9, 2023
da19788
add SKIP_LEAD_SEQ check to RuntimeFreq run_at_time and change TCPairs…
georgemccabe Aug 9, 2023
f19aaf5
run each time input through ti_calculate before passing it to run_at_…
georgemccabe Aug 10, 2023
e0a68d2
move runtime log banner
georgemccabe Aug 11, 2023
21f8f27
rename function to avoid confusion with other find_input_files functions
georgemccabe Aug 11, 2023
ba81987
refactored logic in ti_calculate and added unit tests for common inpu…
georgemccabe Aug 14, 2023
3bc665a
formatting cleanup
georgemccabe Aug 14, 2023
92d5d26
moved logic to compute formatted init or valid so it is still run eve…
georgemccabe Aug 14, 2023
2c60cd4
pass data type to find_data and change template c_dict key if data ty…
georgemccabe Aug 14, 2023
77987c2
refactor MTD wrapper to use RuntimeFreq methods to find files to proc…
georgemccabe Aug 14, 2023
a1ba59b
use f-strings for command, ci-run-all-diff
georgemccabe Aug 14, 2023
c9610e0
added unit test for case that happens in gfdl tracker wrapper
georgemccabe Aug 14, 2023
95a245f
formatting
georgemccabe Aug 14, 2023
55dbcd8
fixed failures when trying to format times that are wildcards, fix ch…
georgemccabe Aug 14, 2023
c60b3eb
set default runtime freq for TCPairs wrapper
georgemccabe Aug 14, 2023
9bac908
fix use case to use init instead of valid, ci-run-all-diff
georgemccabe Aug 14, 2023
460851e
Merge branch 'develop' into feature_2283_include_times
georgemccabe Aug 15, 2023
9b7ee25
preserve old behavior by using init and first lead to compute valid t…
georgemccabe Aug 15, 2023
c133bb4
if [INIT/VALID]_BEG == [INIT/VALID]_END and runtime freq is RUN_ONCE,…
georgemccabe Aug 15, 2023
70fa3cb
fix bug introduced by using RuntimeFreq that causes duplicate regex g…
georgemccabe Aug 15, 2023
9a433e2
fix config to use same init beg/end for RUN_ONCE mode
georgemccabe Aug 15, 2023
4c903c6
use RuntimeFreq time looping, preserve old behavior by setting runtim…
georgemccabe Aug 15, 2023
95eac30
turn on use case groups that were failing to test , ci-run-diff
georgemccabe Aug 15, 2023
334f921
add missing import, ci-run-diff
georgemccabe Aug 15, 2023
9e1eeab
name file_list file with 'single' if MTD_SINGLE_RUN to prevent diffs …
georgemccabe Aug 15, 2023
0d53a0c
remove unused variable
georgemccabe Aug 15, 2023
101f5d0
call self.clear before running in RUN_ONCE mode to clear out command …
georgemccabe Aug 15, 2023
1e4a953
change TCGen wrapper to RuntimeFreq
georgemccabe Aug 15, 2023
df2d18a
set lead_hours/minutes/seconds even if lead is months or years so rel…
georgemccabe Aug 15, 2023
9a1a610
update use case config to use single run time (RUN_ONCE) and set init…
georgemccabe Aug 15, 2023
569c6cf
turn off use cases that now pass and test if other cases will pass, c…
georgemccabe Aug 15, 2023
50fbe5a
turn off all use cases
georgemccabe Aug 15, 2023
4383869
Create class variables for RuntimeFreq wrappers to define default val…
georgemccabe Aug 15, 2023
2bcda33
error and return if start and end times cannot be read, e.g. if end t…
georgemccabe Aug 15, 2023
8f308ff
update config files to use current config options, ci-run-all-diff
georgemccabe Aug 15, 2023
74e52bd
fixed bug when beg/end times are not set and aren't needed, ci-run-a…
georgemccabe Aug 15, 2023
b9eef8f
fix use case reference to init time
georgemccabe Aug 16, 2023
75da7ab
update contrib guide
georgemccabe Aug 16, 2023
407aba4
clean up contrib guide additions
georgemccabe Aug 16, 2023
cef425d
change supported runtime frequency values for wrappers that need enha…
georgemccabe Aug 16, 2023
4ed89d6
fix job args to remove hours from -init_beg and -init_end
georgemccabe Aug 16, 2023
25b0d4c
Update docs/Contributors_Guide/basic_components.rst
georgemccabe Aug 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
201 changes: 160 additions & 41 deletions docs/Contributors_Guide/basic_components.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,121 @@
Basic Components of METplus Python Wrappers
*******************************************

CommandBuilder
==============
.. _bc_class_hierarchy:

Class Hierarchy
===============

CommandBuilder is the parent class of all METplus wrappers.
Every wrapper is a subclass of CommandBuilder or
another subclass of CommandBuilder.
For example, GridStatWrapper, PointStatWrapper, EnsembleStatWrapper,
and MODEWrapper are all subclasses of CompareGriddedWrapper.
CompareGriddedWrapper is a subclass of CommandBuilder.
**CommandBuilder** is the parent class of all METplus wrappers.
Every wrapper is a subclass of CommandBuilder or a subclass of CommandBuilder.
CommandBuilder contains instance variables that are common to every wrapper,
such as config (METplusConfig object), errors (a counter of the number of
errors that have occurred in the wrapper), and
c_dict (a dictionary containing common information).
CommandBuilder also contains use class functions that can be called within
each wrapper, such as create_c_dict, clear, and find_data.

**RuntimeFreqWrapper** is a subclass of **CommandBuilder** that contains all
of the logic to handle time looping.
See :ref:`Runtime_Freq` for more information on time looping.
Unless a wrapper is very basic and does not need to loop over time, then
the wrapper should inherit directly or indirectly from **RuntimeFreqWrapper**.

**LoopTimesWrapper** is a subclass of **RuntimeFreqWrapper**.
This wrapper simply sets the default runtime frequency to **RUN_ONCE_FOR_EACH**
for its subclasses.

**CompareGriddedWrapper** is a subclass of **LoopTimesWrapper** that contains
functions that are common to multiple wrappers that compare forecast (FCST)
and observation (OBS) data. Subclasses of this wrapper include
**GridStatWrapper**, **PointStatWrapper**, **EnsembleStatWrapper**,
**MODEWrapper**, and **MTDWrapper**.

**MTDWrapper** in an exception from the rest of the **CompareGriddeWrapper**
subclasses because it typically runs once for each init or valid time and
reads and processes all forecast leads at once. This wrapper inherits from
**CompareGriddedWrapper** because it still uses many of its functions.


.. _bc_class_vars:

Class Variables
===============

RUNTIME_FREQ_DEFAULT
--------------------

Wrappers that inherit from **RuntimeFreqWrapper** should include a class
variable called **RUNTIME_FREQ_DEFAULT** that lists the default runtime
frequency that should be used if it is not explicitly defined in the METplus
configuration.

Example::

RUNTIME_FREQ_DEFAULT = 'RUN_ONCE_FOR_EACH'

If no clear default value exists, then *None* can be set in place of a string.
This means that a use case will report an error if the frequency is not
defined in the METplus configuration file.
The **UserScriptWrapper** wrapper is an example::

RUNTIME_FREQ_DEFAULT = None


RUNTIME_FREQ_SUPPORTED
----------------------

Wrappers that inherit from **RuntimeFreqWrapper** should include a class
variable called **RUNTIME_FREQ_SUPPORTED** that defines the a list of the
georgemccabe marked this conversation as resolved.
Show resolved Hide resolved
runtime frequency settings that are supported by the wrapper. Example::

RUNTIME_FREQ_SUPPORTED = ['RUN_ONCE_PER_INIT_OR_VALID']

If all runtime frequency values are supported by the wrapper, then the string
*'ALL'* can be set instead of a list of strings::

RUNTIME_FREQ_SUPPORTED = 'ALL'


WRAPPER_ENV_VAR_KEYS
--------------------

This class variable lists all of the environment variables that are set by
the wrapper. These variables are typically referenced in the wrapped MET
config file for the tool and are named with a *METPLUS\_* prefix.
All of the variables that are referenced in the wrapped MET config file must
be listed here so that they will always be set to prevent an error when MET
reads the config file. An empty string will be set if they are not set to
another value by the wrapper.

DEPRECATED_WRAPPER_ENV_VAR_KEYS
--------------------------------

(Optional)
This class variable lists any environment variables that were
previously set by the wrapper and referenced in an old version of the
wrapped MET config file.
This list serves as a developer reference of the variables that were
previously used but are now deprecated. When support for setting these
variables are eventually removed, then the values in this list should also
be removed.

Flags
-----

(Optional)
For wrappers that set a dictionary of flags in the wrapped MET config file,
class variables that contain a list of variable names can be defined.
This makes it easier to add/change these variables.

The list is read by the **self.handle_flags** function.
The name of the variable corresponds to the argument passed to the function.
For example, **EnsembleStatWrapper** includes **OUTPUT_FLAGS** and a call
to **self.handle_flags('OUTPUT')**.

Existing \*_FLAG class variables include **OUTPUT_FLAGS**, **NC_PAIRS_FLAGS**, **NC_ORANK_FLAGS**, and **ENSEMBLE_FLAGS**.


.. _bc_init_function:

Init Function
Expand Down Expand Up @@ -64,16 +163,17 @@ create_c_dict (ExampleWrapper)::
def create_c_dict(self):
c_dict = super().create_c_dict()
# get values from config object and set them to be accessed by wrapper
c_dict['INPUT_TEMPLATE'] = self.config.getraw('filename_templates',
'EXAMPLE_INPUT_TEMPLATE', '')
c_dict['INPUT_TEMPLATE'] = self.config.getraw('config',
'EXAMPLE_INPUT_TEMPLATE')
c_dict['INPUT_DIR'] = self.config.getdir('EXAMPLE_INPUT_DIR', '')

if c_dict['INPUT_TEMPLATE'] == '':
self.logger.info('[filename_templates] EXAMPLE_INPUT_TEMPLATE was not set. '
'You should set this variable to see how the runtime is '
'substituted. For example: {valid?fmt=%Y%m%d%H}.ext')
if not c_dict['INPUT_TEMPLATE']:
self.logger.info('EXAMPLE_INPUT_TEMPLATE was not set. '
'You should set this variable to see how the '
'runtime is substituted. '
'For example: {valid?fmt=%Y%m%d%H}.ext')

if c_dict['INPUT_DIR'] == '':
if not c_dict['INPUT_DIR']:
self.logger.debug('EXAMPLE_INPUT_DIR was not set')

return c_dict
Expand All @@ -95,7 +195,7 @@ create_c_dict (CommandBuilder)::
isOK class variable
===================

isOK is defined in CommandBuilder (ush/command_builder.py).
isOK is defined in CommandBuilder (metplus/wrappers/command_builder.py).

Its function is to note a failed process while not stopping a parent process.
Instead of instantly exiting a larger wrapper script once one subprocess has
Expand All @@ -106,55 +206,74 @@ At the end of the wrapper initialization step, all isOK=false will be
collected and reported. Execution of the wrappers will not occur unless all
wrappers in the process list are initialized correctly.

The **self.log_error** function logs an error and sets self.isOK to False, so
it is not necessary to set *self.isOK = False* if this function is called.

.. code-block:: python

c_dict['CONFIG_FILE'] = self.config.getstr('config', 'MODE_CONFIG_FILE', '')
if not c_dict['CONFIG_FILE']:
self.log_error('MODE_CONFIG_FILE must be set')
if something_else_goes_wrong:
self.isOK = False


See MODEWrapper (ush/mode_wrapper.py) for other examples.
.. _bc_run_at_time_once:

run_at_time_once function
=========================

run_at_time function
====================

run_at_time runs a process for one specific time.
This is defined in CommandBuilder.
**run_at_time_once** runs a process for one specific time. The time depends
on the value of {APP_NAME}_RUNTIME_FREQ. Most wrappers run once per each
init or valid and forecast lead time. This function is often defined in each
wrapper to handle command setup specific to the wrapper. There is a generic
version of the function in **runtime_freq_wrapper.py** that can be used by
other wrappers:

.. code-block:: python

def run_at_time(self, input_dict):
"""! Loop over each forecast lead and build pb2nc command """
# loop of forecast leads and process each
lead_seq = util.get_lead_sequence(self.config, input_dict)
for lead in lead_seq:
input_dict['lead'] = lead
def run_at_time_once(self, time_info):
"""! Process runtime and try to build command to run. Most wrappers
should be able to call this function to perform all of the actions
needed to build the commands using this template. This function can
be overridden if necessary.

lead_string = time_util.ti_calculate(input_dict)['lead_string']
self.logger.info("Processing forecast lead {}".format(lead_string))
@param time_info dictionary containing timing information
@returns True if command was built/run successfully or
False if something went wrong
"""
# get input files
if not self.find_input_files(time_info):
return False

# Run for given init/valid time and forecast lead combination
self.run_at_time_once(input_dict)
# get output path
if not self.find_and_check_output_file(time_info):
return False

See ush/pb2nc_wrapper.py for an example.
# get other configurations for command
self.set_command_line_arguments(time_info)

# set environment variables if using config file
self.set_environment_variables(time_info)

# build command and run
return self.build()

Typically the **find_input_files** and **set_command_line_arguments**
functions need to be implemented in the wrapper to handle the wrapper-specific
functionality.

run_all_times function
======================

run_all_times loops over a series of times calling run_at_time for one
process for each time. Defined in CommandBuilder but overridden in
wrappers that process all of the data from every run time at once.

See SeriesByLeadWrapper (ush/series_by_lead_wrapper.py) for an example of
overriding the function.
If a wrapper is not inheriting from RuntimeFreqWrapper or one of its child
classes, then the **run_all_times** function can be implemented in the wrapper.
This function is called when the wrapper is called.

get_command function
====================

get_command assembles a MET command with arguments that can be run via the
shell or the wrapper.
**get_command** assembles the command that will be run.
It is defined in CommandBuilder but is overridden in most wrappers because
the command line arguments differ for each MET tool.

Expand Down
Loading
Loading