Source code for c3s_magic_wps.processes.wps_multimodel_products

import logging
import os

from pywps import FORMATS, ComplexInput, ComplexOutput, Format, LiteralInput, LiteralOutput, Process
from pywps.app.Common import Metadata
from pywps.response.status import WPS_STATUS
from pywps.inout.literaltypes import AllowedValue
from pywps.validator.allowed_value import ALLOWEDVALUETYPE

from .utils import (default_outputs, model_experiment_ensemble, outputs_from_plot_names,
                    check_constraints, historic_projection_year_ranges)

from .. import runner, util

LOGGER = logging.getLogger("PYWPS")


[docs]class MultimodelProducts(Process): def __init__(self): self.variables = ['tas'] self.frequency = 'mon' inputs = [ *model_experiment_ensemble(model='MPI-ESM-MR', experiment='rcp85', ensemble='r1i1p1', min_occurs=2, required_variables=self.variables, required_frequency=self.frequency, exclude_historical=True), *historic_projection_year_ranges(1961, 1990, 2006, 2100), LiteralInput( 'moninf', 'First month month of the seasonal mean period', abstract="""First month of the seasonal mean period to be computed, if none the monthly anomalies will be computed.""", data_type='string', allowed_values=['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', 'null'], default='6'), LiteralInput( 'monsup', 'Last month month of the seasonal mean period', abstract="""Last month of the seasonal mean period to be computed, if none the monthly anomalies will be computed.""", data_type='string', allowed_values=['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'], default='6'), LiteralInput( 'agreement_threshold', 'Agreement Threshold', abstract="""Threshold in percent for the minimum agreement between models on the sign of the multi-model mean anomaly for the stipling to be plotted.""", data_type='integer', allowed_values=AllowedValue(allowed_type=ALLOWEDVALUETYPE.RANGE, minval=0, maxval=100), default=80), LiteralInput('running_mean', 'Running Mean', abstract='Length of the window for which the running mean is computed.', data_type='integer', allowed_values=AllowedValue(allowed_type=ALLOWEDVALUETYPE.RANGE, minval=1, maxval=365), default=5), LiteralInput('time_series_plot', 'Time series plot', abstract="""Either single or maxmin (plot the individual or the mean with shading between the max and min).""", data_type='string', allowed_values=['single', 'maxmin'], default='single'), ] self.plotlist = [ ('tas', [Format('image/png')]), ('Area', [Format('image/png')]), ] outputs = [ *outputs_from_plot_names(self.plotlist), ComplexOutput('data', 'Data', abstract='Generated output data of ESMValTool processing.', as_reference=True, supported_formats=[FORMATS.NETCDF]), ComplexOutput('archive', 'Archive', abstract='The complete output of the ESMValTool processing as an zip archive.', as_reference=True, supported_formats=[Format('application/zip')]), *default_outputs(), ] super(MultimodelProducts, self).__init__( self._handler, identifier="multimodel_products", title="Generic multi-model products", version=runner.VERSION, abstract="""For the 'generic multi-model diagnostic' the ensemble mean anomaly, and the ensemble variance and agreement are calculated. The results are shown as maps and time series. The estimated calculation time of this process is 1 minute for the default values supplied. For each model choose a projection scenario (e.g. rcp26) and the relevant historical experiment will be added by the WPS process. Also make sure to set the climatology and anomaly start and end years correctly.""", metadata=[ Metadata('ESMValTool', 'http://www.esmvaltool.org/'), Metadata( 'Documentation', 'https://esmvaltool.readthedocs.io/en/v2.0a2/recipes/recipe_multimodel_products.html', role=util.WPS_ROLE_DOC), ], inputs=inputs, outputs=outputs, status_supported=True, store_supported=True) def _handler(self, request, response): response.update_status("starting ...", 0) # build esgf search constraints constraints = dict( models=request.inputs['model'], ensembles=request.inputs['ensemble'], experiments=request.inputs['experiment'], start_historical=request.inputs['start_historical'][0].data, end_historical=request.inputs['end_historical'][0].data, start_projection=request.inputs['start_projection'][0].data, end_projection=request.inputs['end_projection'][0].data, ) check_constraints(constraints) options = dict( moninf=request.inputs['moninf'][0].data, monsup=request.inputs['monsup'][0].data, agreement_threshold=int(request.inputs['agreement_threshold'][0].data), running_mean=int(request.inputs['running_mean'][0].data), time_series_plot=request.inputs['time_series_plot'][0].data, ) # generate recipe response.update_status("generate recipe ...", 10) recipe_file, config_file = runner.generate_recipe( workdir=self.workdir, diag='multimodel_products', constraints=constraints, options=options, start_year=constraints['start_historical'], end_year=constraints['end_projection'], output_format='png', ) # recipe output response.outputs['recipe'].output_format = FORMATS.TEXT response.outputs['recipe'].file = recipe_file # run diag response.update_status("running diagnostic (this could take a while)...", 20) result = runner.run(recipe_file, config_file) response.outputs['success'].data = result['success'] # log output response.outputs['log'].output_format = FORMATS.TEXT response.outputs['log'].file = result['logfile'] # debug log output response.outputs['debug_log'].output_format = FORMATS.TEXT response.outputs['debug_log'].file = result['debug_logfile'] if result['success']: try: self.get_outputs(result, response) except Exception as e: response.update_status("exception occured: " + str(e), 85) LOGGER.exception('Getting output failed: ' + str(e)) else: LOGGER.exception('esmvaltool failed!') response.update_status("exception occured: " + result['exception'], 100) response.update_status("creating archive of diagnostic result ...", 90) response.outputs['archive'].output_format = Format('application/zip') response.outputs['archive'].file = runner.compress_output( os.path.join(self.workdir, 'output'), os.path.join(self.workdir, 'multimodel_products_result.zip')) response.update_status("done.", 100) return response
[docs] def get_outputs(self, result, response): # result plot response.update_status("collecting output ...", 80) for plot, _ in self.plotlist: key = '{}_plot'.format(plot.lower()) response.outputs[key].output_format = Format('application/png') response.outputs[key].file = runner.get_output(result['plot_dir'], path_filter=os.path.join('anomaly_agreement', 'main'), name_filter="{}*".format(plot), output_format="png") response.outputs['data'].output_format = FORMATS.NETCDF response.outputs['data'].file = runner.get_output(result['work_dir'], path_filter=os.path.join('anomaly_agreement', 'main'), name_filter="tas*", output_format="nc")