def test_calculation_file_only_one_variable(self): rd = self.test_data.get_rd('cancm4_tas') field = rd.get() field = field.get_field_slice({'time': slice(0, 10)}) expr = 'es=6.1078*exp(17.08085*(tas-273.16)/(234.175+(tas-273.16)))' ef = EvalFunction(expr=expr, field=field, file_only=True) ret = ef.execute() self.assertEqual(ret['es']._value, None)
def test_calculation_one_variable_exp_and_log(self): rd = self.test_data.get_rd('cancm4_tas') field = rd.get() field = field[:,0:10,:,:,:] expr = 'es=6.1078*exp(log(17.08085)*(tas-273.16)/(234.175+(tas-273.16)))' ef = EvalFunction(expr=expr,field=field) ret = ef.execute() var = field.variables['tas'] actual_value = 6.1078*np.exp(np.log(17.08085)*(var.value-273.16)/(234.175+(var.value-273.16))) self.assertNumpyAll(ret['es'].value,actual_value)
def test_calculation_file_only_one_variable(self): rd = self.test_data.get_rd('cancm4_tas') field = rd.get() field = field[:,0:10,:,:,:] expr = 'es=6.1078*exp(17.08085*(tas-273.16)/(234.175+(tas-273.16)))' ef = EvalFunction(expr=expr,field=field,file_only=True) ret = ef.execute() self.assertEqual(ret['es']._value,None) self.assertEqual(ret['es'].dtype,field.variables['tas'].dtype) self.assertEqual(ret['es'].fill_value,field.variables['tas'].fill_value)
def test_calculation_file_only_two_variables(self): rd = self.test_data.get_rd('cancm4_tas') rd2 = self.test_data.get_rd('cancm4_tasmax_2001') field = rd.get() field2 = rd2.get() field.variables.add_variable(field2.variables['tasmax'],assign_new_uid=True) field = field[:,0:10,:,:,:] expr = 'foo=log(1000*(tasmax-tas))/3' ef = EvalFunction(expr=expr,field=field,file_only=True) ret = ef.execute() self.assertEqual(ret['foo']._value,None)
def test_calculation_one_variable_exp_and_log(self): rd = self.test_data.get_rd('cancm4_tas') field = rd.get() # field = field[:, 0:10, :, :, :] field = field.get_field_slice({'time': slice(0, 10)}) expr = 'es=6.1078*exp(log(17.08085)*(tas-273.16)/(234.175+(tas-273.16)))' ef = EvalFunction(expr=expr, field=field) ret = ef.execute() var = field['tas'] actual_value = 6.1078 * np.exp( np.log(17.08085) * (var.get_value() - 273.16) / (234.175 + (var.get_value() - 273.16))) self.assertNumpyAll(ret['es'].get_value(), actual_value)
def test_calculation_file_only_two_variables(self): rd = self.test_data.get_rd('cancm4_tas') rd2 = self.test_data.get_rd('cancm4_tasmax_2001') field = rd.get() field2 = rd2.get() with orphaned(field2['tasmax']): field.add_variable(field2['tasmax'], is_data=True) field = field.get_field_slice({'time': slice(0, 10)}) expr = 'foo=log(1000*(tasmax-tas))/3' ef = EvalFunction(expr=expr, field=field, file_only=True) ret = ef.execute() self.assertEqual(ret['foo']._value, None)
def test_calculation_one_variable_exp_only(self): rd = self.test_data.get_rd('cancm4_tas') field = rd.get() # field = field[:, 0:10, :, :, :] field = field.get_field_slice({'time': slice(0, 10)}) expr = 'es=6.1078*exp(17.08085*(tas-273.16)/(234.175+(tas-273.16)))' ef = EvalFunction(expr=expr, field=field) ret = ef.execute() self.assertEqual(list(ret.keys()), ['es']) self.assertEqual(ret['es'].units, None) self.assertEqual(ret['es'].name, 'es') var = field['tas'] actual_value = 6.1078 * np.exp(17.08085 * (var.get_value() - 273.16) / (234.175 + (var.get_value() - 273.16))) self.assertNumpyAll(ret['es'].get_value(), actual_value)
def test_calculation_one_variable_exp_only(self): rd = self.test_data.get_rd('cancm4_tas') field = rd.get() field = field[:,0:10,:,:,:] expr = 'es=6.1078*exp(17.08085*(tas-273.16)/(234.175+(tas-273.16)))' ef = EvalFunction(expr=expr,field=field) ret = ef.execute() self.assertEqual(ret.keys(),['es']) self.assertEqual(ret['es'].units,None) self.assertEqual(ret['es'].alias,'es') self.assertEqual(ret['es'].name,'es') self.assertEqual(ret['es'].parents.keys(),['tas']) var = field.variables['tas'] actual_value = 6.1078*np.exp(17.08085*(var.value-273.16)/(234.175+(var.value-273.16))) self.assertNumpyAll(ret['es'].value,actual_value)
def test_calculation_two_variables_exp_only(self): rd = self.test_data.get_rd('cancm4_tas') rd2 = self.test_data.get_rd('cancm4_tasmax_2001') field = rd.get() field2 = rd2.get() field.variables.add_variable(field2.variables['tasmax'],assign_new_uid=True) field = field[:,0:10,:,:,:] expr = 'foo=log(1000*(tasmax-tas))/3' ef = EvalFunction(expr=expr,field=field) ret = ef.execute() self.assertEqual(ret.keys(),['foo']) self.assertEqual(set(ret['foo'].parents.keys()),set(['tas','tasmax'])) tas = field.variables['tas'] tasmax = field.variables['tasmax'] actual_value = np.log(1000*(tasmax.value-tas.value))/3 self.assertNumpyAll(ret['foo'].value,actual_value)
def test_calculation_two_variables_exp_only(self): rd = self.test_data.get_rd('cancm4_tas') rd2 = self.test_data.get_rd('cancm4_tasmax_2001') field = rd.get() field2 = rd2.get() with orphaned(field2['tasmax']): field.add_variable(field2['tasmax'], is_data=True) field = field.get_field_slice({'time': slice(0, 10)}) expr = 'foo=log(1000*(tasmax-tas))/3' ef = EvalFunction(expr=expr, field=field) ret = ef.execute() self.assertEqual(list(ret.keys()), ['foo']) tas = field['tas'] tasmax = field['tasmax'] actual_value = np.log(1000 * (tasmax.get_value() - tas.get_value())) / 3 self.assertNumpyAll(ret['foo'].get_value(), actual_value)
def execute(self,coll,file_only=False,tgds=None): ''' :param :class:~`ocgis.SpatialCollection` coll: :param bool file_only: :param dict tgds: {'field_alias': :class:`ocgis.interface.base.dimension.temporal.TemporalGroupDimension`,...} ''' ## switch field type based on the types of calculations present if self._check_calculation_members_(self.funcs,AbstractMultivariateFunction): klass = DerivedMultivariateField elif self._check_calculation_members_(self.funcs,EvalFunction): ## if the input field has more than one variable, assumed this is a ## multivariate calculation klass = DerivedField for field_container in coll.itervalues(): for field in field_container.itervalues(): if len(field.variables.keys()) > 1: klass = DerivedMultivariateField break else: klass = DerivedField ## select which dictionary will hold the temporal group dimensions if tgds == None: tgds_to_use = self._tgds tgds_overloaded = False else: tgds_to_use = tgds tgds_overloaded = True ## group the variables. if grouping is None, calculations are performed ## on each element. array computations are taken advantage of. if self.grouping is not None: ocgis_lh('Setting temporal groups: {0}'.format(self.grouping),'calc.engine') for v in coll.itervalues(): for k2,v2 in v.iteritems(): if tgds_overloaded: assert(k2 in tgds_to_use) else: if k2 not in tgds_to_use: tgds_to_use[k2] = v2.temporal.get_grouping(self.grouping) ## iterate over functions for ugid,dct in coll.iteritems(): for alias_field,field in dct.iteritems(): ## choose a representative data type based on the first variable dtype = field.variables.values()[0].dtype new_temporal = tgds_to_use.get(alias_field) ## if the engine has a grouping, ensure it is equivalent to the ## new temporal dimension. if self.grouping is not None: try: compare = set(new_temporal.grouping) == set(self.grouping) ## types may be unhashable, compare directly except TypeError: compare = new_temporal.grouping == self.grouping if compare == False: msg = ('Engine temporal grouping and field temporal grouping ' 'are not equivalent. Perhaps optimizations are incorrect?') ocgis_lh(logger='calc.engine',exc=ValueError(msg)) out_vc = VariableCollection() for f in self.funcs: try: ocgis_lh('Calculating: {0}'.format(f['func']),logger='calc.engine') ## initialize the function function = f['ref'](alias=f['name'],dtype=dtype,field=field,file_only=file_only,vc=out_vc, parms=f['kwds'],tgd=new_temporal,use_raw_values=self.use_raw_values, calc_sample_size=self.calc_sample_size) except KeyError: ## likely an eval function which does not have the name ## key function = EvalFunction(field=field,file_only=file_only,vc=out_vc, expr=self.funcs[0]['func']) ocgis_lh('calculation initialized',logger='calc.engine',level=logging.DEBUG) ## return the variable collection from the calculations out_vc = function.execute() for dv in out_vc.itervalues(): ## any outgoing variables from a calculation must have a ## data type associated with it try: assert(dv.dtype != None) except AssertionError: assert(isinstance(dv.dtype,np.dtype)) ## if this is a file only operation, then there should ## be no values. if file_only: assert(dv._value == None) ocgis_lh('calculation finished',logger='calc.engine',level=logging.DEBUG) ## try to mark progress try: self._progress.mark() except AttributeError: pass new_temporal = new_temporal or field.temporal new_field = klass(variables=out_vc,temporal=new_temporal,spatial=field.spatial, level=field.level,realization=field.realization,meta=field.meta, uid=field.uid,name=field.name) coll[ugid][alias_field] = new_field return(coll)
def execute(self, coll, file_only=False, tgds=None): """ :param :class:~`ocgis.SpatialCollection` coll: :param bool file_only: :param dict tgds: {'field_alias': :class:`ocgis.interface.base.dimension.temporal.TemporalGroupDimension`,...} """ from ocgis import VariableCollection # Select which dictionary will hold the temporal group dimensions. if tgds is None: tgds_to_use = self._tgds tgds_overloaded = False else: tgds_to_use = tgds tgds_overloaded = True # Group the variables. If grouping is None, calculations are performed on each element. if self.grouping is not None: ocgis_lh('Setting temporal groups: {0}'.format(self.grouping), 'calc.engine') for field in coll.iter_fields(): if tgds_overloaded: assert field.name in tgds_to_use else: if field.name not in tgds_to_use: tgds_to_use[field.name] = field.time.get_grouping(self.grouping) # Iterate over functions. for ugid, container in list(coll.children.items()): for field_name, field in list(container.children.items()): new_temporal = tgds_to_use.get(field_name) if new_temporal is not None: new_temporal = new_temporal.copy() # If the engine has a grouping, ensure it is equivalent to the new temporal dimension. if self.grouping is not None: try: compare = set(new_temporal.grouping) == set(self.grouping) # Types may be unhashable, compare directly. except TypeError: compare = new_temporal.grouping == self.grouping if not compare: msg = 'Engine temporal grouping and field temporal grouping are not equivalent. Perhaps ' \ 'optimizations are incorrect?' ocgis_lh(logger='calc.engine', exc=ValueError(msg)) out_vc = VariableCollection() for f in self.funcs: try: ocgis_lh('Calculating: {0}'.format(f['func']), logger='calc.engine') # Initialize the function. function = f['ref'](alias=f['name'], dtype=None, field=field, file_only=file_only, vc=out_vc, parms=f['kwds'], tgd=new_temporal, calc_sample_size=self.calc_sample_size, meta_attrs=f.get('meta_attrs'), spatial_aggregation=self.spatial_aggregation) # Allow a calculation to create a temporal aggregation after initialization. if new_temporal is None and function.tgd is not None: new_temporal = function.tgd.extract() except KeyError: # Likely an eval function which does not have the name key. function = EvalFunction(field=field, file_only=file_only, vc=out_vc, expr=self.funcs[0]['func'], meta_attrs=self.funcs[0].get('meta_attrs')) ocgis_lh('calculation initialized', logger='calc.engine', level=logging.DEBUG) # Return the variable collection from the calculations. out_vc = function.execute() for dv in out_vc.values(): # Any outgoing variables from a calculation must have an associated data type. try: assert dv.dtype is not None except AssertionError: assert isinstance(dv.dtype, np.dtype) # If this is a file only operation, there should be no computed values. if file_only: assert dv._value is None ocgis_lh('calculation finished', logger='calc.engine', level=logging.DEBUG) # Try to mark progress. Okay if it is not there. try: self._progress.mark() except AttributeError: pass out_field = function.field.copy() function_tag = function.tag # Format the returned field. Doing things like removing original data variables and modifying the # time dimension if necessary. Field functions handle all field modifications on their own, so bypass # in that case. if new_temporal is not None: new_temporal = new_temporal.extract() format_return_field(function_tag, out_field, new_temporal=new_temporal) # Add the calculation variables. for variable in list(out_vc.values()): variable = variable.extract() out_field.add_variable(variable) # Tag the calculation data as data variables. out_field.append_to_tags(function_tag, list(out_vc.keys())) # Update the field if there is a CRS. This will ensure accurate tagging of data variables. if out_field.crs is not None: # print 'here' out_field.crs.format_spatial_object(out_field) coll.children[ugid].children[field_name] = out_field return coll
def execute(self, coll, file_only=False, tgds=None): """ :param :class:~`ocgis.SpatialCollection` coll: :param bool file_only: :param dict tgds: {'field_alias': :class:`ocgis.interface.base.dimension.temporal.TemporalGroupDimension`,...} """ from ocgis import VariableCollection # Select which dictionary will hold the temporal group dimensions. if tgds is None: tgds_to_use = self._tgds tgds_overloaded = False else: tgds_to_use = tgds tgds_overloaded = True # Group the variables. If grouping is None, calculations are performed on each element. if self.grouping is not None: ocgis_lh('Setting temporal groups: {0}'.format(self.grouping), 'calc.engine') for field in coll.iter_fields(): if tgds_overloaded: assert field.name in tgds_to_use else: if field.name not in tgds_to_use: tgds_to_use[field.name] = field.time.get_grouping( self.grouping) # Iterate over functions. for ugid, container in list(coll.children.items()): for field_name, field in list(container.children.items()): new_temporal = tgds_to_use.get(field_name) if new_temporal is not None: new_temporal = new_temporal.copy() # If the engine has a grouping, ensure it is equivalent to the new temporal dimension. if self.grouping is not None: try: compare = set(new_temporal.grouping) == set( self.grouping) # Types may be unhashable, compare directly. except TypeError: compare = new_temporal.grouping == self.grouping if not compare: msg = 'Engine temporal grouping and field temporal grouping are not equivalent. Perhaps ' \ 'optimizations are incorrect?' ocgis_lh(logger='calc.engine', exc=ValueError(msg)) out_vc = VariableCollection() for f in self.funcs: try: ocgis_lh('Calculating: {0}'.format(f['func']), logger='calc.engine') # Initialize the function. function = f['ref']( alias=f['name'], dtype=None, field=field, file_only=file_only, vc=out_vc, parms=f['kwds'], tgd=new_temporal, calc_sample_size=self.calc_sample_size, meta_attrs=f.get('meta_attrs'), spatial_aggregation=self.spatial_aggregation) # Allow a calculation to create a temporal aggregation after initialization. if new_temporal is None and function.tgd is not None: new_temporal = function.tgd.extract() except KeyError: # Likely an eval function which does not have the name key. function = EvalFunction( field=field, file_only=file_only, vc=out_vc, expr=self.funcs[0]['func'], meta_attrs=self.funcs[0].get('meta_attrs')) ocgis_lh('calculation initialized', logger='calc.engine', level=logging.DEBUG) # Return the variable collection from the calculations. out_vc = function.execute() for dv in out_vc.values(): # Any outgoing variables from a calculation must have an associated data type. try: assert dv.dtype is not None except AssertionError: assert isinstance(dv.dtype, np.dtype) # If this is a file only operation, there should be no computed values. if file_only: assert dv._value is None ocgis_lh('calculation finished', logger='calc.engine', level=logging.DEBUG) # Try to mark progress. Okay if it is not there. try: self._progress.mark() except AttributeError: pass out_field = function.field.copy() function_tag = function.tag # Format the returned field. Doing things like removing original data variables and modifying the # time dimension if necessary. Field functions handle all field modifications on their own, so bypass # in that case. if new_temporal is not None: new_temporal = new_temporal.extract() format_return_field(function_tag, out_field, new_temporal=new_temporal) # Add the calculation variables. for variable in list(out_vc.values()): variable = variable.extract() out_field.add_variable(variable) # Tag the calculation data as data variables. out_field.append_to_tags(function_tag, list(out_vc.keys())) # Update the field if there is a CRS. This will ensure accurate tagging of data variables. if out_field.crs is not None: # print 'here' out_field.crs.format_spatial_object(out_field) coll.children[ugid].children[field_name] = out_field return coll