diff --git a/VERSION b/VERSION index b0e4bca8dc425eb9c69cadb49bf354eda61295fa..270d3fa30bb6ed002b481ca012fd8d6b15a81833 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ -3.0.0b42 +3.0.0b43 diff --git a/doc/source/conf.py b/doc/source/conf.py index 0be2cea52029c2d488e6b7e1771e61bbcf96fb13..2ea3808c623862ebc138beac8dcf979479b622b5 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -64,7 +64,7 @@ copyright = u'2016, BSC-CNS Earth Sciences Department' # The short X.Y version. version = '3.0b' # The full version, including alpha/beta/rc tags. -release = '3.0.0b42' +release = '3.0.0b43' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/doc/source/diagnostic_list.rst b/doc/source/diagnostic_list.rst index e2393d709804f2e375ab7e9d0722a0696bab9643..22393659665c81678ca5f8104a453e768b60fc78 100644 --- a/doc/source/diagnostic_list.rst +++ b/doc/source/diagnostic_list.rst @@ -175,6 +175,10 @@ Options: 7. Max limit = NaN: If there is any value above this threshold, scale will not be applied +8. Frequencies = [*Default_frequency*]: + List of frequencies ('-' separated) to apply the scale on. Default is the frequency defined globally for all the + diagnostics + yearlymean ~~~~~~~~~~ Calculates the daily mean for a given variable. See :class:`~earthdiagnostics.general.yearlymean.YearlyMean` @@ -373,6 +377,10 @@ Options: 4. Invert latitude: If True, inverts the latitude in the output file. +5. Original grid = '': + Source grid to choose. By default this is the original data, but sometimes you will want to use another + (for example, the 'rotated' one produced by the rotation diagnostic) + interpolateCDO ~~~~~~~~~~~~~~ @@ -398,6 +406,9 @@ Options: If True, replaces the values in the ocean by NaN. You must only set it to false if, for some reason, you are interpolating an atmospheric or land variable that is stored in the NEMO grid (yes, this can happen, i.e. with tas). +5. Original grid = '': + Source grid to choose. By default this is the original data, but sometimes you will want to use another + (for example, the 'rotated' one produced by the rotation diagnostic) maxmoc ~~~~~~ diff --git a/earthdiagnostics/EarthDiagnostics.pdf b/earthdiagnostics/EarthDiagnostics.pdf index 40d79dfe0e345bbf5501604881c82035f9ed89d4..db0951f6ddc8b5f840430ec30f14ae830ac8d1f3 100644 Binary files a/earthdiagnostics/EarthDiagnostics.pdf and b/earthdiagnostics/EarthDiagnostics.pdf differ diff --git a/earthdiagnostics/diagnostic.py b/earthdiagnostics/diagnostic.py index 3548451d0e7e039979b8c3db6426c84d5f48b27b..dfc2082a83f814f51582c2881887a6aeb48ba32c 100644 --- a/earthdiagnostics/diagnostic.py +++ b/earthdiagnostics/diagnostic.py @@ -197,6 +197,19 @@ class DiagnosticListIntOption(DiagnosticOption): return values +class DiagnosticListFrequenciesOption(DiagnosticOption): + + def __init__(self, name, default_value=None): + super(DiagnosticListFrequenciesOption, self).__init__(name, default_value) + + def parse(self, option_value): + option_value = self.check_default(option_value) + if isinstance(option_value, tuple) or isinstance(option_value, list): + return option_value + values = [Frequency(i) for i in option_value.split('-')] + return values + + class DiagnosticVariableOption(DiagnosticOption): def parse(self, option_value): option_value = self.check_default(option_value) diff --git a/earthdiagnostics/general/scale.py b/earthdiagnostics/general/scale.py index 83ec9056d75293a9d3c4fe917732f7679c48931a..59fc369d3ab3fcef62e13836629d632bbfc39742 100644 --- a/earthdiagnostics/general/scale.py +++ b/earthdiagnostics/general/scale.py @@ -1,6 +1,5 @@ # coding=utf-8 -from earthdiagnostics.diagnostic import Diagnostic, DiagnosticOption, DiagnosticFloatOption, DiagnosticDomainOption, \ - DiagnosticVariableOption +from earthdiagnostics.diagnostic import * from earthdiagnostics.utils import Utils from earthdiagnostics.modelingrealm import ModelingRealm import numpy as np @@ -35,7 +34,7 @@ class Scale(Diagnostic): "Diagnostic alias for the configuration file" def __init__(self, data_manager, startdate, member, chunk, value, offset, domain, variable, grid, - min_limit, max_limit): + min_limit, max_limit, frequency): Diagnostic.__init__(self, data_manager) self.startdate = startdate self.member = member @@ -47,18 +46,19 @@ class Scale(Diagnostic): self.offset = offset self.min_limit = min_limit self.max_limit = max_limit + self.frequency = frequency self.original_values = None def __str__(self): return 'Scale output Startdate: {0} Member: {1} Chunk: {2} ' \ - 'Scale value: {5} Offset: {6} Variable: {3}:{4}'.format(self.startdate, self.member, self.chunk, - self.domain, self.variable, - self.value, self.offset) + 'Scale value: {5} Offset: {6} Variable: {3}:{4} ' \ + 'Frequency: {7}'.format(self.startdate, self.member, self.chunk, self.domain, self.variable, + self.value, self.offset, self.frequency) def __eq__(self, other): return self.startdate == other.startdate and self.member == other.member and self.chunk == other.chunk and \ - self.domain == other.domain and self.variable == other.variable + self.domain == other.domain and self.variable == other.variable and self.frequency == other.frequency @classmethod def generate_jobs(cls, diags, options): @@ -77,13 +77,15 @@ class Scale(Diagnostic): DiagnosticFloatOption('offset'), DiagnosticOption('grid', ''), DiagnosticFloatOption('min_limit', float('nan')), - DiagnosticFloatOption('max_limit', float('nan'))) + DiagnosticFloatOption('max_limit', float('nan')), + DiagnosticListFrequenciesOption('frequencies', [diags.config.frequency])) options = cls.process_options(options, options_available) job_list = list() - for startdate, member, chunk in diags.config.experiment.get_chunk_list(): - job_list.append(Scale(diags.data_manager, startdate, member, chunk, - options['value'], options['offset'], options['domain'], options['variable'], - options['grid'], options['min_limit'], options['max_limit'])) + for frequency in options['frequencies']: + for startdate, member, chunk in diags.config.experiment.get_chunk_list(): + job_list.append(Scale(diags.data_manager, startdate, member, chunk, + options['value'], options['offset'], options['domain'], options['variable'], + options['grid'], options['min_limit'], options['max_limit'], frequency)) return job_list def compute(self): @@ -91,7 +93,7 @@ class Scale(Diagnostic): Runs the diagnostic """ variable_file = self.data_manager.get_file(self.domain, self.variable, self.startdate, self.member, self.chunk, - grid=self.grid) + grid=self.grid, frequency=self.frequency) handler = Utils.openCdf(variable_file) var_handler = handler.variables[self.variable] @@ -100,7 +102,7 @@ class Scale(Diagnostic): var_handler[:] = self.original_values * self.value + self.offset handler.close() self.send_file(variable_file, self.domain, self.variable, self.startdate, self.member, self.chunk, - grid=self.grid) + grid=self.grid, frequency=self.frequency) def _check_limits(self): if not math.isnan(self.min_limit) and (self.original_values.min() < self.min_limit): diff --git a/earthdiagnostics/ocean/interpolate.py b/earthdiagnostics/ocean/interpolate.py index 123447c4a9d03b4106959f09af614ea73033c501..35bbcb4c450535bee90ea65f3dc8589de75acf78 100644 --- a/earthdiagnostics/ocean/interpolate.py +++ b/earthdiagnostics/ocean/interpolate.py @@ -44,7 +44,7 @@ class Interpolate(Diagnostic): lock = threading.Lock() def __init__(self, data_manager, startdate, member, chunk, domain, variable, target_grid, model_version, - invert_lat): + invert_lat, original_grid): Diagnostic.__init__(self, data_manager) self.startdate = startdate self.member = member @@ -57,18 +57,20 @@ class Interpolate(Diagnostic): self.tempTemplate = '' self.grid = target_grid self.invert_latitude = invert_lat + self.original_grid = original_grid def __eq__(self, other): return self.startdate == other.startdate and self.member == other.member and self.chunk == other.chunk and \ self.model_version == other.model_version and self.domain == other.domain and \ self.variable == other.variable and self.grid == other.grid and \ - self.invert_latitude == other.invert_latitude + self.invert_latitude == other.invert_latitude and self.original_grid == other.original_grid def __str__(self): return 'Interpolate Startdate: {0} Member: {1} Chunk: {2} ' \ 'Variable: {3}:{4} Target grid: {5} Invert lat: {6} ' \ - 'Model: {7}' .format(self.startdate, self.member, self.chunk, self.domain, self.variable, self.grid, - self.invert_latitude, self.model_version) + 'Model: {7} Original grid: {8}'.format(self.startdate, self.member, self.chunk, self.domain, + self.variable, self.grid, self.invert_latitude, + self.model_version, self.original_grid) @classmethod def generate_jobs(cls, diags, options): @@ -84,7 +86,8 @@ class Interpolate(Diagnostic): options_available = (DiagnosticOption('target_grid'), DiagnosticVariableOption('variable'), DiagnosticDomainOption('domain', ModelingRealms.ocean), - DiagnosticBoolOption('invert_lat', False)) + DiagnosticBoolOption('invert_lat', False), + DiagnosticOption('original_grid')) options = cls.process_options(options, options_available) job_list = list() @@ -92,14 +95,15 @@ class Interpolate(Diagnostic): job_list.append( Interpolate(diags.data_manager, startdate, member, chunk, options['domain'], options['variable'], options['target_grid'], - diags.config.experiment.model_version, options['invert_lat'])) + diags.config.experiment.model_version, options['invert_lat'], options['original_grid'])) return job_list def compute(self): """ Runs the diagnostic """ - variable_file = self.data_manager.get_file(self.domain, self.variable, self.startdate, self.member, self.chunk) + variable_file = self.data_manager.get_file(self.domain, self.variable, self.startdate, self.member, self.chunk, + grid=self.original_grid) Utils.rename_variables(variable_file, {'i': 'x', 'j': 'y'}, must_exist=False, rename_dimension=True) cdo = Utils.cdo nco = Utils.nco diff --git a/earthdiagnostics/ocean/interpolatecdo.py b/earthdiagnostics/ocean/interpolatecdo.py index 404e4aa520e852808c22ee850cc597a809ea960c..bff647adaed13df8f48f917a8b4772780864418f 100644 --- a/earthdiagnostics/ocean/interpolatecdo.py +++ b/earthdiagnostics/ocean/interpolatecdo.py @@ -37,7 +37,7 @@ class InterpolateCDO(Diagnostic): "Diagnostic alias for the configuration file" def __init__(self, data_manager, startdate, member, chunk, domain, variable, target_grid, model_version, - mask_oceans): + mask_oceans, original_grid): Diagnostic.__init__(self, data_manager) self.startdate = startdate self.member = member @@ -50,11 +50,12 @@ class InterpolateCDO(Diagnostic): self.tempTemplate = '' self.grid = target_grid self.mask_oceans = mask_oceans + self.original_grid = original_grid def __eq__(self, other): return self.startdate == other.startdate and self.member == other.member and self.chunk == other.chunk and \ self.model_version == other.model_version and self.domain == other.domain and \ - self.variable == other.variable and self.grid == other.grid + self.variable == other.variable and self.grid == other.grid and self.original_grid == other.original_grid def __str__(self): return 'Interpolate with CDO Startdate: {0} Member: {1} Chunk: {2} ' \ @@ -76,14 +77,16 @@ class InterpolateCDO(Diagnostic): options_available = (DiagnosticVariableOption('variable'), DiagnosticOption('target_grid', diags.config.experiment.atmos_grid.lower()), DiagnosticDomainOption('domain', ModelingRealms.ocean), - DiagnosticBoolOption('mask_oceans', True)) + DiagnosticBoolOption('mask_oceans', True), + DiagnosticOption('original_grid')) options = cls.process_options(options, options_available) target_grid = cls._translate_ifs_grids_to_cdo_names(options['target_grid']) job_list = list() for startdate, member, chunk in diags.config.experiment.get_chunk_list(): job_list.append(InterpolateCDO(diags.data_manager, startdate, member, chunk, options['domain'], options['variable'], target_grid, - diags.config.experiment.model_version, options['mask_oceans'])) + diags.config.experiment.model_version, options['mask_oceans'], + options['original_grid'])) return job_list @classmethod @@ -100,7 +103,8 @@ class InterpolateCDO(Diagnostic): """ Runs the diagnostic """ - variable_file = self.data_manager.get_file(self.domain, self.variable, self.startdate, self.member, self.chunk) + variable_file = self.data_manager.get_file(self.domain, self.variable, self.startdate, self.member, self.chunk, + grid=self.original_grid) if self.mask_oceans: handler = Utils.openCdf(variable_file) diff --git a/test/unit/test_maxmoc.py b/test/unit/test_maxmoc.py index 35117a673907aa68ea13978ae39111e75bdba608..f9542b2996ae9c8cf4d5670b1dcbc128827184a1 100644 --- a/test/unit/test_maxmoc.py +++ b/test/unit/test_maxmoc.py @@ -12,11 +12,11 @@ class TestMaxMoc(TestCase): def setUp(self): self.data_manager = Mock() - self.box = Box() - self.box.min_lat = 0 - self.box.max_lat = 0 - self.box.min_depth = 0 - self.box.max_depth = 0 + self.box = Box(True) + self.box.min_lat = 0.0 + self.box.max_lat = 0.0 + self.box.min_depth = 0.0 + self.box.max_depth = 0.0 self.maxmoc = MaxMoc(self.data_manager, '20000101', 1, 2000, Basins.Global, self.box) diff --git a/test/unit/test_verticalmeanmeters.py b/test/unit/test_verticalmeanmeters.py index ecfb3da3f5d9b88f2cd5c249b457d70b26563b43..39a86c3a66c21fc7de7d2d1232402e2ff8c9ca55 100644 --- a/test/unit/test_verticalmeanmeters.py +++ b/test/unit/test_verticalmeanmeters.py @@ -53,5 +53,5 @@ class TestVerticalMeanMeters(TestCase): VerticalMeanMeters.generate_jobs(self.diags, ['diagnostic', '0', '0', '0', '0', '0', '0', '0']) def test_str(self): - self.assertEquals(str(self.mixed), 'Vertical mean meters Startdate: 20000101 Member: 1 Chunk: 1 Variable: var ' - 'Box: 0m-100m') + self.assertEquals(str(self.mixed), 'Vertical mean meters Startdate: 20000101 Member: 1 Chunk: 1 ' + 'Variable: ocean:var Box: 0m-100m')