diff --git a/README.rst b/README.rst index 450ea53..18d0ea1 100644 --- a/README.rst +++ b/README.rst @@ -4,20 +4,9 @@ WRF Runner Tools to configure, run and monitor the Weather Research and Forecasting model (WRF). -The idea behind this project is to create a (web) interface for WRF that could be added to a docker container together with WRF that would allow to configure the model, run it and monitoring the progress from this interface without having to touch the config files. It will also have an API that would allow to send the configuraiton from a script so that bigger scale simmulations consisting of many runs could be run this way. - * Free software: MIT license -(Planed) Features --------- - -Most of these is not done as long as this message is here. - -* Configure the software. Use a simpler configuration schema then the oldschool namelist files. -* Run the software and show the progress. -* Save the logs -* on-the-fly processing of the output files. Allow to run precessing scripts on the files as soon as the files are saved. -* web interface +Note: This project has been restarte. The original attempt is in the branch first_attemp. Credits --------- diff --git a/setup.py b/setup.py index ff2ecf4..856226a 100644 --- a/setup.py +++ b/setup.py @@ -12,14 +12,7 @@ history = history_file.read() requirements = [ - 'Jinja2', - 'jsonschema', - 'click', - 'progressbar2', - 'dateparser', - 'transitions', - 'sanic', - 'aiohttp' + 'click' # TODO: put package requirements here ] diff --git a/src/wrf_runner/real.py b/src/.keep similarity index 100% rename from src/wrf_runner/real.py rename to src/.keep diff --git a/src/wrf_runner/__init__.py b/src/wrf_runner/__init__.py deleted file mode 100644 index 875d946..0000000 --- a/src/wrf_runner/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- - -"""Top-level package for WRF Runner.""" - -__author__ = """Tomas Barton""" -__email__ = 'tomas@tomasbarton.net' -__version__ = '0.1.0' - -import logging - -logging.getLogger(__name__).setLevel('INFO') - -ch = logging.StreamHandler() -ch.setLevel(logging.DEBUG) -formatter = logging.Formatter( - '%(asctime)s - (%(name)s)[%(levelname)s]: %(message)s', '%Y-%m-%d %H:%M:%S') -ch.setFormatter(formatter) - -logging.getLogger(__name__).addHandler(ch) - -from .wrf_exceptions import WrfException diff --git a/src/wrf_runner/__main__.py b/src/wrf_runner/__main__.py deleted file mode 100644 index af55078..0000000 --- a/src/wrf_runner/__main__.py +++ /dev/null @@ -1,5 +0,0 @@ -# -*- coding: utf-8 -*- - -from .cli import cli - -cli() diff --git a/src/wrf_runner/cli.py b/src/wrf_runner/cli.py deleted file mode 100644 index 092c901..0000000 --- a/src/wrf_runner/cli.py +++ /dev/null @@ -1,158 +0,0 @@ -# -*- coding: utf-8 -*- - -import asyncio -import collections -import json -import sys -import traceback - -import click -import progressbar - - -from .namelist import generate_config_file -from . import ungrib -from . import metgrid -from . import geogrid - -from .configuration import WpsConfiguration - -# From: https://gist.github.com/angstwad/bf22d1822c38a92ec0a9 - - -def dict_merge(dct, merge_dct): - """ Recursive dict merge. Inspired by :meth:``dict.update()``, instead of - updating only top-level keys, dict_merge recurses down into dicts nested - to an arbitrary depth, updating keys. The ``merge_dct`` is merged into - ``dct``. - :param dct: dict onto which the merge is executed - :param merge_dct: dct merged into dct - :return: None - """ - for k, v in merge_dct.items(): - if k in dct and isinstance(dct[k], dict) and isinstance(merge_dct[k], collections.Mapping): - dict_merge(dct[k], merge_dct[k]) - else: - dct[k] = merge_dct[k] - - -@click.group() -def cli(): - """WRF Runner.""" - pass - - -@cli.group() -def generate(): - pass - - -@generate.command() -@click.argument('configuration_file') -@click.option('--debug/--no-debug', default=False, - help='Prints additional information in the case of an error.') -def wps_namelist(configuration_file, debug): - """Will generate the WPS namelist""" - - try: - with open(configuration_file, 'r') as f: - config = json.load(f) - - wps_configuration = WpsConfiguration(config) - - click.echo(wps_configuration.get_namelist()) - except Exception as e: - click.echo(click.style(str(e), bg='red')) - - if debug: - click.echo( - ''.join(traceback.format_exception(None, e, e.__traceback__))) - - sys.exit(1) - - -@cli.group() -def run(): - pass - - -@run.command('geogrid') -@click.argument('configuration_file') -@click.option('--log', default=None, help='Logfile for the output from geogrid') -def cli_geogrid(configuration_file, progress, log): - with open(configuration_file, 'r') as f: - config = json.load(f) - - geogrid_program = geogrid.Geogrid(config, - log_file=log) - - loop = asyncio.get_event_loop() - success = loop.run_until_complete(geogrid_program.run()) - loop.close() - - if success: - print('Success.') - sys.exit(0) - else: - print('Failure.') - sys.exit(1) - - -@run.command('ungrib') -@click.argument('configuration_file') -@click.option('--log', default=None, help='Logfile for the output from ungrib') -def cli_ungrib(configuration_file, log): - with open(configuration_file, 'r') as f: - config = json.load(f) - - ungrib_program = ungrib.Ungrib(config, - log_file=log) - - loop = asyncio.get_event_loop() - success = loop.run_until_complete(ungrib_program.run()) - loop.close() - - if success: - print('Success.') - sys.exit(0) - else: - print('Failure.') - sys.exit(1) - - -@run.command('metgrid') -@click.argument('configuration_file') -@click.option('--log', default=None, help='Logfile for the output from metgrid') -def cli_metgrid(configuration_file, log): - with open(configuration_file, 'r') as f: - config = json.load(f) - - metgrid_program = metgrid.Metgrid(config, - log_file=log) - - loop = asyncio.get_event_loop() - success = loop.run_until_complete(metgrid_program.run()) - loop.close() - - if success: - print('Success.') - sys.exit(0) - else: - print('Failure.') - sys.exit(1) - - -@cli.command() -@click.argument('path_pattern', nargs=-1, required=True) -def link_grib(path_pattern): - - print(path_pattern) - - try: - ungrib.link_grib(path_pattern) - except Exception as e: - click.echo(click.style('ERROR', bg='red')) - click.echo(click.style(str(e), bg='red')) - sys.exit(1) - - sys.exit(0) diff --git a/src/wrf_runner/configuration.py b/src/wrf_runner/configuration.py deleted file mode 100644 index 13442d1..0000000 --- a/src/wrf_runner/configuration.py +++ /dev/null @@ -1,136 +0,0 @@ -# -*- coding: utf-8 -*- -"""Represents the configuration of WRF.""" - -import datetime -import json - -import jsonschema -import dateparser - -from .wrf_exceptions import WrfException -from . import namelist_wps -from . import namelist - - -TWO_NUMBERS = {"type": "array", "items": { - "type": "number"}, "minItems": 2, "maxItems": 2} - -WPS_CONFIGURATION_SCHEMA = { - "type": "object", - "properties": { - "domains": { - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "properties": { - "parent_id": {"type": "number"}, - "parent_ratio": {"type": "number"}, - "parent_start": TWO_NUMBERS, - "size": TWO_NUMBERS, - "step_size": TWO_NUMBERS - }, - "required": [ - "parent_id", "parent_ratio", "parent_start", "size"] - }}, - "projection": { - "type": "object", - "properties": { - "type": {"type": "string"}, - "ref_location": TWO_NUMBERS, - "truelat": TWO_NUMBERS, - "stand_lot": {"type": "number"} - }, - "required": ["type", "ref_location", "truelat", "stand_lot"] - }, - "data_path": {"type": "string"}, - - "start_date": {"type": "string"}, - "end_date": {"type": "string"}, - "interval": {"type": "number"}, - "prefix": {"type": "string"} - }, - "required": ["domains", "projection", "data_path", "start_date", - "end_date", "interval", "prefix"] -} - - -def configuration_dict_from_json(path): - with open(path, 'r') as f: - config = json.load(f) - return config - - -class WpsConfiguration: - """Represents the WPS Configuration. - - Can validate the config and convert it into the namelist. - """ - - def __init__(self, config_dict): - """Initialize the object from a configuration dictionary. - - The dictioraty is validated against schema in WPS_CONFIGURATION_SCHEMA and agains WPS - configuration requirements. - """ - self.config_dict = config_dict - - self.validate() - - def validate_geogrid_part(self): - """Validate the geogrid section of the config.""" - # Check if it has stepsize in the first domain but not the others - if 'step_size' not in self.config_dict['domains'][0]: - raise WrfException( - "Step size not specified in the first domain") - - if any(['step_size' in domain for domain in self.config_dict['domains'][1:]]): - raise WrfException( - "Step size can be specified only on the first domain") - - # Check first domain id - if self.config_dict['domains'][0]['parent_id'] != 1: - raise WrfException('First domain parent id has to be 1.') - - # Check other domains ids - for i, domain in enumerate(self.config_dict['domains'][1:]): - if domain['parent_id'] > i + 1: - raise WrfException('Invalid parent_id (too large)') - if domain['parent_id'] < 1: - raise WrfException( - 'Invalid parent_id. Has to be bigger than 1.') - - def validate_ungrib_part(self): - """Validate the ungrib section of the config.""" - # Check if we understand the dates - self.config_dict['start_date'] = dateparser.parse( - self.config_dict['start_date']) - self.config_dict['end_date'] = dateparser.parse( - self.config_dict['end_date']) - - if not isinstance(self.config_dict['start_date'], datetime.datetime): - raise WrfException("Cannot parse the start date") - - if not isinstance(self.config_dict['end_date'], datetime.datetime): - raise WrfException("Cannot parse the end date") - - def validate(self): - """Validate the configuration. - - Raises an exception if the config is invalid or incomplete. - """ - try: - jsonschema.validate(self.config_dict, WPS_CONFIGURATION_SCHEMA) - except jsonschema.ValidationError as exception: - raise WrfException("Configuration error", exception) - - self.validate_geogrid_part() - self.validate_ungrib_part() - - def get_namelist(self): - """Generate the namelist.wps and return it as a string.""" - namelist_dict = namelist_wps.config_to_namelist(self.config_dict) - return namelist.generate_config_file(namelist_dict) - - def __getitem__(self, key): - return self.config_dict[key] diff --git a/src/wrf_runner/geogrid.py b/src/wrf_runner/geogrid.py deleted file mode 100644 index c67da8e..0000000 --- a/src/wrf_runner/geogrid.py +++ /dev/null @@ -1,113 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -Code to represent and manipulate the GEOGRID program. -""" - -import re -from typing import Callable -import os -import logging -import glob - -from .wrf_runner import system_config -from .program import Program -from .wps_state_machine import WpsStateMachine -from .configuration import WpsConfiguration - - -def check_progress_update(line: str): - """ - Scan one line of the output of GEOGRID to check if the program started working on a new domain. - - :param line: one line of GEOGRID stdout - :return: tuple (current_domain, domain_count) if the status changed, None otherwise - """ - result = re.search(r"Processing domain (\d+) of (\d+)", line) - - if result: - return int(result.group(1)), int(result.group(2)) - - return None - - -class Geogrid(): - def __init__(self, - config, - progress_update_cb: Callable[[int, int], None] = None, - log_file=None): - """ - - :param config: WpsConfiguration class - :param progress_update_cb: Callback that will get called everytime a domain processing - is finished. - :param print_message_cb: will be called for printing messages - """ - - if isinstance(config, WpsConfiguration): - self.config = config - else: - self.config = WpsConfiguration(config) - - self.logger = logging.getLogger(__name__ + '.' + Geogrid.__name__) - - def log_progress(current_domain, max_domains): - self.logger.info( - f'Processing domain {current_domain} out of {max_domains}.') - - domains_count = len(self.config['domains']) - self.state_machine = WpsStateMachine( - domains_count, - check_progress_update, - lambda line: 'Successful completion of geogrid.' in line, - lambda line: 'ERROR' in line, - log_progress) - - self.program = Program( - './geogrid.exe', - self.state_machine.process_line, - self.state_machine.process_line, - log_file=log_file) - - def __str__(self): - if self.state_machine.current_domain == 0: - return 'Geogrid (not running)' - - return f'Geogrid(processing domain '\ - f'{self.state_machine.current_domain} '\ - f'out of {self.state_machine.max_domains}' - - async def run(self): - self.logger.info('Starting.') - - cwd = os.getcwd() - os.chdir(system_config['wps_path']) - - old_files = glob.glob('./geo_em.*') - if old_files: - self.logger.info(f'Deleting old output files: {old_files}') - - for old_file in old_files: - os.remove(old_file) - - try: - self.logger.info('Processing the configuration file...') - - # Generate the config file and save it - config_file_content = self.config.get_namelist() - - # Generate the namelist - with open('namelist.wps', 'w') as namelist_file: - namelist_file.write(config_file_content) - - self.state_machine.reset() - self.logger.info('Initializing...') - - return_code = await self.program.run() - - finally: - os.chdir(cwd) - - self.logger.info(f'Files created: {self.program.new_files}') - - return self.state_machine.state == 'done' and return_code == 0 diff --git a/src/wrf_runner/metgrid.py b/src/wrf_runner/metgrid.py deleted file mode 100644 index 6fa663a..0000000 --- a/src/wrf_runner/metgrid.py +++ /dev/null @@ -1,94 +0,0 @@ -# -*- coding: utf-8 -*- - -import os -from typing import Callable -import logging - -from .wrf_runner import system_config -from .wps_state_machine import WpsStateMachine -from . import geogrid -from .program import Program -from .configuration import WpsConfiguration - - -class Metgrid: - def __init__(self, - config, - progress_update_cb: Callable[[int, int], None] = None, - log_file=None): - - if isinstance(config, WpsConfiguration): - self.config = config - else: - self.config = WpsConfiguration(config) - - domain_count = len(config['domains']) - - self.logger = logging.getLogger( - __name__ + '.' + Metgrid.__name__) - - def log_progress(current_domain, max_domains): - self.logger.info( - f'Processing domain {current_domain} out of {max_domains}.') - - self.state_machine = WpsStateMachine( - domain_count, - geogrid.check_progress_update, - lambda line: 'Successful completion of metgrid.' in line, - lambda line: 'ERROR' in line, - log_progress - ) - - self.program = Program( - './metgrid.exe', - self.state_machine.process_line, - self.state_machine.process_line, - log_file=log_file - ) - - self.output_files = [] - - def __str__(self): - if self.state_machine.current_domain == 0: - return 'Metgrid (not running)' - else: - return f'Metgrid (processing domain {self.state_machine.current_domain} '\ - 'out of {self.state_machine.max_domains})' - - async def run(self): - self.logger.info('Metgrid starting.') - - # cd to the WPS folder - cwd = os.getcwd() - os.chdir(system_config['wps_path']) - - try: - - self.logger.info('Processing the configuration file...') - - # Generate the config file and save it - config_file_content = self.config.get_namelist() - - # Generate the namelist - with open('namelist.wps', 'w') as namelist_file: - namelist_file.write(config_file_content) - - self.state_machine.reset() - - # Wait for the program to end - return_code = await self.program.run() - - self.logger.info(f'Files created: {self.program.new_files}') - - finally: - os.chdir(cwd) - - # Evaluate the result - successful_run = self.state_machine.state == 'done' and return_code == 0 - - if successful_run: - self.output_files = [ - f for f in self.program.new_files if f.endswith('.nc')] - self.logger.info(f'Output files: {self.output_files}') - - return successful_run diff --git a/src/wrf_runner/namelist.py b/src/wrf_runner/namelist.py deleted file mode 100644 index 8345065..0000000 --- a/src/wrf_runner/namelist.py +++ /dev/null @@ -1,64 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Code to convert the namelist in a dictionary form into a proper namelist.wps file format. -""" - -from jinja2 import Environment, DictLoader - -loader = DictLoader({ - 'macros': """\ -{% macro make_line(key, value) %}\ -{{ key }} = \ -{% if value is string %}'{{ value}}'\ -{% elif value is iterable %}{{ value|join(', ') }}\ -{% else %}{{ value }}{% endif %}\ -{%- endmacro %} - - -{% macro make_section(name, items) -%} -&{{ name }} -{% for key, value in items.items() %}\ - {{ make_line(key, value) }} -{% endfor -%} -/ -{%- endmacro %} - -{% macro make_file(items) -%} -{% for key, value in items.items() %}\ -{{ make_section(key, value) }} -{{- "\n" if not loop.last }} -{% endfor -%} -{%- endmacro %} - -""", - 'line_template': '{%from \'macros\' import make_line%}{{ make_line(key, value) }}', - 'section_template': '{%from \'macros\' import make_section%}{{ make_section(name, items) }}', - 'file_template': '{%from \'macros\' import make_file%}{{ make_file(items) }}' -}) - -environment = Environment(loader=loader) - -#: creates one line in the configuration file -#: takes a 'key' and 'value' parameters and outputs -#: "key = value" -#: Works for strings, numbers and lists of numbers -line_template = environment.get_template('line_template') - -#: generates an entire section 'name' -#: takes 'name' and 'items' where items is a dict like where the key and value are passed to line_template -section_template = environment.get_template('section_template') - -#: generates the entire config file. Take a dict of sections (section_name, section_dict) in the parameter 'items' -file_template = environment.get_template('file_template') - - -def generate_config_file(config): - """ - Converts a dictionary of configuration options into a config file formatted for WPS or WRF - - :param config: a dictionary of configuration section. Section name is stored as the key, value stores another - dictionary of (config_name, config_values). - :return: string with the content of the config file - """ - - return file_template.render(items=config) diff --git a/src/wrf_runner/namelist_wps.py b/src/wrf_runner/namelist_wps.py deleted file mode 100644 index b0f61bd..0000000 --- a/src/wrf_runner/namelist_wps.py +++ /dev/null @@ -1,259 +0,0 @@ -# -*- coding: utf-8 -*- -"""Namelist.wps file generator. - -Contains a structure that holds the rules how to encode the configuration into the various -parameters of namelist.wps - -The structure is a dict of namelist sections, each section stored as dict or option name -and a tuple ( documentation, format_function ) - -The format function takes the config dict and formats the appropriate parameters for the namelist. -""" - -from itertools import repeat - - -def list_from_list_of_dict(list_of_dict, key, selector=lambda x: x): - """Take a value from each dict in a list of dictionaries.""" - return [selector(d[key]) for d in list_of_dict] - - -WPS_NAMELIST_DEFINITIONS = { - 'share': { - 'start_date': - ( - "START_DATE : A list of MAX_DOM character strings of the form 'YYYY-MM-DD_HH:mm:ss" - " specifying the starting UTC date of the simulation for each nest. The start_date" - " variable is an alternate to specifying start_year, start_month, start_day, and " - "start_hour, and if both methods are used for specifying the starting time, the " - "start_date variable will take precedence. No default value.", - lambda config: list(repeat(config['start_date'].strftime( - "'%Y-%m-%d_%H:%M:%S'"), len(config['domains']))) - ), - 'end_date': - ( - "END_DATE : A list of MAX_DOM character strings of the form 'YYYY-MM-DD_HH:mm:ss'" - " specifying the ending UTC date of the simulation for each nest. The end_date " - "variable is an alternate to specifying end_year, end_month, end_day, and end_hour" - " and if both methods are used for specifying the ending time, the end_date " - "variable will take precedence. No default value.", - lambda config: list(repeat(config['end_date'].strftime( - "'%Y-%m-%d_%H:%M:%S'"), len(config['domains']))) - ), - 'interval_seconds': - ( - "INTERVAL_SECONDS : The integer number of seconds between time-varying " - "meteorological input files. No default value.", - lambda config: config['interval'] - ), - 'wrf_core': - ( - "WRF_CORE : A character string set to either 'ARW' or 'NMM' that tells the WPS " - "which dynamical core the input data are being prepared for. Default value is " - "'ARW'.", - lambda config: 'ARW'), - 'max_dom': - ( - "MAX_DOM : An integer specifying the total number of domains/nests, including the " - "parent domain, in the simulation. Default value is 1.", - lambda config: len(config['domains'])), - 'io_form_geogrid': - ( - "IO_FORM_GEOGRID : The WRF I/O API format that the domain files created by the " - "geogrid program will be written in. Possible options are: 1 for binary; 2 for " - "NetCDF; 3 for GRIB1. When option 1 is given, domain files will have a suffix of " - ".int; when option 2 is given, domain files will have a suffix of .nc;when option " - "3 is given,domain files will have a suffix of .gr1. Default value is 2 (NetCDF).", - lambda config: 2) - }, - 'ungrib': { - 'out_format': - ( - "OUT_FORMAT : A character string set either to 'WPS', 'SI', or 'MM5'. If set to " - "'MM5', ungrib will write output in the format of the MM5 pregrid program; if set " - "to 'SI', ungrib will write output in the format of grib_prep.exe; if set to " - "'WPS', ungrib will write data in the WPS intermediate format. Default value is " - "'WPS'.", - lambda config: 'WPS' - ), - 'prefix': - ( - "PREFIX : A character string that will be used as the prefix for " - "intermediate-format files created by ungrib; here, prefix refers to the string " - "PREFIX in the filename PREFIX:YYYY-MM-DD_HH of an intermediate file. The prefix " - "may contain path information, either relative or absolute, in which case the " - "intermediate files will be written in the directory specified. This option may " - "be useful to avoid renaming intermediate files if ungrib is to be run on multiple" - " sources of GRIB data. Default value is 'FILE'.", - lambda config: config['prefix'] - ) - }, - 'metgrid': { - 'fg_name': ( - 'FG_NAME : A list of character strings specifying the path and prefix ' - 'of ungribbed data files. The path may be relative or absolute, and ' - 'the prefix should contain all characters of the filenames up to, but' - 'not including, the colon preceding the date. When more than one fg_name' - 'is specified, and the same field is found in two or more input sources,' - 'the data in the last encountered source will take priority over all' - 'preceding sources for that field. Default value is an empty list (i.e.,' - 'no meteorological fields).', - lambda config: config['prefix'] - ), - 'io_form_metgrid': ( - ' IO_FORM_METGRID : The WRF I/O API format that the output created by the ' - 'metgrid program will be written in. Possible options are: 1 for binary; 2 ' - 'for NetCDF; 3 for GRIB1. When option 1 is given, output files will have a ' - 'suffix of .int; when option 2 is given, output files will have a suffix ' - 'of .nc; when option 3 is given, output files will have a suffix of .gr1. ' - 'Default value is 2 (NetCDF).', - lambda config: 2 - ) - }, - 'geogrid': { - 'parent_id': - ( - "PARENT_ID : A list of MAX_DOM integers specifying, for each nest, the domain " - "number of the nest’s parent; for the coarsest domain, this variable should be " - "set to 1. Default value is 1.", - lambda config: list_from_list_of_dict(config['domains'], 'parent_id')), - 'parent_grid_ratio': - ( - "PARENT_GRID_RATIO : A list of MAX_DOM integers specifying, for each nest, the " - "nesting ratio relative to the domain’s parent. No default value.", - lambda config: list_from_list_of_dict(config['domains'], 'parent_ratio')), - 'i_parent_start': - ( - "I_PARENT_START : A list of MAX_DOM integers specifying, for each nest, the " - "x-coordinate of the lower-left corner of the nest in the parent unstaggered " - "grid. For the coarsest domain, a value of 1 should be specified. No default " - "value.", - lambda config: list_from_list_of_dict( - config['domains'], 'parent_start', selector=lambda x: x[0])), - 'j_parent_start': - ( - "J_PARENT_START : A list of MAX_DOM integers specifying, for each nest, the " - "y-coordinate of the lower-left corner of the nest in the parent unstaggered " - "grid. For the coarsest domain, a value of 1 should be specified. No default " - "value.", - lambda config: list_from_list_of_dict( - config['domains'], 'parent_start', selector=lambda x: x[1])), - 'e_we': - ( - "E_WE : A list of MAX_DOM integers specifying, for each nest, the nest’s full " - "west-east dimension. For nested domains, e_we must be one greater than an " - "integer multiple of the nest's parent_grid_ratio (i.e., e_we = " - "n*parent_grid_ratio+1 for some positive integer n). No default value.", - lambda config: list_from_list_of_dict( - config['domains'], 'size', selector=lambda x: x[0])), - 'e_sn': - ( - "E_SN : A list of MAX_DOM integers specifying, for each nest, the nest’s full " - "south-north dimension. For nested domains, e_sn must be one greater than an " - "integer multiple of the nest's parent_grid_ratio (i.e., e_sn = " - "n*parent_grid_ratio+1 for some positive integer n). No default value.", - lambda config: list_from_list_of_dict( - config['domains'], 'size', selector=lambda x: x[1])), - 'geog_data_res': - ( - "GEOG_DATA_RES : A list of MAX_DOM character strings specifying, for each nest, " - "a corresponding resolution or list of resolutions separated by + symbols of " - "source data to be used when interpolating static terrestrial data to the nest’s " - "grid. For each nest, this string should contain a resolution matching a string " - "preceding a colon in a rel_path or abs_path specification (see the description " - "of GEOGRID.TBL options) in the GEOGRID.TBL file for each field. If a resolution " - "in the string does not match any such string in a rel_path or abs_path " - "specification for a field in GEOGRID.TBL, a default resolution of data for that " - "field, if one is specified, will be used. If multiple resolutions match, the " - "first resolution to match a string in a rel_path or abs_path specification in " - "the GEOGRID.TBL file will be used. Default value is 'default'.", - lambda config: 'default'), - 'dx': - ( - "DX : A real value specifying the grid distance in the x-direction where the map " - "scale factor is 1. For ARW, the grid distance is in meters for the 'polar', " - "'lambert', and 'mercator' projection, and in degrees longitude for the 'lat-lon' " - "projection; for NMM, the grid distance is in degrees longitude. Grid distances " - "for nests are determined recursively based on values specified for " - "parent_grid_ratio and parent_id. No default value.", - lambda config: config['domains'][0]['step_size'][0]), - 'dy': - ( - "DY : A real value specifying the nominal grid distance in the y-direction where " - "the map scale factor is 1. For ARW, the grid distance is in meters for the " - "'polar', 'lambert', and 'mercator' projection, and in degrees latitude for the " - "'lat-lon' projection; for NMM, the grid distance is in degrees latitude. Grid " - "distances for nests are determined recursively based on values specified for " - "parent_grid_ratio and parent_id. No default value.", - lambda config: config['domains'][0]['step_size'][1]), - 'map_proj': - ( - "MAP_PROJ : A character string specifying the projection of the simulation " - "domain. For ARW, accepted projections are 'lambert', 'polar', 'mercator', and " - "'lat-lon'; for NMM, a projection of 'rotated_ll' must be specified. Default " - "value is 'lambert'.", - lambda config: config['projection']['type']), - 'ref_lat': - ( - "REF_LAT : A real value specifying the latitude part of a (latitude, longitude) " - "location whose (i,j) location in the simulation domain is known. For ARW, " - "ref_lat gives the latitude of the center-point of the coarse domain by default " - "(i.e., when ref_x and ref_y are not specified). For NMM, ref_lat always gives " - "the latitude to which the origin is rotated. No default value.", - lambda config: config['projection']['ref_location'][0]), - 'ref_lon': - ( - "REF_LON : A real value specifying the longitude part of a (latitude, longitude) " - "location whose (i, j) location in the simulation domain is known. For ARW, " - "ref_lon gives the longitude of the center-point of the coarse domain by default " - "(i.e., when ref_x and ref_y are not specified). For NMM, ref_lon always gives " - "the longitude to which the origin is rotated. For both ARW and NMM, west " - "longitudes are negative, and the value of ref_lon should be in the range " - "[-180, 180]. No default value.", - lambda config: config['projection']['ref_location'][1]), - 'truelat1': - ( - "TRUELAT1 : A real value specifying, for ARW, the first true latitude for the " - "Lambert conformal projection, or the only true latitude for the Mercator and " - "polar stereographic projections. For NMM, truelat1 is ignored. No default value.", - lambda config: config['projection']['truelat'][0]), - 'truelat2': - ( - "TRUELAT2 : A real value specifying, for ARW, the second true latitude for " - "the Lambert conformal conic projection. For all other projections, truelat2 is " - "ignored. No default value.", - lambda config: config['projection']['truelat'][1]), - 'stand_lon': - ( - "STAND_LON : A real value specifying, for ARW, the longitude that is parallel " - "with the y-axis in the Lambert conformal and polar stereographic projections. " - "For the regular latitude-longitude projection, this value gives the rotation " - "about the earth's geographic poles. For NMM, stand_lon is ignored. No default " - "value.", - lambda config: config['projection']['stand_lot']), - 'geog_data_path': - ( - "GEOG_DATA_PATH : A character string giving the path, either relative or " - "absolute, to the directory where the geographical data directories may be found. " - "This path is the one to which rel_path specifications in the GEOGRID.TBL file " - "are given in relation to. No default value.", - lambda config: config['data_path']) - } -} - - -def config_to_namelist(config): - """Take the configuration dict and convers it to a dict with the namelist.wps structure.""" - output_dict = {} - - for section_name in WPS_NAMELIST_DEFINITIONS: - inner_dict = {} - - for option_name in WPS_NAMELIST_DEFINITIONS[section_name]: - config_generator = WPS_NAMELIST_DEFINITIONS[section_name][option_name][1] - - inner_dict[option_name] = config_generator(config) - - output_dict[section_name] = inner_dict - - return output_dict diff --git a/src/wrf_runner/program.py b/src/wrf_runner/program.py deleted file mode 100644 index 200c2ac..0000000 --- a/src/wrf_runner/program.py +++ /dev/null @@ -1,129 +0,0 @@ -# -*- coding: utf-8 -*- -import logging -import asyncio -import os -import datetime as dt - -from contextlib import closing -from types import SimpleNamespace - - -async def stream_reader(stream, callback): - """ - Awaits on the stream and runs the callback for each line. - :param callback: callable expecting the line as a string - """ - while True: - line = await stream.readline() - if line: - callback(line.decode('ASCII', 'ignore').strip()) - else: - return - - -class Program: - """ - Base class for runable WPS and WRF programs. - - Responsibilities: - - * Run the program - * Listen for output on stderr and stdout - * Pass the output to configured callbacks - * Log the output - * Return the retval - - """ - - def __init__(self, - executable, - stdout_callback, - stderr_callback, - log_file=None): - """ - :param executable: path to the program executable - :param stdout_callback: a function that will be called for each line of stdout - :param stderr_callback: a function that will be called for each line of stderr - :param log_file: The stdout and stderr will be saved to this file - """ - - self.executable = executable - self.log_file = os.path.abspath(log_file) - - self.stderr_callback = stderr_callback - self.stdout_callback = stdout_callback - self.logger = None - - self.new_files = [] - - def initialize_logger(self): - """ - Prepares the stderr and stdout logger if the log filename is provided - - returns the file handler that can be closed or a dummy object with a close - method that does nothing - """ - - if self.log_file: - self.logger = logging.Logger(self.executable) - - file_handler = logging.FileHandler(self.log_file) - - formatter = logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s') - file_handler.setFormatter(formatter) - - self.logger.addHandler(file_handler) - - return file_handler - - # Just a dymmy object with a close method - return SimpleNamespace(close=lambda: None) - - async def run(self): - """ - Listens on the stdout and stderr and runs the callbacks for each line. - """ - start_time = dt.datetime.now() - - with closing(self.initialize_logger()): - # Run the program - process = await asyncio.create_subprocess_exec( - self.executable, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE) - - # Setup the line callbacks - await stream_reader(process.stdout, self.private_stdout_callback) - await stream_reader(process.stderr, self.private_stderr_callback) - - # Wait for the program to end - retcode = await process.wait() - - self.new_files = [] - - for file_to_check in os.listdir('.'): - st = os.stat(file_to_check) - mtime = dt.datetime.fromtimestamp(st.st_mtime) - if mtime >= start_time: - self.new_files.append(os.path.abspath(file_to_check)) - - return retcode - - def private_stderr_callback(self, line): - """ - Is called by the async reader. Logs the line if the log file was configured and - pass the line to the callback - """ - if self.logger: - self.logger.error(line) - self.stderr_callback(line) - - def private_stdout_callback(self, line): - """ - Is called by the async reader. Logs the line if the log file was configured and - pass the line to the callback - """ - if self.logger: - self.logger.info(line) - self.stdout_callback(line) diff --git a/src/wrf_runner/ungrib.py b/src/wrf_runner/ungrib.py deleted file mode 100644 index d652744..0000000 --- a/src/wrf_runner/ungrib.py +++ /dev/null @@ -1,160 +0,0 @@ -# -*- coding: utf-8 -*- - -import logging -import os -from typing import Callable -import glob -import shutil - -from .wrf_runner import system_config -from .program import Program -from .wps_state_machine import WpsStateMachine -from .configuration import WpsConfiguration - - -def check_progress_update(line: str): - if 'Inventory for date' in line: - return 1, 10 - - return None - - -class Ungrib: - def __init__(self, - config, - progress_update_cb: Callable[[int, int], None] = None, - log_file=None): - """""" - - if isinstance(config, WpsConfiguration): - self.config = config - else: - self.config = WpsConfiguration(config) - - files_count = 10 # TODO - - self.logger = logging.getLogger(__name__ + '.' + Ungrib.__name__) - - def log_progress(current_domain, max_domains): - self.logger.info(f'Processing next time ...') - - self.state_machine = WpsStateMachine( - files_count, - check_progress_update, - lambda line: 'Successful completion of ungrib.' in line, - lambda line: 'ERROR' in line, - log_progress - ) - - self.program = Program( - './ungrib.exe', - self.state_machine.process_line, - self.state_machine.process_line, - log_file=log_file - ) - - def __str__(self): - return 'Ungrib ()' - - async def run(self): - self.logger.info('Ungrib starting.') - - # cd to the WPS folder - cwd = os.getcwd() - os.chdir(system_config['wps_path']) - - try: - - try: - pattern = self.config['metdata_pattern'] - except KeyError: - self.logger.error('`key_error` is missing in the config file.') - return False - - try: - vtable_path = self.config['ungrib_vtable'] - self.logger.info( - f'Copying the Vtable from `{os.path.abspath(vtable_path)}`') - shutil.copyfile(vtable_path, './Vtable') - except KeyError: - pass - - old_output_files = glob.glob(self.config['prefix'] + '*') - - if old_output_files: - self.logger.info( - f'Removing old output files: {old_output_files}') - for old_output_file in old_output_files: - os.remove(old_output_file) - - self.logger.info( - f'Linking in the metdata using pattern `{pattern}`.') - link_grib(pattern) - - self.logger.info('Processing the configuration file...') - - # Generate the config file and save it - config_file_content = self.config.get_namelist() - - # Generate the namelist - with open('namelist.wps', 'w') as namelist_file: - namelist_file.write(config_file_content) - - self.state_machine.reset() - - # Wait for the program to end - return_code = await self.program.run() - - self.logger.info(f'Files created: {self.program.new_files}') - - finally: - os.chdir(cwd) - - # Evaluate the result - return self.state_machine.state == 'done' and return_code == 0 - - -def grib_alphabetical_extensions(): - """Generate file extensions AAA, AAB, AAC, ...""" - - current = 'AAA' - yield current - - while current != 'ZZZ': - numbers = [ord(c) for c in current] - - numbers[2] += 1 - - if numbers[2] > ord('Z'): - numbers[2] = ord('A') - numbers[1] += 1 - if numbers[1] > ord('Z'): - numbers[1] = ord('A') - numbers[0] += 1 - - current = ''.join(map(chr, numbers)) - - yield current - - -def link_grib(path_pattern): - """Link the data files into the working directory. - - Python implementation of the script linkgrib.classmethod - """ - - # Delete all links - current_links = glob.glob('GRIBFILE.???') - - for link in current_links: - os.remove(link) - - # Get the new files - if isinstance(path_pattern, str): - new_files = glob.glob(path_pattern) - else: - new_files = path_pattern - - # Link the new paths - for extension, new_file in zip(grib_alphabetical_extensions(), new_files): - os.symlink(new_file, 'GRIBFILE.' + extension) diff --git a/src/wrf_runner/wps_state_machine.py b/src/wrf_runner/wps_state_machine.py deleted file mode 100644 index b09b309..0000000 --- a/src/wrf_runner/wps_state_machine.py +++ /dev/null @@ -1,99 +0,0 @@ -from transitions import Machine - - -class WpsStateMachine(Machine): - """ - This state machine monitors the output ... TODO - """ - - initial = 'initialization' - - states = ['initialization', 'domain_processing', 'done', 'error'] - - transitions = [ - { - 'trigger': 'process_line', - 'source': '*', - 'dest': 'error', - 'conditions': 'is_error_line' - }, - { - 'trigger': 'process_line', - 'source': 'initialization', - 'dest': 'domain_processing', - 'conditions': 'is_domain_processing_line', - 'after': 'update_current_domain' - }, - { - 'trigger': 'process_line', - 'source': 'initialization', - 'dest': 'initialization' - }, - { - 'trigger': 'process_line', - 'source': 'domain_processing', - 'dest': 'domain_processing', - 'conditions': 'is_domain_processing_line', - 'after': 'update_current_domain' - }, - { - 'trigger': 'process_line', - 'source': 'domain_processing', - 'dest': 'done', - 'conditions': 'is_finished_line', - 'after': 'finish_current_domain' - }, - { - 'trigger': 'process_line', - 'source': 'domain_processing', - 'dest': 'domain_processing' - }, - { - 'trigger': 'process_line', - 'source': 'done', - 'dest': 'done' - } - ] - - def __init__(self, - max_domains, - check_progress, - check_finish, - check_error, - progress_cb=None): - - Machine.__init__(self, states=WpsStateMachine.states, initial=WpsStateMachine.initial, - transitions=WpsStateMachine.transitions) - - self.check_progress = check_progress - self.check_finish = check_finish - self.check_error = check_error - - self.progress_cb = progress_cb - self.max_domains = max_domains - self.reset() - - def is_domain_processing_line(self, line): - return self.check_progress(line) is not None - - def is_finished_line(self, line): - return self.check_finish(line) - - def is_error_line(self, line): - return self.check_error(line) - - def update_current_domain(self, line): - self.current_domain, self.max_domains = self.check_progress(line) - self.call_progress_callback() - - def finish_current_domain(self, line): - self.current_domain = self.max_domains - self.call_progress_callback() - - def reset(self): - self.current_domain = 0 - self.to_initialization() - - def call_progress_callback(self): - if self.progress_cb: - self.progress_cb(self.current_domain, self.max_domains) diff --git a/src/wrf_runner/wrf_exceptions.py b/src/wrf_runner/wrf_exceptions.py deleted file mode 100644 index f8e6192..0000000 --- a/src/wrf_runner/wrf_exceptions.py +++ /dev/null @@ -1,5 +0,0 @@ -# -*- coding: utf-8 -*- - - -class WrfException(Exception): - pass diff --git a/src/wrf_runner/wrf_runner.py b/src/wrf_runner/wrf_runner.py deleted file mode 100644 index bf5d56e..0000000 --- a/src/wrf_runner/wrf_runner.py +++ /dev/null @@ -1,94 +0,0 @@ -# -*- coding: utf-8 -*- - -"""Main module.""" - -from os import getenv -import asyncio -import logging - -import jsonschema -from sanic import Sanic -from sanic import response - -from .wrf_exceptions import WrfException -from .configuration import WpsConfiguration - -configuration_schema = { - "type": "object", - "properties": { - "geogrib": {"type": "object"}, - "ungrib": {"type": "object"}, - "metgrib": {"type": "object"}, - "real": {"type": "object"}, - "wrf": {"type": "object"} - }, - "required": ["geogrib", "ungrib", "metgrib", "real", "wrf"] -} - -system_config = { - 'wps_path': getenv('WPS_PATH', None) -} - - -def check_configuration(config): - WpsConfiguration(config) - - -class WrfRunner: - def __init__(self, event_loop=None): - self.steps = [] - - if event_loop is None: - self.event_loop = asyncio.get_event_loop() - else: - self.event_loop = event_loop - - self.app = Sanic(__name__) - self.app.add_route(self.html_handler, '/') - - self.current_step = None - - self.logger = logging.getLogger(__name__) - self.logger.setLevel('INFO') - - def add_step(self, step): - """Add a step to the pipeline. - - step: an object with a run() coroutine - """ - self.steps.append(step) - self.logger.info(f'Step registered: `{step}`') - - return self - - async def run_steps(self): - for step in self.steps: - self.logger.info(f'Starting step `{step}`.') - self.current_step = step - return_code = await step.run() - if not return_code: - self.logger.error(f'Failed step `{step}`') - return False - self.logger.info('Finished all steps.') - return True - - async def html_handler(self, request): - header = '

WRF Runner

' - footer = '' - if self.current_step is None: - return response.html(header + '

Nothing is running

' + footer) - - return response.html( - header + - f'

Step `{self.current_step}` is running.

' + - footer) - - def run(self): - - server = self.app.create_server(host="0.0.0.0", port=8000) - self.event_loop.create_task(server) - - self.logger.info('Starting the event loop.') - retcode = self.event_loop.run_until_complete(self.run_steps()) - - return retcode diff --git a/src/wrf_runner/wrf.py b/tests/.keep similarity index 100% rename from src/wrf_runner/wrf.py rename to tests/.keep diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index 85d5ebc..0000000 --- a/tests/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# -*- coding: utf-8 -*- - -"""Unit test package for wrf_runner.""" diff --git a/tests/conftest.py b/tests/conftest.py deleted file mode 100644 index 74283c6..0000000 --- a/tests/conftest.py +++ /dev/null @@ -1,12 +0,0 @@ -import json -import os - -import pytest - -from .test_cli import resources_directory - - -@pytest.fixture -def valid_config_1(): - return json.load(open(os.path.join( - resources_directory, 'valid_config.json'))) diff --git a/tests/resources/geogrid.success.stdout.txt b/tests/resources/geogrid.success.stdout.txt deleted file mode 100644 index afaf2f3..0000000 --- a/tests/resources/geogrid.success.stdout.txt +++ /dev/null @@ -1,115 +0,0 @@ -Parsed 25 entries in GEOGRID.TBL -Processing domain 1 of 3 - Processing XLAT and XLONG - Processing MAPFAC - Processing F and E - Processing ROTANG - Processing LANDUSEF - Calculating landmask from LANDUSEF ( WATER = 17 21 ) - Processing HGT_M - Processing SOILTEMP - Processing SOILCTOP - Processing SCT_DOM - Processing SOILCBOT - Processing SCB_DOM - Processing ALBEDO12M - Processing GREENFRAC - Processing LAI12M - Processing SNOALB - Processing SLOPECAT - Processing SLOPECAT - Processing CON - Processing VAR - Processing OA1 - Processing OA2 - Processing OA3 - Processing OA4 - Processing OL1 - Processing OL2 - Processing OL3 - Processing OL4 - Processing VAR_SSO - Processing LAKE_DEPTH - Processing URB_PARAM - - Optional fields not processed by geogrid: - IMPERV (priority=1, resolution='default', path='/WPS_GEOG/nlcd2011_imp_ll_9s/') - CANFRA (priority=1, resolution='default', path='/WPS_GEOG/nlcd2011_can_ll_9s/') - -Processing domain 2 of 3 - Processing XLAT and XLONG - Processing MAPFAC - Processing F and E - Processing ROTANG - Processing LANDUSEF - Calculating landmask from LANDUSEF ( WATER = 17 21 ) - Processing HGT_M - Processing SOILTEMP - Processing SOILCTOP - Processing SCT_DOM - Processing SOILCBOT - Processing SCB_DOM - Processing ALBEDO12M - Processing GREENFRAC - Processing LAI12M - Processing SNOALB - Processing SLOPECAT - Processing SLOPECAT - Processing CON - Processing VAR - Processing OA1 - Processing OA2 - Processing OA3 - Processing OA4 - Processing OL1 - Processing OL2 - Processing OL3 - Processing OL4 - Processing VAR_SSO - Processing LAKE_DEPTH - Processing URB_PARAM - - Optional fields not processed by geogrid: - IMPERV (priority=1, resolution='default', path='/WPS_GEOG/nlcd2011_imp_ll_9s/') - CANFRA (priority=1, resolution='default', path='/WPS_GEOG/nlcd2011_can_ll_9s/') - -Processing domain 3 of 3 - Processing XLAT and XLONG - Processing MAPFAC - Processing F and E - Processing ROTANG - Processing LANDUSEF - Calculating landmask from LANDUSEF ( WATER = 17 21 ) - Processing HGT_M - Processing SOILTEMP - Processing SOILCTOP - Processing SCT_DOM - Processing SOILCBOT - Processing SCB_DOM - Processing ALBEDO12M - Processing GREENFRAC - Processing LAI12M - Processing SNOALB - Processing SLOPECAT - Processing SLOPECAT - Processing CON - Processing VAR - Processing OA1 - Processing OA2 - Processing OA3 - Processing OA4 - Processing OL1 - Processing OL2 - Processing OL3 - Processing OL4 - Processing VAR_SSO - Processing LAKE_DEPTH - Processing URB_PARAM - - Optional fields not processed by geogrid: - IMPERV (priority=1, resolution='default', path='/WPS_GEOG/nlcd2011_imp_ll_9s/') - CANFRA (priority=1, resolution='default', path='/WPS_GEOG/nlcd2011_can_ll_9s/') - -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -! Successful completion of geogrid. ! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! diff --git a/tests/resources/geogrid_valid_conf.json b/tests/resources/geogrid_valid_conf.json deleted file mode 100644 index b0dd099..0000000 --- a/tests/resources/geogrid_valid_conf.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "geogrid": { - "domains": [ - { - "parent_id": 1, - "parent_ratio": 1, - "parent_start": [1, 1], - "size": [100, 100], - "step_size": [9000, 9000] - }, - { - "parent_id": 1, - "parent_ratio": 3, - "parent_start": [33, 33], - "size": [100, 100] - }, - { - "parent_id": 2, - "parent_ratio": 3, - "parent_start": [33, 33], - "size": [100, 100] - } - ], - "projection": { - "type": "lambert", - "ref_location": [49.571239, -113.818533], - "truelat": [47.571239, 51.571239], - "stand_lot": -113.818533 - }, - "data_path": "/WPS_GEOG" - } -} diff --git a/tests/resources/metgrid.success.stdout.txt b/tests/resources/metgrid.success.stdout.txt deleted file mode 100644 index b66d048..0000000 --- a/tests/resources/metgrid.success.stdout.txt +++ /dev/null @@ -1,24 +0,0 @@ -Processing domain 1 of 3 - Processing 2017-08-26_12 - MET - Processing 2017-08-26_18 - MET - Processing 2017-08-27_00 - MET -Processing domain 2 of 3 - Processing 2017-08-26_12 - MET - Processing 2017-08-26_18 - MET - Processing 2017-08-27_00 - MET -Processing domain 3 of 3 - Processing 2017-08-26_12 - MET - Processing 2017-08-26_18 - MET - Processing 2017-08-27_00 - MET -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -! Successful completion of metgrid. ! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! diff --git a/tests/resources/ungrib.success.stdout.txt b/tests/resources/ungrib.success.stdout.txt deleted file mode 100644 index e2573b5..0000000 --- a/tests/resources/ungrib.success.stdout.txt +++ /dev/null @@ -1,1305 +0,0 @@ - *** Starting program ungrib.exe *** -Start_date = 2017-08-26_12:00:00 , End_date = 2017-08-27_00:00:00 -output format is WPS -Path to intermediate files is ./ -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 00:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 01:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 02:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 03:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 06:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 06:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 07:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 08:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 09:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 12:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 12:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 13:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 14:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 15:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 18:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 18:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 19:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 20:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-26 21:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-27 00:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable -Subsoil level 3 in the GRIB2 file, was not found in the Vtable - -############################################################################### - -Inventory for date = 2017-08-27 00:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- - -############################################################################### - -Inventory for date = 2017-08-26 12:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- - -############################################################################### - -Inventory for date = 2017-08-26 18:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- - -############################################################################### - -Inventory for date = 2017-08-27 00:00:00 - -PRES TT UU VV RH HGT PSFC PMSL SM000010 SM010040 SM040100 SM100200 ST000010 ST010040 ST040100 ST100200 SEAICE LANDSEA SOILHGT SKINTEMP SNOW SNOWH CANWAT -------------------------------------------------------------------------------- -2013.0 O O O O O O X O O O O O O O O O O O O O O O -2001.0 X X X X O X O X X X X X X X X X X X X X X X -1000.0 X X X X X - 975.0 X X X X X - 950.0 X X X X X - 925.0 X X X X X - 900.0 X X X X X - 875.0 X X X X X - 850.0 X X X X X - 825.0 X X X X X - 800.0 X X X Subroutine DATINT: Interpolating 3-d files to fill in any missing data... -Looking for data at time 2017-08-26_12 -Found file: MET:2017-08-26_12 -Looking for data at time 2017-08-26_18 -Found file: MET:2017-08-26_18 -Looking for data at time 2017-08-27_00 -Found file: MET:2017-08-27_00 -End Subroutine DATINT. -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -! Successful completion of ungrib. ! -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - Name of source model =>NCEP MESO NAM Model GRID 218 - Name of source model =>NCEP MESO NAM Model GRID 218 - Name of source model =>NCEP MESO NAM Model GRID 218 - X X - 775.0 X X X X X - 750.0 X X X X X - 725.0 X X X X X - 700.0 X X X X X - 675.0 X X X X X - 650.0 X X X X X - 625.0 X X X X X - 600.0 X X X X X - 575.0 X X X X X - 550.0 X X X X X - 525.0 X X X X X - 500.0 X X X X X - 475.0 X X X X X - 450.0 X X X X X - 425.0 X X X X X - 400.0 X X X X X - 375.0 X X X X X - 350.0 X X X X X - 325.0 X X X X X - 300.0 X X X X X - 275.0 X X X X X - 250.0 X X X X X - 225.0 X X X X X - 200.0 X X X X X - 175.0 X X X X X - 150.0 X X X X X - 125.0 X X X X X - 100.0 X X X X X - 75.0 X X X X X - 50.0 X X X X X -------------------------------------------------------------------------------- - -********** -Deleting temporary files created by ungrib... -********** - -Deleting file: ./PFILE:2017-08-26_00 -Deleting file: ./PFILE:2017-08-26_01 -Deleting file: ./PFILE:2017-08-26_02 -Deleting file: ./PFILE:2017-08-26_03 -Deleting file: ./PFILE:2017-08-26_06 -Deleting file: ./PFILE:2017-08-26_07 -Deleting file: ./PFILE:2017-08-26_08 -Deleting file: ./PFILE:2017-08-26_09 -Deleting file: ./PFILE:2017-08-26_12 -Deleting file: ./PFILE:2017-08-26_13 -Deleting file: ./PFILE:2017-08-26_14 -Deleting file: ./PFILE:2017-08-26_15 -Deleting file: ./PFILE:2017-08-26_18 -Deleting file: ./PFILE:2017-08-26_19 -Deleting file: ./PFILE:2017-08-26_20 -Deleting file: ./PFILE:2017-08-26_21 -Deleting file: ./PFILE:2017-08-27_00 - -********** -Done deleting temporary files. -********** - diff --git a/tests/resources/ungrib_valid_config.json b/tests/resources/ungrib_valid_config.json deleted file mode 100644 index fc5cb49..0000000 --- a/tests/resources/ungrib_valid_config.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "ungrib": { - "start_date": "2017-09-10", - "end_date": "2017-09-11", - "interval": 3600, - "prefix": "FILE" - } -} diff --git a/tests/resources/valid_config.json b/tests/resources/valid_config.json deleted file mode 100644 index 086864f..0000000 --- a/tests/resources/valid_config.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "domains": [ - { - "parent_id": 1, - "parent_ratio": 1, - "parent_start": [ - 1, - 1 - ], - "size": [ - 100, - 100 - ], - "step_size": [ - 9000, - 9000 - ] - }, - { - "parent_id": 1, - "parent_ratio": 3, - "parent_start": [ - 33, - 33 - ], - "size": [ - 100, - 100 - ] - }, - { - "parent_id": 2, - "parent_ratio": 3, - "parent_start": [ - 33, - 33 - ], - "size": [ - 100, - 100 - ] - } - ], - "projection": { - "type": "lambert", - "ref_location": [ - 49.571239, - -113.818533 - ], - "truelat": [ - 47.571239, - 51.571239 - ], - "stand_lot": -113.818533 - }, - "data_path": "/WPS_GEOG", - "start_date": "2017-09-10", - "end_date": "2017-09-11", - "interval": 3600, - "prefix": "FILE" -} \ No newline at end of file diff --git a/tests/test_cli.py b/tests/test_cli.py deleted file mode 100644 index d3f4a65..0000000 --- a/tests/test_cli.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- coding: utf-8 -*- - -import os -from shutil import copy - -import wrf_runner.cli as cli -from click.testing import CliRunner - -resources_directory = os.path.join(os.path.dirname(__file__), 'resources') - - -def test_generate_namelist_wps_nonexisting_file(): - runner = CliRunner() - result = runner.invoke(cli.wps_namelist, ['nonexisting']) - assert (result.exit_code != 0) - - -def test_generate_namelist_wps_missing_parameter(): - runner = CliRunner() - result = runner.invoke(cli.wps_namelist, []) - assert (result.exit_code != 0) - - -def test_generate_namelist_wps(): - # Get the config file - config_fn = os.path.join(resources_directory, 'valid_config.json') - - runner = CliRunner() - - with runner.isolated_filesystem(): - copy(config_fn, '.') - result = runner.invoke(cli.wps_namelist, ['valid_config.json']) - assert (result.exit_code == 0) diff --git a/tests/test_configuration.py b/tests/test_configuration.py deleted file mode 100644 index 2e87441..0000000 --- a/tests/test_configuration.py +++ /dev/null @@ -1,57 +0,0 @@ -# -*- coding: utf-8 -*- - -import json -import os - -import pytest - -from wrf_runner import WrfException -from wrf_runner.configuration import WpsConfiguration -from wrf_runner.namelist_wps import list_from_list_of_dict, WPS_NAMELIST_DEFINITIONS - -from .test_cli import resources_directory - - -@pytest.mark.parametrize("config", [{}, 5, 'nonsense']) -def test_basic_wrong_configuration(config): - with pytest.raises(WrfException): - WpsConfiguration(config) - - -def test_valid_configuration(valid_config_1): - WpsConfiguration(valid_config_1) - - -def test_namelist_documentation(): - """ - Checks if all of the definitions in the structure are copied correctly. - - Only check if the first word of the documentation is equal to the key. - :return: - """ - - for _, section_config in WPS_NAMELIST_DEFINITIONS.items(): - for option, config_tuple in section_config.items(): - first_word = config_tuple[0].split()[0] - - assert (option.lower() == first_word.lower()) - - -def test_list_from_list_of_dict(): - test_list = [{ - "parent_id": 1, - "parent_ratio": 1, - "parent_start": [1], # Only one - "size": [74, 61] - }, { - "parent_id": 1, - "parent_ratio": 3, - "parent_start": [1], # Only one - "size": [100, 200] - }] - - assert (list_from_list_of_dict(test_list, 'parent_ratio') == [1, 3]) - assert (list_from_list_of_dict(test_list, 'size', - selector=lambda x: x[0]) == [74, 100]) - assert (list_from_list_of_dict(test_list, 'size', - selector=lambda x: x[1]) == [61, 200]) diff --git a/tests/test_geogrid.py b/tests/test_geogrid.py deleted file mode 100644 index 892255d..0000000 --- a/tests/test_geogrid.py +++ /dev/null @@ -1,102 +0,0 @@ -# -*- coding: utf-8 -*- - -import pytest -import asyncio -import os -from wrf_runner import geogrid, WrfException - -from wrf_runner.namelist import generate_config_file -from .test_cli import resources_directory - - -def test_config_to_namelist_basic(valid_config_1): - # Should not fail - geogrid.Geogrid(valid_config_1) - - -success_stdout_from_file = open(os.path.join( - resources_directory, 'geogrid.success.stdout.txt'), 'r').readlines() - - -class TestRunMethods: - - @pytest.fixture(autouse=True) - def mock_system_config(self, mocker): - self.test_working_directory = "/tmp" - mock_system_config = { - 'wps_path': self.test_working_directory - } - - mocker.patch('wrf_runner.geogrid.system_config', mock_system_config) - - @pytest.mark.asyncio - async def test_run_changes_cwd(self, mocker, valid_config_1): - - mocked_chdir = mocker.patch('os.chdir') - - geo = geogrid.Geogrid(config=valid_config_1) - - try: - result = await geo.run() - except FileNotFoundError: # We probably don't have the executable - pass - - mocked_chdir.assert_called_with(self.test_working_directory) - - # the param is a list of tuples (Expected result, list of stdout lines) - @pytest.fixture(params=[ - (False, ['', 'some ERROR happened', '']), - (False, ['Messages', 'something happened', - 'Processing domain 1 of 3', 'there is not final message']), - (True, ['Messages', 'something happened', 'Processing domain 1 of 3', - 'Something else', '* Successful completion of geogrid. *']), - (True, success_stdout_from_file)]) - def process_simulator(self, request, mocker): - async def fake_create_subprocess(*args, **kwargs): - return FakeProcess(request.param[1], [], 0) - - mocker.patch( - 'wrf_runner.program.asyncio.create_subprocess_exec', fake_create_subprocess) - - return request.param[0] - - @pytest.mark.asyncio - async def test_run(self, mocker, process_simulator, valid_config_1): - - # The process_simulator fixture creates and mocks the create_subprocess_exec call - # It prepares the mock with a fake output and provides the expected_result in the - # process_simulator parameter - - # process_simulator is the expected result - geo = geogrid.Geogrid(config=valid_config_1) - result = await geo.run() - assert(result == process_simulator) - - -class FakeStreamReader: - def __init__(self, lines): - self.lines = lines - self.counter = 0 - - @asyncio.coroutine - async def readline(self): - if self.counter < len(self.lines): - line = self.lines[self.counter] - - if(len(line) == 0 or line[-1] != '\n'): - line += '\n' - - self.counter += 1 - return line.encode('ASCII') - else: - return ''.encode('ASCII') - - -class FakeProcess: - def __init__(self, stdout_lines, stderr_lines, return_code): - self.stdout = FakeStreamReader(stdout_lines) - self.stderr = FakeStreamReader(stderr_lines) - self.return_code = return_code - - async def wait(self): - return self.return_code diff --git a/tests/test_metgrid.py b/tests/test_metgrid.py deleted file mode 100644 index 39fdc18..0000000 --- a/tests/test_metgrid.py +++ /dev/null @@ -1,47 +0,0 @@ - -import asyncio -import os - -import pytest - -from unittest.mock import MagicMock - -from wrf_runner.metgrid import Metgrid - -from .test_geogrid import FakeProcess -from .test_cli import resources_directory - - -class TestRunMethods: - - @pytest.fixture(autouse=True) - def mock_system_config(self, mocker): - self.test_working_directory = "/tmp" - - mock_system_config = { - 'wps_path': self.test_working_directory - } - - mocker.patch('wrf_runner.metgrid.system_config', mock_system_config) - - @pytest.fixture - def valid_process_simulator(self, mocker): - - with open(os.path.join(resources_directory, 'metgrid.success.stdout.txt'), 'r') as stdout: - lines = stdout.readlines() - - async def fake_create_subprocess(*args, **kwargs): - return FakeProcess(lines, [], 0) - - mocker.patch( - 'wrf_runner.program.asyncio.create_subprocess_exec', fake_create_subprocess) - - @pytest.mark.asyncio - async def test_success_run(self, valid_process_simulator, valid_config_1): - - ungrib = Metgrid( - config=valid_config_1) - result = await ungrib.run() - - # Success - assert(result) diff --git a/tests/test_namelist.py b/tests/test_namelist.py deleted file mode 100644 index 1829eeb..0000000 --- a/tests/test_namelist.py +++ /dev/null @@ -1,79 +0,0 @@ -from collections import OrderedDict - -import pytest - -from wrf_runner.namelist import line_template, section_template, file_template, generate_config_file - -line_template_examples = [ - ('parent_id', 2, 'parent_id = 2'), - ('parent_id', [1, 1, 2], 'parent_id = 1, 1, 2'), - ('map_proj', 'lambert', 'map_proj = \'lambert\''), -] - - -@pytest.mark.parametrize("key, value, expected", line_template_examples) -def test_line_template(key, value, expected): - assert (line_template.render(key=key, value=value) == expected) - - -# items has to be ordered for the test -section_template_examples = [ - ('geogrid', OrderedDict([ - ('parent_id', [1, 1, 2]), - ('e_sn', [100, 100, 100]), - ('map_proj', 'lambert'), - ('truelat2', 51.57)]), '''\ -&geogrid - parent_id = 1, 1, 2 - e_sn = 100, 100, 100 - map_proj = 'lambert' - truelat2 = 51.57 -/''') -] - - -@pytest.mark.parametrize("name, items, expected", section_template_examples) -def test_section_template(name, items, expected): - result = section_template.render(name=name, items=items) - assert (result == expected) - - -file_template_examples = [ - (OrderedDict([ - ('share', OrderedDict([ - ('parent_id', [1, 1, 2]), - ('e_sn', [100, 100, 100]), - ('map_proj', 'lambert'), - ('truelat2', 51.57)])), - ('geogrid', OrderedDict([ - ('parent_id', [1, 1, 2]), - ('e_sn', [100, 100, 100]), - ('map_proj', 'lambert'), - ('truelat2', 51.57)]))]), '''\ -&share - parent_id = 1, 1, 2 - e_sn = 100, 100, 100 - map_proj = 'lambert' - truelat2 = 51.57 -/ - -&geogrid - parent_id = 1, 1, 2 - e_sn = 100, 100, 100 - map_proj = 'lambert' - truelat2 = 51.57 -/ -''') -] - - -@pytest.mark.parametrize("items, expected", file_template_examples) -def test_file_template(items, expected): - result = file_template.render(items=items) - assert (result == expected) - - -@pytest.mark.parametrize("config, expected", file_template_examples) -def test_generate_config_file(config, expected): - result = generate_config_file(config) - assert (result == expected) diff --git a/tests/test_ungrib.py b/tests/test_ungrib.py deleted file mode 100644 index 7e5b395..0000000 --- a/tests/test_ungrib.py +++ /dev/null @@ -1,64 +0,0 @@ -import pytest -import os -from wrf_runner import WrfException -from wrf_runner import namelist -from wrf_runner.ungrib import Ungrib, grib_alphabetical_extensions -from unittest.mock import MagicMock - -from .test_geogrid import FakeProcess -from .test_cli import resources_directory - - -class TestRunMethods: - - @pytest.fixture(autouse=True) - def mock_system_config(self, mocker): - self.test_working_directory = "/tmp" - - mock_system_config = { - 'wps_path': self.test_working_directory - } - - mocker.patch('wrf_runner.ungrib.system_config', mock_system_config) - - @pytest.fixture - def valid_process_simulator(self, mocker): - - with open(os.path.join(resources_directory, 'ungrib.success.stdout.txt'), 'r') as stdout: - lines = stdout.readlines() - - async def fake_create_subprocess(*args, **kwargs): - return FakeProcess(lines, [], 0) - - mocker.patch( - 'wrf_runner.program.asyncio.create_subprocess_exec', fake_create_subprocess) - - @pytest.mark.asyncio - async def test_success_run(self, valid_process_simulator, valid_config_1): - - ungrib = Ungrib( - config=valid_config_1) - result = await ungrib.run() - - # Success - assert(result) - - -def test_grib_alphabetical_extensions(): - - generator = grib_alphabetical_extensions() - - assert(next(generator) == 'AAA') - assert(next(generator) == 'AAB') - assert(next(generator) == 'AAC') - - -def test_grib_alphabetical_extensions2(): - - generator = grib_alphabetical_extensions() - - extensions = list(generator) - - assert(len(extensions) == (ord('Z') - ord('A') + 1)**3) - assert(extensions[0] == 'AAA') - assert(extensions[-1] == 'ZZZ') diff --git a/tests/test_wrf_runner.py b/tests/test_wrf_runner.py deleted file mode 100644 index 4d56d5b..0000000 --- a/tests/test_wrf_runner.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -"""Tests for `wrf_runner` package.""" -import asyncio -from datetime import datetime - -import aiohttp -import async_timeout - -import pytest -from wrf_runner import WrfException - -from wrf_runner import wrf_runner - - -class TestConfiguration: - - @pytest.mark.parametrize("config", [{}, 5, 'nonsense']) - def test_basic_wrong(self, config): - with pytest.raises(WrfException): - wrf_runner.check_configuration(config) - - -def test_web_starts(): - - class Tester: - def __init__(self): - self.response_text = None - self.response_status = None - - async def fetch(self): - await asyncio.sleep(1) - async with aiohttp.ClientSession() as session: - with async_timeout.timeout(1): - async with session.get("http://127.0.0.1:8000/") as response: - self.response_text = await response.text() - self.response_status = response.status - - class Waiting: - - def __init__(self, waiting_time=10): - self.waiting_time = waiting_time - self.start_time = None - - async def run(self): - self.start_time = datetime.now() - await asyncio.sleep(self.waiting_time) - - def __str__(self): - if self.start_time: - now = datetime.now() - - remaining = self.waiting_time - (now - self.start_time).seconds - return f"Dummy waiting step. Remaining {remaining} seconds." - else: - return f"Dummy waiting step. Not active." - - tester = Tester() - - loop = asyncio.get_event_loop() - loop.create_task(tester.fetch()) - runner = wrf_runner.WrfRunner(event_loop=loop) - runner.add_step(Waiting(2)) - - runner.run() - - assert (tester.response_status == 200) - assert (tester.response_text is not None) diff --git a/tox.ini b/tox.ini index 0f3c6e4..ddce4f1 100644 --- a/tox.ini +++ b/tox.ini @@ -1,13 +1,10 @@ [tox] -envlist = py26, py27, py33, py34, py35, flake8 +envlist = py35, py36, flake8 [travis] python = + 3.6: py36 3.5: py35 - 3.4: py34 - 3.3: py33 - 2.7: py27 - 2.6: py26 [testenv:flake8] basepython=python