From 923861ccb45c4345aeb827878ab4d20db6cedc50 Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Mon, 6 Aug 2018 16:25:44 -0600 Subject: [PATCH 01/23] New convention for Python interpreter setting --- .pydevproject | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pydevproject b/.pydevproject index 9698d126..212594a6 100644 --- a/.pydevproject +++ b/.pydevproject @@ -1,7 +1,7 @@ python 2.7 -Anaconda Python 2.7 +Miniconda3 [conform] /${PROJECT_DIR_NAME}/source From 026932164e68666e6b3bb1670e8730582552bee3 Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Mon, 20 Aug 2018 11:09:58 -0600 Subject: [PATCH 02/23] Computing correct final units for POP function --- source/pyconform/modules/commonfunctions.py | 393 +++++++++++--------- 1 file changed, 207 insertions(+), 186 deletions(-) diff --git a/source/pyconform/modules/commonfunctions.py b/source/pyconform/modules/commonfunctions.py index 51693e85..d55217d5 100644 --- a/source/pyconform/modules/commonfunctions.py +++ b/source/pyconform/modules/commonfunctions.py @@ -6,9 +6,11 @@ from numpy import diff, empty, mean import numpy as np -#=================================================================================================== +#========================================================================= # ZonalMeanFunction -#=================================================================================================== +#========================================================================= + + class ZonalMeanFunction(Function): key = 'zonalmean' @@ -22,23 +24,24 @@ def __getitem__(self, index): data = self.arguments[0][index] m = mean(data, axis=3) return m - #return mean(data, axis=3) + # return mean(data, axis=3) -#======================================================================================================================= +#========================================================================= # BoundsFunction -#======================================================================================================================= +#========================================================================= class BoundsFunction(Function): key = 'bounds' def __init__(self, data, bdim='bnds', location=1, endpoints=1, idata=None): - super(BoundsFunction, self).__init__(data, bdim=bdim, location=location, endpoints=endpoints, idata=idata) + super(BoundsFunction, self).__init__(data, bdim=bdim, + location=location, endpoints=endpoints, idata=idata) data_info = data if is_constant(data) else data[None] if not isinstance(data_info, PhysArray): raise TypeError('bounds: data must be a PhysArray') if not isinstance(bdim, basestring): raise TypeError('bounds: bounds dimension name must be a string') - if location not in [0,1,2]: + if location not in [0, 1, 2]: raise ValueError('bounds: location must be 0, 1, or 2') if len(data_info.dimensions) != 1: raise DimensionsError('bounds: data can only be 1D') @@ -68,20 +71,20 @@ def __getitem__(self, index): if self._compute_idata: dx = diff(data.data) if location == 0: - new_data[:-1,1] = data.data[:-1] + dx + new_data[:-1, 1] = data.data[:-1] + dx if self._mod_end: - new_data[-1,1] = data.data[-1] + dx[-1] + new_data[-1, 1] = data.data[-1] + dx[-1] elif location == 1: hdx = 0.5 * dx - new_data[1:,0] = data.data[1:] - hdx - new_data[:-1,1] = data.data[:-1] + hdx + new_data[1:, 0] = data.data[1:] - hdx + new_data[:-1, 1] = data.data[:-1] + hdx if self._mod_end: - new_data[0,0] = data.data[0] - hdx[0] - new_data[-1,1] = data.data[-1] + hdx[-1] + new_data[0, 0] = data.data[0] - hdx[0] + new_data[-1, 1] = data.data[-1] + hdx[-1] elif location == 2: - new_data[1:,0] = data.data[1:] - dx + new_data[1:, 0] = data.data[1:] - dx if self._mod_end: - new_data[0,0] = data.data[0] - dx[0] + new_data[0, 0] = data.data[0] - dx[0] return new_data else: @@ -98,30 +101,32 @@ def __getitem__(self, index): ifc_data[1:-1] = idata.data[:] if location == 0: ifc_data[0] = data.data[0] - ifc_data[-1] = 2*data.data[-1] - data.data[-2] + ifc_data[-1] = 2 * data.data[-1] - data.data[-2] elif location == 1: - ifc_data[0] = 2*data.data[0] - idata.data[0] - ifc_data[-1] = 2*data.data[-1] - idata.data[-1] + ifc_data[0] = 2 * data.data[0] - idata.data[0] + ifc_data[-1] = 2 * data.data[-1] - idata.data[-1] else: - ifc_data[0] = 2*data.data[0] - data.data[1] + ifc_data[0] = 2 * data.data[0] - data.data[1] ifc_data[-1] = data.data[-1] else: raise ValueError('bounds: interface-data length is {} but should be {} or ' - '{}'.format(len(idata), ifc_len, ifc_len-2)) + '{}'.format(len(idata), ifc_len, ifc_len - 2)) - new_data[:,0] = ifc_data[:-1] - new_data[:,1] = ifc_data[1:] + new_data[:, 0] = ifc_data[:-1] + new_data[:, 1] = ifc_data[1:] return new_data -#=================================================================================================== +#========================================================================= # AgeofAirFunction -#=================================================================================================== +#========================================================================= + + class AgeofAirFunction(Function): key = 'ageofair' - def __init__(self, spc_zm,date,time,lat,lev): - super(AgeofAirFunction, self).__init__(spc_zm,date,time,lat,lev) + def __init__(self, spc_zm, date, time, lat, lev): + super(AgeofAirFunction, self).__init__(spc_zm, date, time, lat, lev) def __getitem__(self, index): p_spc_zm = self.arguments[0][index] @@ -131,45 +136,46 @@ def __getitem__(self, index): p_lev = self.arguments[4][index] if index is None: - return PhysArray(np.zeros((0,0,0)), dimensions=[p_time.dimensions[0],p_lev.dimensions[0],p_lat.dimensions[0]]) - + return PhysArray(np.zeros((0, 0, 0)), dimensions=[p_time.dimensions[0], p_lev.dimensions[0], p_lat.dimensions[0]]) + spc_zm = p_spc_zm.data date = p_date.data time = p_time.data lat = p_lat.data lev = p_lev.data - - a = np.zeros((len(time),len(lev),len(lat))) - # Unpack month and year. Adjust to compensate for the output convention in h0 files - year = date/10000 - month = (date/100 % 100) - day = date - 10000*year - 100*month + a = np.zeros((len(time), len(lev), len(lat))) + + # Unpack month and year. Adjust to compensate for the output + # convention in h0 files + year = date / 10000 + month = (date / 100 % 100) + day = date - 10000 * year - 100 * month month = month - 1 for m in range(len(month)): if month[m] == 12: - year[m] = year[m]-1 + year[m] = year[m] - 1 month[m] = 0 - timeyr = year + (month-0.5)/12. + timeyr = year + (month - 0.5) / 12. - spc_ref = spc_zm[:,0,0] + spc_ref = spc_zm[:, 0, 0] for iy in range(len(lat)): for iz in range(len(lev)): - spc_local = spc_zm[:,iz,iy] - time0 = np.interp(spc_local,spc_ref,timeyr) - a[:,iz,iy] = timeyr - time0 + spc_local = spc_zm[:, iz, iy] + time0 = np.interp(spc_local, spc_ref, timeyr) + a[:, iz, iy] = timeyr - time0 + new_name = 'ageofair({}{}{}{}{})'.format( + p_spc_zm.name, p_date.name, p_time.name, p_lat.name, p_lev.name) - new_name = 'ageofair({}{}{}{}{})'.format(p_spc_zm.name,p_date.name,p_time.name,p_lat.name,p_lev.name) + return PhysArray(a, name=new_name, units="yr") - return PhysArray(a, name = new_name, units="yr") - -#=================================================================================================== +#========================================================================= # yeartomonth_dataFunction -#=================================================================================================== +#========================================================================= class YeartoMonth_dataFunction(Function): key = 'yeartomonth_data' @@ -183,25 +189,28 @@ def __getitem__(self, index): p_lon = self.arguments[3][index] if index is None: - return PhysArray(np.zeros((0,0,0)), dimensions=[p_time.dimensions[0],p_lat.dimensions[0],p_lon.dimensions[0]]) + return PhysArray(np.zeros((0, 0, 0)), dimensions=[p_time.dimensions[0], p_lat.dimensions[0], p_lon.dimensions[0]]) data = p_data.data time = p_time.data lat = p_lat.data lon = p_lon.data - a = np.zeros((len(time)*12,len(lat),len(lon))) + a = np.zeros((len(time) * 12, len(lat), len(lon))) for i in range(len(time)): for j in range(12): - a[((i*12)+j),:,:] = data[i,:,:] + a[((i * 12) + j), :, :] = data[i, :, :] - new_name = 'yeartomonth_data({}{}{}{})'.format(p_data.name, p_time.name, p_lat.name, p_lon.name) + new_name = 'yeartomonth_data({}{}{}{})'.format( + p_data.name, p_time.name, p_lat.name, p_lon.name) - return PhysArray(a, name = new_name, units=p_data.units) + return PhysArray(a, name=new_name, units=p_data.units) -#=================================================================================================== +#========================================================================= # yeartomonth_timeFunction -#=================================================================================================== +#========================================================================= + + class YeartoMonth_timeFunction(Function): key = 'yeartomonth_time' @@ -215,23 +224,25 @@ def __getitem__(self, index): return PhysArray(np.zeros((0)), dimensions=[p_time.dimensions[0]], units=p_time.units, calendar='noleap') time = p_time.data - monLens = [31.0,28.0,31.0,30.0,31.0,30.0,31.0,31.0,30.0,31.0,30.0,31.0] + monLens = [31.0, 28.0, 31.0, 30.0, 31.0, + 30.0, 31.0, 31.0, 30.0, 31.0, 30.0, 31.0] - a = np.zeros((len(time)*12)) + a = np.zeros((len(time) * 12)) for i in range(len(time)): prev = 0 for j in range(12): - a[((i*12)+j)] = float((time[i]-365)+prev+float(monLens[j]/2.0)) + a[((i * 12) + j)] = float((time[i] - 365) + + prev + float(monLens[j] / 2.0)) prev += monLens[j] new_name = 'yeartomonth_time({})'.format(p_time.name) - return PhysArray(a, name = new_name, dimensions=[p_time.dimensions[0]], units=p_time.units, calendar='noleap') + return PhysArray(a, name=new_name, dimensions=[p_time.dimensions[0]], units=p_time.units, calendar='noleap') -#=================================================================================================== +#========================================================================= # POP_bottom_layerFunction -#=================================================================================================== +#========================================================================= class POP_bottom_layerFunction(Function): key = 'POP_bottom_layer' @@ -243,26 +254,25 @@ def __getitem__(self, index): p_data = self.arguments[1][index] if index is None: - return PhysArray(np.zeros((0,0,0)), dimensions=[p_data.dimensions[0],p_data.dimensions[2],p_data.dimensions[3]]) + return PhysArray(np.zeros((0, 0, 0)), dimensions=[p_data.dimensions[0], p_data.dimensions[2], p_data.dimensions[3]]) data = p_data.data KMT = p_KMT.data - a = np.zeros((p_data.shape[0],p_data.shape[2],p_data.shape[3])) + a = np.zeros((p_data.shape[0], p_data.shape[2], p_data.shape[3])) for j in range(KMT.shape[0]): for i in range(KMT.shape[1]): - a[:,j,i] = data[:,KMT[j,i]-1,j,i] - - new_name = 'POP_bottom_layer({}{})'.format( p_KMT.name, p_data.name) + a[:, j, i] = data[:, KMT[j, i] - 1, j, i] - return PhysArray(a, name = new_name, units=p_data.units) + new_name = 'POP_bottom_layer({}{})'.format(p_KMT.name, p_data.name) + return PhysArray(a, name=new_name, units=p_data.units) -#=================================================================================================== +#========================================================================= # sftofFunction -#=================================================================================================== +#========================================================================= class sftofFunction(Function): key = 'sftof' @@ -273,31 +283,31 @@ def __getitem__(self, index): p_KMT = self.arguments[0][index] if index is None: - return PhysArray(np.zeros((0,0)), dimensions=[p_KMT.dimensions[0],p_KMT.dimensions[1]]) + return PhysArray(np.zeros((0, 0)), dimensions=[p_KMT.dimensions[0], p_KMT.dimensions[1]]) KMT = p_KMT.data - a = np.zeros((KMT.shape[0],KMT.shape[1])) + a = np.zeros((KMT.shape[0], KMT.shape[1])) for j in range(KMT.shape[0]): for i in range(KMT.shape[1]): - if KMT[j,i] > 0: - a[j,i] = 1 + if KMT[j, i] > 0: + a[j, i] = 1 - new_name = 'sftof({})'.format( p_KMT.name) + new_name = 'sftof({})'.format(p_KMT.name) - return PhysArray(a, name = new_name, dimensions=[p_KMT.dimensions[0],p_KMT.dimensions[1]], units=p_KMT.units) + return PhysArray(a, name=new_name, dimensions=[p_KMT.dimensions[0], p_KMT.dimensions[1]], units=p_KMT.units) - -#=================================================================================================== +#========================================================================= # POP_bottom_layer_multFunction -#=================================================================================================== +#========================================================================= class POP_bottom_layer_multaddFunction(Function): key = 'POP_bottom_layer_multadd' def __init__(self, KMT, data1, data2): - super(POP_bottom_layer_multaddFunction, self).__init__(KMT, data1, data2) + super(POP_bottom_layer_multaddFunction, + self).__init__(KMT, data1, data2) def __getitem__(self, index): p_KMT = self.arguments[0][index] @@ -308,29 +318,29 @@ def __getitem__(self, index): data2 = p_data2.data KMT = p_KMT.data - a1 = np.zeros((p_data2.shape[0],p_data2.shape[2],p_data2.shape[3])) + a1 = np.zeros((p_data2.shape[0], p_data2.shape[2], p_data2.shape[3])) a2 = np.zeros((p_data2.shape[0])) for t in range(p_data2.shape[0]): for j in range(KMT.shape[0]): for i in range(KMT.shape[1]): - k = KMT[j,i]-1 - if data2[t,k,j,i] < 1e+16: - a1[t,j,i] = data1[k]*data2[t,k,j,i] - #print a1[t,j,i],data1[k],data2[t,k,j,i] + k = KMT[j, i] - 1 + if data2[t, k, j, i] < 1e+16: + a1[t, j, i] = data1[k] * data2[t, k, j, i] + # print a1[t,j,i],data1[k],data2[t,k,j,i] for t in range(p_data2.shape[0]): - a2[t] = np.ma.sum(a1[t,:,:]) - #print a2[t] - - new_name = 'POP_bottom_layer_multadd({}{}{})'.format(KMT.name, data1.name, data2.name) - return PhysArray(a2, name=new_name, dimensions=[p_data2.dimensions[0]], units=p_data2.units) + a2[t] = np.ma.sum(a1[t, :, :]) + # print a2[t] + new_name = 'POP_bottom_layer_multadd({}{}{})'.format( + KMT.name, data1.name, data2.name) + new_units = p_data1.units * p_data2.units + return PhysArray(a2, name=new_name, dimensions=[p_data2.dimensions[0]], units=new_units) - -#=================================================================================================== +#========================================================================= # masked_invalidFunction -#=================================================================================================== +#========================================================================= class masked_invalidFunction(Function): key = 'masked_invalid' @@ -341,7 +351,7 @@ def __getitem__(self, index): p_data = self.arguments[0][index] if index is None: - return PhysArray(np.zeros((0,0,0)), dimensions=[p_data.dimensions[0],p_data.dimensions[1],p_data.dimensions[2]]) + return PhysArray(np.zeros((0, 0, 0)), dimensions=[p_data.dimensions[0], p_data.dimensions[1], p_data.dimensions[2]]) data = p_data.data @@ -349,12 +359,12 @@ def __getitem__(self, index): new_name = 'masked_invalid({})'.format(p_data.name) - return PhysArray(a, name = new_name, units=p_data.units) + return PhysArray(a, name=new_name, units=p_data.units) -#=================================================================================================== +#========================================================================= # hemisphereFunction -#=================================================================================================== +#========================================================================= class hemisphereFunction(Function): key = 'hemisphere' @@ -373,30 +383,30 @@ def __getitem__(self, index): # dim0? if dim in p_data.dimensions[0]: if ">" in dr: - return p_data[(data.shape[0]/2):data.shape[0],:,:] + return p_data[(data.shape[0] / 2):data.shape[0], :, :] elif "<" in dr: - return p_data[0:(data.shape[0]/2),:,:] + return p_data[0:(data.shape[0] / 2), :, :] # dim1? if dim in p_data.dimensions[1]: if ">" in dr: - return p_data[:,(data.shape[1]/2):data.shape[1],:] + return p_data[:, (data.shape[1] / 2):data.shape[1], :] elif "<" in dr: - return p_data[:,0:(data.shape[1]/2),:] + return p_data[:, 0:(data.shape[1] / 2), :] # dim2? if dim in p_data.dimensions[2]: if ">" in dr: - return p_data[:,:,(data.shape[2]/2):data.shape[2]] + return p_data[:, :, (data.shape[2] / 2):data.shape[2]] elif "<" in dr: - return p_data[:,:,0:(data.shape[2]/2)] + return p_data[:, :, 0:(data.shape[2] / 2)] -#=================================================================================================== +#========================================================================= # cice_whereFunction -#=================================================================================================== +#========================================================================= class cice_whereFunction(Function): key = 'cice_where' - # np.where(x < 5, x, -1) + # np.where(x < 5, x, -1) def __init__(self, a1, condition, a2, var, value): super(cice_whereFunction, self).__init__(a1, condition, a2, var, value) @@ -409,33 +419,34 @@ def __getitem__(self, index): value = self.arguments[4] if index is None: - return PhysArray(a1.data, dimensions=[a1.dimensions[0],a1.dimensions[1],a1.dimensions[2]]) - + return PhysArray(a1.data, dimensions=[a1.dimensions[0], a1.dimensions[1], a1.dimensions[2]]) + a = np.ma.zeros(a1.shape) for t in range(a1.data.shape[0]): if '>=' in condition: - a[t,:,:] = np.ma.where(a1[t,:,:] >= a2, var, value) + a[t, :, :] = np.ma.where(a1[t, :, :] >= a2, var, value) elif '<=' in condition: - a[t,:,:] = np.ma.where(a1[t,:,:] <= a2, var, value) + a[t, :, :] = np.ma.where(a1[t, :, :] <= a2, var, value) elif '==' in condition: - a[t,:,:] = np.ma.where(a1[t,:,:] == a2, var, value) + a[t, :, :] = np.ma.where(a1[t, :, :] == a2, var, value) elif '<' in condition: - a[t,:,:] = np.ma.where(a1[t,:,:] < a2, var, value) + a[t, :, :] = np.ma.where(a1[t, :, :] < a2, var, value) elif '>' in condition: - a[t,:,:] = np.ma.where(a1[t,:,:] > a2, var, value) + a[t, :, :] = np.ma.where(a1[t, :, :] > a2, var, value) new_name = 'cice_where()'.format() - return PhysArray(a, name=new_name, dimensions=[a1.dimensions[0],a1.dimensions[1],a1.dimensions[2]], units=var.units) + return PhysArray(a, name=new_name, dimensions=[a1.dimensions[0], a1.dimensions[1], a1.dimensions[2]], units=var.units) -#=================================================================================================== +#========================================================================= # cice_regions -#=================================================================================================== +#========================================================================= class cice_regionsFunction(Function): key = 'cice_regions' def __init__(self, p_aice, p_uvel, p_vvel, p_HTE, p_HTN, p_siline, multiple): - super(cice_regionsFunction, self).__init__(p_aice, p_uvel, p_vvel, p_HTE, p_HTN, p_siline, multiple) + super(cice_regionsFunction, self).__init__( + p_aice, p_uvel, p_vvel, p_HTE, p_HTN, p_siline, multiple) def __getitem__(self, index): p_aice = self.arguments[0][index] @@ -446,90 +457,98 @@ def __getitem__(self, index): p_siline = self.arguments[5][index] multiple = self.arguments[6] - aice=p_aice - uvel=p_uvel - vvel=p_vvel - HTE=p_HTE - HTN=p_HTN - siline=p_siline - a = np.ma.zeros((aice.data.shape[0],siline.data.shape[0])) + aice = p_aice + uvel = p_uvel + vvel = p_vvel + HTE = p_HTE + HTN = p_HTN + siline = p_siline + a = np.ma.zeros((aice.data.shape[0], siline.data.shape[0])) for t in range(aice.data.shape[0]): - #1 + # 1 i = 92 for j in range(370, 381): - if aice[t,j,i] < 1e+16 and aice[t,j,i+1] < 1e+16 and HTE[j,i] < 1e+16 and uvel[t,j,i] < 1e+16 and uvel[t,j-1,i] < 1e+16: - a[t,0] += 0.5*(aice[t,j,i]+aice[t,j,i+1])*0.5*(HTE[j,i]*uvel[t,j,i]+HTE[j,i]*uvel[t,j-1,i]) - #2 + if aice[t, j, i] < 1e+16 and aice[t, j, i + 1] < 1e+16 and HTE[j, i] < 1e+16 and uvel[t, j, i] < 1e+16 and uvel[t, j - 1, i] < 1e+16: + a[t, 0] += 0.5 * (aice[t, j, i] + aice[t, j, i + 1]) * 0.5 * ( + HTE[j, i] * uvel[t, j, i] + HTE[j, i] * uvel[t, j - 1, i]) + # 2 i = 214 - for j in range(375,377): - if aice[t,j,i] < 1e+16 and aice[t,j,i+1] < 1e+16 and HTE[j,i] < 1e+16 and uvel[t,j,i] < 1e+16 and uvel[t,j-1,i] < 1e+16 and aice[t,j+1,i] < 1e+16 and HTN[j,i] < 1e+16 and vvel[t,j,i] < 1e+16 and vvel[t,j,i-1] < 1e+16: - a[t,1] += 0.5*(aice[t,j,i]+aice[t,j,i+1])*0.5*(HTE[j,i]*uvel[t,j,i]+HTE[j,i]*uvel[t,j-1,i]) + 0.5*(aice[t,j,i]+aice[t,j+1,i])*0.5*(HTN[j,i]*vvel[t,j,i]+HTN[j,i]*vvel[t,j,i-1]) + for j in range(375, 377): + if aice[t, j, i] < 1e+16 and aice[t, j, i + 1] < 1e+16 and HTE[j, i] < 1e+16 and uvel[t, j, i] < 1e+16 and uvel[t, j - 1, i] < 1e+16 and aice[t, j + 1, i] < 1e+16 and HTN[j, i] < 1e+16 and vvel[t, j, i] < 1e+16 and vvel[t, j, i - 1] < 1e+16: + a[t, 1] += 0.5 * (aice[t, j, i] + aice[t, j, i + 1]) * 0.5 * (HTE[j, i] * uvel[t, j, i] + HTE[j, i] * uvel[t, j - 1, i]) + 0.5 * ( + aice[t, j, i] + aice[t, j + 1, i]) * 0.5 * (HTN[j, i] * vvel[t, j, i] + HTN[j, i] * vvel[t, j, i - 1]) j = 366 - for i in range(240,244): - if aice[t,j,i] < 1e+16 and aice[t,j,i+1] < 1e+16 and HTE[j,i] < 1e+16 and uvel[t,j,i] < 1e+16 and uvel[t,j-1,i] < 1e+16 and aice[t,j+1,i] < 1e+16 and HTN[j,i] < 1e+16 and vvel[t,j,i] < 1e+16 and vvel[t,j,i-1] < 1e+16: - a[t,1] += 0.5*(aice[t,j,i]+aice[t,j,i+1])*0.5*(HTE[j,i]*uvel[t,j,i]+HTE[j,i]*uvel[t,j-1,i]) + 0.5*(aice[t,j,i]+aice[t,j+1,i])*0.5*(HTN[j,i]*vvel[t,j,i]+HTN[j,i]*vvel[t,j,i-1]) - #3 + for i in range(240, 244): + if aice[t, j, i] < 1e+16 and aice[t, j, i + 1] < 1e+16 and HTE[j, i] < 1e+16 and uvel[t, j, i] < 1e+16 and uvel[t, j - 1, i] < 1e+16 and aice[t, j + 1, i] < 1e+16 and HTN[j, i] < 1e+16 and vvel[t, j, i] < 1e+16 and vvel[t, j, i - 1] < 1e+16: + a[t, 1] += 0.5 * (aice[t, j, i] + aice[t, j, i + 1]) * 0.5 * (HTE[j, i] * uvel[t, j, i] + HTE[j, i] * uvel[t, j - 1, i]) + 0.5 * ( + aice[t, j, i] + aice[t, j + 1, i]) * 0.5 * (HTN[j, i] * vvel[t, j, i] + HTN[j, i] * vvel[t, j, i - 1]) + # 3 i = 85 - for j in range(344,366): - if aice[t,j,i] < 1e+16 and aice[t,j,i+1] < 1e+16 and HTE[j,i] < 1e+16 and uvel[t,j,i] < 1e+16 and uvel[t,j-1,i] < 1e+16: - a[t,2] += 0.5*(aice[t,j,i]+aice[t,j,i+1])*0.5*(HTE[j,i]*uvel[t,j,i]+HTE[j,i]*uvel[t,j-1,i]) - #4 + for j in range(344, 366): + if aice[t, j, i] < 1e+16 and aice[t, j, i + 1] < 1e+16 and HTE[j, i] < 1e+16 and uvel[t, j, i] < 1e+16 and uvel[t, j - 1, i] < 1e+16: + a[t, 2] += 0.5 * (aice[t, j, i] + aice[t, j, i + 1]) * 0.5 * ( + HTE[j, i] * uvel[t, j, i] + HTE[j, i] * uvel[t, j - 1, i]) + # 4 j = 333 - for i in range(198,201): - if aice[t,j,i] < 1e+16 and aice[t,j+1,i] < 1e+16 and HTN[j,i] < 1e+16 and vvel[t,j,i] < 1e+16 and vvel[t,j,i-1] < 1e+16: - a[t,3] += 0.5*(aice[t,j,i]+aice[t,j+1,i])*0.5*(HTN[j,i]*vvel[t,j,i]+HTN[j,i]*vvel[t,j,i-1]) + for i in range(198, 201): + if aice[t, j, i] < 1e+16 and aice[t, j + 1, i] < 1e+16 and HTN[j, i] < 1e+16 and vvel[t, j, i] < 1e+16 and vvel[t, j, i - 1] < 1e+16: + a[t, 3] += 0.5 * (aice[t, j, i] + aice[t, j + 1, i]) * 0.5 * ( + HTN[j, i] * vvel[t, j, i] + HTN[j, i] * vvel[t, j, i - 1]) - a = a*multiple + a = a * multiple new_name = 'cice_regions()'.format() - return PhysArray(a, name=new_name, dimensions=[p_aice.dimensions[0],p_siline.dimensions[0]], units=uvel.units) + return PhysArray(a, name=new_name, dimensions=[p_aice.dimensions[0], p_siline.dimensions[0]], units=uvel.units) -#=================================================================================================== +#========================================================================= # reduce_luFunction -#=================================================================================================== +#========================================================================= class reduce_luFunction(Function): key = 'reduce_lu' - # np.where(x < 5, x, -1) + # np.where(x < 5, x, -1) - def __init__(self, p_data,p_lu): - super(reduce_luFunction, self).__init__(p_data,p_lu) + def __init__(self, p_data, p_lu): + super(reduce_luFunction, self).__init__(p_data, p_lu) def __getitem__(self, index): p_data = self.arguments[0][index] p_lu = self.arguments[1][index] - #if index is None: - # return PhysArray(p_data.data, dimensions=[p_data.dimensions[0],p_lu.dimensions[0],p_data.dimensions[2],p_data.dimensions[3]]) + # if index is None: + # return PhysArray(p_data.data, + # dimensions=[p_data.dimensions[0],p_lu.dimensions[0],p_data.dimensions[2],p_data.dimensions[3]]) data = p_data.data - lu = p_lu.data + lu = p_lu.data - data2 = np.ma.zeros((data.shape[0],4,data.shape[2],data.shape[3])) + data2 = np.ma.zeros((data.shape[0], 4, data.shape[2], data.shape[3])) for t in range(data.shape[0]): for x in range(data.shape[2]): for y in range(data.shape[3]): - data2[t,0,x,y] = data[t,0,x,y] - data2[t,1,x,y] = 0 - data2[t,2,x,y] = data[t,1,x,y] - data2[t,3,x,y] = data[t,6,x,y]+data[t,7,x,y]+data[t,8,x,y] - data2[data2>=1e+16] = 1e+20 + data2[t, 0, x, y] = data[t, 0, x, y] + data2[t, 1, x, y] = 0 + data2[t, 2, x, y] = data[t, 1, x, y] + data2[t, 3, x, y] = data[t, 6, x, y] + \ + data[t, 7, x, y] + data[t, 8, x, y] + data2[data2 >= 1e+16] = 1e+20 - new_name = 'reduce_lu({}{})'.format(p_data.name,p_lu.name) - return PhysArray(data2, name=new_name, dimensions=[p_data.dimensions[0],p_lu.dimensions[0],p_data.dimensions[2],p_data.dimensions[3]], units=p_data.units) + new_name = 'reduce_lu({}{})'.format(p_data.name, p_lu.name) + return PhysArray(data2, name=new_name, dimensions=[p_data.dimensions[0], p_lu.dimensions[0], p_data.dimensions[2], p_data.dimensions[3]], units=p_data.units) -#=================================================================================================== +#========================================================================= # soilpoolsFunction -#=================================================================================================== +#========================================================================= class get_soilpoolsFunction(Function): key = 'get_soilpools' - def __init__(self, p_data1,p_data2,p_data3,p_soilpool): - super(get_soilpoolsFunction, self).__init__(p_data1,p_data2,p_data3,p_soilpool) + def __init__(self, p_data1, p_data2, p_data3, p_soilpool): + super(get_soilpoolsFunction, self).__init__( + p_data1, p_data2, p_data3, p_soilpool) def __getitem__(self, index): p_data1 = self.arguments[0][index] @@ -542,27 +561,28 @@ def __getitem__(self, index): data3 = p_data3.data soilpool = p_soilpool.data - data = np.ma.zeros((data1.shape[0],3,data1.shape[1],data1.shape[2])) + data = np.ma.zeros((data1.shape[0], 3, data1.shape[1], data1.shape[2])) - data[:,0,:,:] = data1 - data[:,1,:,:] = data2 - data[:,2,:,:] = data3 + data[:, 0, :, :] = data1 + data[:, 1, :, :] = data2 + data[:, 2, :, :] = data3 - data[data>=1e+16] = 1e+20 + data[data >= 1e+16] = 1e+20 data = np.ma.masked_values(data, 1e+20) - new_name = 'soilpools({}{}{}{})'.format(p_data1.name,p_data2.name,p_data3.name,p_soilpool.name) - return PhysArray(data, name=new_name, dimensions=[p_data1.dimensions[0],p_soilpool.dimensions[0],p_data1.dimensions[1],p_data1.dimensions[2]], units=p_data1.units) + new_name = 'soilpools({}{}{}{})'.format( + p_data1.name, p_data2.name, p_data3.name, p_soilpool.name) + return PhysArray(data, name=new_name, dimensions=[p_data1.dimensions[0], p_soilpool.dimensions[0], p_data1.dimensions[1], p_data1.dimensions[2]], units=p_data1.units) -#=================================================================================================== +#========================================================================= # expand_latlonFunction -#=================================================================================================== +#========================================================================= class expand_latlonFunction(Function): key = 'expand_latlon' - def __init__(self, p_data1,p_lat,p_lon): - super(expand_latlonFunction, self).__init__(p_data1,p_lat,p_lon) + def __init__(self, p_data1, p_lat, p_lon): + super(expand_latlonFunction, self).__init__(p_data1, p_lat, p_lon) def __getitem__(self, index): p_data1 = self.arguments[0][index] @@ -573,22 +593,23 @@ def __getitem__(self, index): lat = p_lat.data lon = p_lon.data - data = np.ma.zeros((data1.shape[0],lat.shape[0],lon.shape[0])) + data = np.ma.zeros((data1.shape[0], lat.shape[0], lon.shape[0])) for x in range(lat.shape[0]): for y in range(lon.shape[0]): - data[:,x,y] = data1 + data[:, x, y] = data1 - data[data>=1e+16] = 1e+20 + data[data >= 1e+16] = 1e+20 data = np.ma.masked_values(data, 1e+20) - new_name = 'expand_latlon({}{}{})'.format( p_data1.name, p_lat.name, p_lon.name) - return PhysArray(data, name=new_name, dimensions=[p_data1.dimensions[0],p_lat.dimensions[0],p_lon.dimensions[0]], units=p_data1.units) + new_name = 'expand_latlon({}{}{})'.format( + p_data1.name, p_lat.name, p_lon.name) + return PhysArray(data, name=new_name, dimensions=[p_data1.dimensions[0], p_lat.dimensions[0], p_lon.dimensions[0]], units=p_data1.units) -#=================================================================================================== +#========================================================================= # ocean_basinFunction -#=================================================================================================== +#========================================================================= class ocean_basinFunction(Function): key = 'ocean_basin' @@ -604,19 +625,19 @@ def __getitem__(self, index): comp = int(p_comp) basin = p_basin.data - data = np.ma.zeros((data1.shape[0],data1.shape[4],data1.shape[3],basin.shape[0])) + data = np.ma.zeros( + (data1.shape[0], data1.shape[4], data1.shape[3], basin.shape[0])) for t in range(data1.shape[0]): for x in range(data1.shape[4]): for y in range(data1.shape[3]): - data[t,x,y,0] = data1[t,1,comp,y,x] - data[t,x,y,1] = data1[t,0,comp,y,x]-data1[t,1,comp,y,x] - data[t,x,y,2] = data1[t,0,comp,y,x] + data[t, x, y, 0] = data1[t, 1, comp, y, x] + data[t, x, y, 1] = data1[t, 0, comp, y, x] - \ + data1[t, 1, comp, y, x] + data[t, x, y, 2] = data1[t, 0, comp, y, x] - data[data>=1e+16] = 1e+20 + data[data >= 1e+16] = 1e+20 data = np.ma.masked_values(data, 1e+20) new_name = 'ocean_basin({}{})'.format(p_data1.name, p_basin.name) - return PhysArray(data, name=new_name, dimensions=[p_data1.dimensions[0],p_data1.dimensions[4],p_data1.dimensions[3],p_basin.dimensions[0]], units=p_data1.units) - - + return PhysArray(data, name=new_name, dimensions=[p_data1.dimensions[0], p_data1.dimensions[4], p_data1.dimensions[3], p_basin.dimensions[0]], units=p_data1.units) From b4c0701100476994f1cf9b29f5a2df7da0b382e1 Mon Sep 17 00:00:00 2001 From: Keith Lindsay Date: Mon, 27 Aug 2018 13:33:41 -0600 Subject: [PATCH 03/23] add diff_axis1_ind0bczero_4d function compute diff along axis1 of 4d data, using boundary condition=0 at index=0 of axis1 --- source/pyconform/modules/commonfunctions.py | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/source/pyconform/modules/commonfunctions.py b/source/pyconform/modules/commonfunctions.py index d55217d5..5e154d71 100644 --- a/source/pyconform/modules/commonfunctions.py +++ b/source/pyconform/modules/commonfunctions.py @@ -269,6 +269,37 @@ def __getitem__(self, index): return PhysArray(a, name=new_name, units=p_data.units) +#========================================================================= +# diff_axis1_ind0bczero_4dFunction +#========================================================================= +class diff_axis1_ind0bczero_4dFunction(Function): + key = 'diff_axis1_ind0bczero_4d' + + def __init__(self, data): + super(diff_axis1_ind0bczero_4dFunction, self).__init__(data) + data_info = data if is_constant(data) else data[None] + if not isinstance(data_info, PhysArray): + raise TypeError('diff_axis1_ind0bczero_4d: data must be a PhysArray') + if len(data_info.dimensions) != 4: + raise DimensionsError('diff_axis1_ind0bczero_4d: data can only be 4D') + + def __getitem__(self, index): + p_data = self.arguments[0][index] + + if index is None: + return PhysArray(np.zeros((0, 0, 0, 0)), dimensions=[p_data.dimensions]) + + data = p_data.data + + a = np.zeros((p_data.shape)) + + a[:, 0, :, :] = data[:, 0, :, :] + a[:, 1:, :, :] = np.diff(data, axis=1) + + new_name = 'diff_axis1_ind0bczero_4d({})'.format(p_data.name) + + return PhysArray(a, name=new_name, units=p_data.units) + #========================================================================= # sftofFunction From 2b5b9cda5db1516c38c52a4a0c3de936e0ac0cd0 Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Wed, 29 Aug 2018 15:07:04 -0600 Subject: [PATCH 04/23] Just a little refactor It should be ready for testing, now. --- source/pyconform/modules/commonfunctions.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/source/pyconform/modules/commonfunctions.py b/source/pyconform/modules/commonfunctions.py index 5e154d71..e3a26c1d 100644 --- a/source/pyconform/modules/commonfunctions.py +++ b/source/pyconform/modules/commonfunctions.py @@ -269,6 +269,7 @@ def __getitem__(self, index): return PhysArray(a, name=new_name, units=p_data.units) + #========================================================================= # diff_axis1_ind0bczero_4dFunction #========================================================================= @@ -287,18 +288,18 @@ def __getitem__(self, index): p_data = self.arguments[0][index] if index is None: - return PhysArray(np.zeros((0, 0, 0, 0)), dimensions=[p_data.dimensions]) - - data = p_data.data - - a = np.zeros((p_data.shape)) - - a[:, 0, :, :] = data[:, 0, :, :] - a[:, 1:, :, :] = np.diff(data, axis=1) + a = np.zeros((0, 0, 0, 0)) + else: + data = p_data.data - new_name = 'diff_axis1_ind0bczero_4d({})'.format(p_data.name) + a = np.empty((p_data.shape)) + a[:, 0, :, :] = data[:, 0, :, :] + a[:, 1:, :, :] = np.diff(data, axis=1) - return PhysArray(a, name=new_name, units=p_data.units) + new_name = '{}({})'.format(self.key, p_data.name) + new_units = p_data.units + new_dims = p_data.dimensions + return PhysArray(a, name=new_name, units=new_units, dimensions=new_dims) #========================================================================= From 96d55b3826a7d93b3f7d314d86678de334aaeee7 Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Tue, 11 Sep 2018 10:56:53 -0600 Subject: [PATCH 05/23] Adding DynVarMIP functions (needs testing) --- source/pyconform/modules/dynvarmipdiags.py | 193 ++++++++++++++++++ .../pyconform/modules/dynvarmipfunctions.py | 125 ++++++++++++ source/pyconform/modules/idl.py | 167 +++++++++++++++ 3 files changed, 485 insertions(+) create mode 100644 source/pyconform/modules/dynvarmipdiags.py create mode 100644 source/pyconform/modules/dynvarmipfunctions.py create mode 100644 source/pyconform/modules/idl.py diff --git a/source/pyconform/modules/dynvarmipdiags.py b/source/pyconform/modules/dynvarmipdiags.py new file mode 100644 index 00000000..4e2dc495 --- /dev/null +++ b/source/pyconform/modules/dynvarmipdiags.py @@ -0,0 +1,193 @@ +""" +Basic functions for the DynVarMIP Diagnostics + +Copyright 2017-2018, University Corporation for Atmospheric Research +LICENSE: See the LICENSE.rst file for details +""" + +import numpy +from pyconform.modules.idl import deriv, int_tabulated + +# Constants +p0 = 101325. +a = 6.37123e6 +om = 7.29212e-5 +H = 7000. +g0 = 9.80665 + + +def _init_wtem(time, levi, lat, wzm, vthzm, thzm): + ntime = len(time) + nlevi = len(levi) + nlat = len(lat) + + latrad = (lat / 180.0) * numpy.pi + coslat = numpy.cos(latrad) + levi100 = 100.0 * levi + + _wzm = -1.0 * numpy.einsum('ijk,j->ijk', wzm, levi100) / H + + wtem = numpy.zeros((ntime, nlevi, nlat), dtype='d') + for itime in range(ntime): + dthdp = deriv(levi100, thzm[itime, :, :]) + psieddy = vthzm[itime, :, :] / dthdp + dpsidy = deriv(latrad, psieddy * coslat, axis=1) / (a * coslat) + wtem[itime, :, :] = _wzm[itime, :, :] + dpsidy + return wtem + + +def wtem(time, levi, lat, wzm, vthzm, thzm): + levi100 = 100.0 * levi + wtem = _init_wtem(time, levi, lat, wzm, vthzm, thzm) + wtem = -1.0 * numpy.einsum('ijk,j->ijk', wtem, H / levi100) + return wtem + + +def vtem(time, levi, lat, vzm, vthzm, thzm): + ntime = len(time) + nlevi = len(levi) + nlat = len(lat) + + levi100 = 100.0 * levi + + vtem = numpy.zeros((ntime, nlevi, nlat), dtype='d') + for itime in range(ntime): + dthdp = deriv(levi100, thzm[itime, :, :]) + psieddy = vthzm[itime, :, :] / dthdp + dpsidp = deriv(levi100, psieddy) + vtem[itime, :, :] = vzm[itime, :, :] - dpsidp + return vtem + + +def utendvtem(time, levi, lat, uzm, vzm, vthzm, thzm): + ntime = len(time) + nlevi = len(levi) + nlat = len(lat) + + latrad = (lat / 180.0) * numpy.pi + coslat = numpy.cos(latrad) + f = 2 * om * numpy.sin(latrad) + vtem = vtem(time, levi, lat, vzm, vthzm, thzm) + + utendvtem = numpy.zeros((ntime, nlevi, nlat), dtype='d') + for itime in range(ntime): + ucos = numpy.einsum('ij,j->ij', uzm[itime, :, :], coslat) + dudphi = deriv(latrad, ucos, axis=1) / a + utendvtem[itime, :, :] = vtem[itime, :, :] * (f - dudphi) + return utendvtem + + +def utendwtem(time, levi, lat, uzm, wzm, vthzm, thzm): + ntime = len(time) + nlevi = len(levi) + nlat = len(lat) + + levi100 = 100.0 * levi + wtem = _init_wtem(time, levi, lat, wzm, vthzm, thzm) + + utendwtem = numpy.zeros((ntime, nlevi, nlat), dtype='d') + for itime in range(ntime): + dudp = deriv(levi100, uzm[itime, :, :]) + utendwtem[itime, :, :] = -1.0 * wtem[itime, :, :] * dudp + return utendwtem + + +def _init_epfy(time, levi, lat, uzm, uvzm, vthzm, thzm): + ntime = len(time) + nlevi = len(levi) + nlat = len(lat) + + latrad = (lat / 180.0) * numpy.pi + coslat = numpy.cos(latrad) + levi100 = 100.0 * levi + + epfy = numpy.zeros((ntime, nlevi, nlat), dtype='d') + for itime in range(ntime): + dthdp = deriv(levi100, thzm[itime, :, :]) + dudp = deriv(levi100, uzm[itime, :, :]) + psieddy = vthzm[itime, :, :] / dthdp + epfy[itime, :, :] = a * coslat * (dudp * psieddy - uvzm[itime, :, :]) + return epfy + + +def epfy(time, levi, lat, uzm, uvzm, vthzm, thzm): + levi100 = 100.0 * levi + epfy = _init_epfy(time, levi, lat, uzm, uvzm, vthzm, thzm) + epfy = numpy.einsum('ijk,j->ijk', epfy, levi100) / p0 + return epfy + + +def _init_epfz(time, levi, lat, uzm, uwzm, vthzm, thzm): + ntime = len(time) + nlevi = len(levi) + nlat = len(lat) + + latrad = (lat / 180.0) * numpy.pi + coslat = numpy.cos(latrad) + f = 2 * om * numpy.sin(latrad) + levi100 = 100.0 * levi + + _uwzm = -1.0 * numpy.einsum('ijk,j->ijk', uwzm, levi100) / H + + epfz = numpy.zeros((ntime, nlevi, nlat), dtype='d') + for itime in range(ntime): + ucos = numpy.einsum('ij,j->ij', uzm[itime, :, :], coslat) + dudphi = deriv(latrad, ucos, axis=1) / a + dthdp = deriv(levi100, thzm[itime, :, :]) + psieddy = vthzm[itime, :, :] / dthdp + epfz[itime, :, :] = a * coslat * \ + (((f - dudphi) * psieddy) - _uwzm[itime, :, :]) + return epfz + + +def epfz(time, levi, lat, uzm, uwzm, vthzm, thzm): + epfz = _init_epfz(time, levi, lat, uzm, uwzm, vthzm, thzm) + epfz = -1.0 * (H / p0) * epfz + return epfz + + +def utendepfd(time, levi, lat, uzm, uvzm, uwzm, vthzm, thzm): + ntime = len(time) + nlevi = len(levi) + nlat = len(lat) + + latrad = (lat / 180.0) * numpy.pi + coslat = numpy.cos(latrad) + levi100 = 100.0 * levi + + epfy = _init_epfy(time, levi, lat, uzm, uvzm, vthzm, thzm) + epfz = _init_epfz(time, levi, lat, uzm, uwzm, vthzm, thzm) + + utendepfd = numpy.zeros((ntime, nlevi, nlat), dtype='d') + for itime in range(ntime): + depfydphi = deriv(latrad, epfy[itime, :, :] + * coslat, axis=1) / (a * coslat) + depfzdp = deriv(levi100, epfz[itime, :, :]) + utendepfd[itime, :, :] = (depfydphi + depfzdp) / (a * coslat) + return utendepfd + + +def psitem(time, levi, lat, vzm, vthzm, thzm): + ntime = len(time) + nlevi = len(levi) + nlat = len(lat) + + latrad = (lat / 180.0) * numpy.pi + coslat = numpy.cos(latrad) + levi100 = levi * 100.0 + + psitem = numpy.zeros((ntime, nlevi, nlat), dtype='d') + vzmwithzero = numpy.zeros((nlevi + 1, nlat), dtype='d') + levi100withzero = numpy.zeros((nlevi + 1,), dtype='d') + levi100withzero[1:] = levi100 + for itime in range(ntime): + dthdp = deriv(levi100, thzm[itime, :, :]) + psieddy = vthzm[itime, :, :] / dthdp + vzmwithzero[1:, :] = vzm[itime, :, :] + for ilev in range(1, nlevi + 1): + result = int_tabulated( + levi100withzero[0:ilev + 1], vzmwithzero[0:ilev + 1, :]) + psitem[itime, ilev - 1, + :] = ((2 * numpy.pi * a * coslat) / g0) * (result - psieddy[ilev - 1, :]) + + return psitem diff --git a/source/pyconform/modules/dynvarmipfunctions.py b/source/pyconform/modules/dynvarmipfunctions.py new file mode 100644 index 00000000..3c9b3a34 --- /dev/null +++ b/source/pyconform/modules/dynvarmipfunctions.py @@ -0,0 +1,125 @@ +""" +DynVarMIP Diagnostic Function Classes + +Copyright 2017-2018, University Corporation for Atmospheric Research +LICENSE: See the LICENSE.rst file for details +""" + +from pyconform.physarray import PhysArray +from pyconform.functions import Function + +import pyconform.modules.dynvarmipdiags as dvmd + + +#========================================================================= +# DynVarMIPFunction +#========================================================================= +class DynVarMIPFunction(Function): + key = 'dynvarmipfunc' + + def __init__(self, *args): + super(DynVarMIPFunction, self).__init__(*args) + self._dynvarmip_func = None + + def __getitem__(self, index): + args = [self.arguments[i][index] for i in len(self.arguments)] + arg_names = ','.join([arg.name for arg in args]) + data = self._dynvarmip_func(*args) + name = '{}({})'.format(self.key, arg_names) + return PhysArray(data, name=name) + + +#========================================================================= +# wtemDynVarMIPFunction +#========================================================================= +class wtemDynVarMIPFunction(Function): + key = 'dynvarmip_wtem' + + def __init__(self, time, levi, lat, wzm, vthzm, thzm): + super(wtemDynVarMIPFunction, self).__init__( + time, levi, lat, wzm, vthzm, thzm) + self._dynvarmip_func = dvmd.wtem + + +#========================================================================= +# utendwtemDynVarMIPFunction +#========================================================================= +class utendwtemDynVarMIPFunction(Function): + key = 'dynvarmip_utendwtem' + + def __init__(self, time, levi, lat, uzm, wzm, vthzm, thzm): + super(utendwtemDynVarMIPFunction, self).__init__( + time, levi, lat, uzm, wzm, vthzm, thzm) + self._dynvarmip_func = dvmd.utendwtem + + +#========================================================================= +# vtemDynVarMIPFunction +#========================================================================= +class vtemDynVarMIPFunction(Function): + key = 'dynvarmip_vtem' + + def __init__(self, time, levi, lat, vzm, vthzm, thzm): + super(vtemDynVarMIPFunction, self).__init__( + time, levi, lat, vzm, vthzm, thzm) + self._dynvarmip_func = dvmd.vtem + + +#========================================================================= +# utendvtemDynVarMIPFunction +#========================================================================= +class utendvtemDynVarMIPFunction(Function): + key = 'dynvarmip_utendvtem' + + def __init__(self, time, levi, lat, uzm, vzm, vthzm, thzm): + super(utendvtemDynVarMIPFunction, self).__init__( + time, levi, lat, uzm, vzm, vthzm, thzm) + self._dynvarmip_func = dvmd.utendvtem + + +#========================================================================= +# epfyDynVarMIPFunction +#========================================================================= +class epfyDynVarMIPFunction(Function): + key = 'dynvarmip_epfy' + + def __init__(self, time, levi, lat, uzm, uvzm, vthzm, thzm): + super(epfyDynVarMIPFunction, self).__init__( + time, levi, lat, uzm, uvzm, vthzm, thzm) + self._dynvarmip_func = dvmd.epfy + + +#========================================================================= +# epfzDynVarMIPFunction +#========================================================================= +class epfzDynVarMIPFunction(Function): + key = 'dynvarmip_epfz' + + def __init__(self, time, levi, lat, uzm, uwzm, vthzm, thzm): + super(epfzDynVarMIPFunction, self).__init__( + time, levi, lat, uzm, uwzm, vthzm, thzm) + self._dynvarmip_func = dvmd.epfz + + +#========================================================================= +# utendepfdDynVarMIPFunction +#========================================================================= +class utendepfdDynVarMIPFunction(Function): + key = 'dynvarmip_utendepfd' + + def __init__(self, time, levi, lat, uzm, uvzm, uwzm, vthzm, thzm): + super(utendepfdDynVarMIPFunction, self).__init__( + time, levi, lat, uzm, uvzm, uwzm, vthzm, thzm) + self._dynvarmip_func = dvmd.utendepfd + + +#========================================================================= +# psitemDynVarMIPFunction +#========================================================================= +class psitemDynVarMIPFunction(Function): + key = 'dynvarmip_psitem' + + def __init__(self, time, levi, lat, vzm, vthzm, thzm): + super(psitemDynVarMIPFunction, self).__init__( + time, levi, lat, vzm, vthzm, thzm) + self._dynvarmip_func = dvmd.psitem diff --git a/source/pyconform/modules/idl.py b/source/pyconform/modules/idl.py new file mode 100644 index 00000000..8b7627fa --- /dev/null +++ b/source/pyconform/modules/idl.py @@ -0,0 +1,167 @@ +""" +IDL Functions Re-written in Python + +These functions are modeled to mimic calculations of the IDL functions +'deriv', 'spl_init', 'spl_interp', and 'int_tabulated', which are used +to compute the DynVarMIP diagnostics. + +IDL code for these functions can be found in the GDL, open-source version +of IDL, available at https://github.com/gnudatalanguage/gdl. Obviously, +the code was not copied, but the Python code was based on the original IDL +code to determine operation order and bit-for-bit correctness. + +Copyright 2017-2018, University Corporation for Atmospheric Research +LICENSE: See the LICENSE.rst file for details +""" + +import numpy + +_signature_letters = 'ijklmnabcdefgh' + + +def deriv(x1, y1, axis=0): + """ + Python version of GDL deriv function + """ + x0 = numpy.roll(x1, 1) + x2 = numpy.roll(x1, -1) + y0 = numpy.roll(y1, 1, axis=axis) + y2 = numpy.roll(y1, -1, axis=axis) + + x12 = x1 - x2 + x01 = x0 - x1 + x02 = x0 - x2 + + a0 = x12 / (x01 * x02) + a1 = 1.0 / x12 - 1.0 / x01 + a2 = -x01 / (x02 * x12) + + xsig = _signature_letters[axis] + ysig = _signature_letters[:numpy.ndim(y1)] + sig = '{},{}->{}'.format(ysig, xsig, ysig) + d = (numpy.einsum(sig, y0, a0) + + numpy.einsum(sig, y1, a1) + + numpy.einsum(sig, y2, a2)) + + i0 = tuple(0 if i == axis else slice(None) for i in range(numpy.ndim(y1))) + i1 = tuple(1 if i == axis else slice(None) for i in range(numpy.ndim(y1))) + i2 = tuple(2 if i == axis else slice(None) for i in range(numpy.ndim(y1))) + a0 = (x01[1] + x02[1]) / (x01[1] * x02[1]) + a1 = -x02[1] / (x01[1] * x12[1]) + a2 = x01[1] / (x02[1] * x12[1]) + d[i0] = y1[i0] * a0 + y1[i1] * a1 + y1[i2] * a2 + + i0 = tuple(-3 if i == axis else slice(None) for i in range(numpy.ndim(y1))) + i1 = tuple(-2 if i == axis else slice(None) for i in range(numpy.ndim(y1))) + i2 = tuple(-1 if i == axis else slice(None) for i in range(numpy.ndim(y1))) + a0 = -x12[-2] / (x01[-2] * x02[-2]) + a1 = x02[-2] / (x01[-2] * x12[-2]) + a2 = -(x02[-2] + x12[-2]) / (x02[-2] * x12[-2]) + d[i2] = y1[i0] * a0 + y1[i1] * a1 + y1[i2] * a2 + return d + + +def spl_init(x1, y1, axis=0): + """ + Python version of GDL spl_init function + """ + n = len(x1) + x0 = numpy.roll(x1, 1) + x2 = numpy.roll(x1, -1) + y0 = numpy.roll(y1, 1, axis=axis) + y2 = numpy.roll(y1, -1, axis=axis) + + psig = (x1 - x0) / (x2 - x0) + psig[0] = (x1[0] - x2[0]) / (x0[0] - x2[0]) + psig[-1] = (x1[-1] - x2[-1]) / (x0[-1] - x2[-1]) + + xsig = _signature_letters[axis] + ysig = _signature_letters[:numpy.ndim(y1)] + sig = '{},{}->{}'.format(ysig, xsig, ysig) + + pu_a = numpy.einsum(sig, y2 - y1, 1.0 / ((x2 - x1) * (x2 - x0))) + pu_b = numpy.einsum(sig, y1 - y0, 1.0 / ((x1 - x0) * (x2 - x0))) + pu = pu_a - pu_b + + b = numpy.empty_like(y1) + u = numpy.empty_like(y1) + + i0 = tuple(0 if i == axis else slice(None) for i in range(numpy.ndim(y1))) + b[i0] = 0.0 + u[i0] = 0.0 + for i in range(1, n - 1): + j = tuple(i if k == axis else slice(None) + for k in range(numpy.ndim(y1))) + jm1 = tuple(i - 1 if k == axis else slice(None) + for k in range(numpy.ndim(y1))) + p = psig[i] * b[jm1] + 2.0 + b[j] = (psig[i] - 1.0) / p + u[j] = (6.0 * pu[j] - psig[i] * u[jm1]) / p + i0 = tuple(-1 if i == axis else slice(None) for i in range(numpy.ndim(y1))) + b[i0] = 0.0 + for i in range(n - 2, -1, -1): + j = tuple(i if k == axis else slice(None) + for k in range(numpy.ndim(y1))) + jp1 = tuple(i + 1 if k == axis else slice(None) + for k in range(numpy.ndim(y1))) + b[j] = b[j] * b[jp1] + u[j] + return b + + +def spl_interp(xa, ya, y2a, x, axis=0): + """ + Python version of GDL spl_interp function + """ + n = len(xa) + valloc = numpy.digitize(x, xa) - 1 + klo = [] + for i in valloc: + klo.append(min(max(i, 0), (n - 2))) + klo = numpy.array(klo) + khi = klo + 1 + xahi = xa[khi] + xalo = xa[klo] + + h = xahi - xalo + + jhi = [khi if k == axis else slice(None) for k in range(numpy.ndim(ya))] + jlo = [klo if k == axis else slice(None) for k in range(numpy.ndim(ya))] + yahi = ya[jhi] + yalo = ya[jlo] + y2ahi = y2a[jhi] + y2alo = y2a[jlo] + + a = (xahi - x) / h + b = (x - xalo) / h + c = (a ** 3 - a) * (h ** 2) / 6.0 + d = (b ** 3 - b) * (h ** 2) / 6.0 + + xsig = _signature_letters[axis] + ysig = _signature_letters[:numpy.ndim(ya)] + sig = '{},{}->{}'.format(ysig, xsig, ysig) + + return (numpy.einsum(sig, yalo, a) + numpy.einsum(sig, yahi, b) + + numpy.einsum(sig, y2alo, c) + numpy.einsum(sig, y2ahi, d)) + + +def int_tabulated(x, y, axis=0): + """ + Python version of GDL int_tabulated function + """ + nx = len(x) + nseg = nx - 1 + while nseg % 4 != 0: + nseg += 1 + nint = nseg / 4 + xmin = numpy.min(x) + xmax = numpy.max(x) + h = (xmax - xmin) / float(nseg) + x_unif = numpy.linspace(xmin, xmax, nseg + 1) + z_spl = spl_init(x, y, axis=axis) + z_unif = spl_interp(x, y, z_spl, x_unif, axis=axis) + coef_l = [7] + [32, 12, 32, 14] * (nint - 1) + [32, 12, 32, 7] + coeffs = 2.0 * h * numpy.array(coef_l, dtype='d') / 45.0 + xsig = _signature_letters[axis] + ysig = _signature_letters[:numpy.ndim(y)] + sig = '{},{}->{}'.format(ysig, xsig, ysig) + return numpy.sum(numpy.einsum(sig, z_unif, coeffs), axis=axis) From a8249f8b84646856b5e8a675914b5fef6b642797 Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Tue, 25 Sep 2018 09:45:51 -0600 Subject: [PATCH 06/23] Fix some tests with new cf_units --- source/test/functionsTests.py | 389 +++++++++++++++++++++++----------- 1 file changed, 260 insertions(+), 129 deletions(-) diff --git a/source/test/functionsTests.py b/source/test/functionsTests.py index a3577485..57bb4662 100644 --- a/source/test/functionsTests.py +++ b/source/test/functionsTests.py @@ -15,9 +15,9 @@ import operator as op -#=================================================================================================== +#========================================================================= # FindTests -#=================================================================================================== +#========================================================================= class FindTests(unittest.TestCase): """ Unit tests for finding functions and operators @@ -35,7 +35,8 @@ def test_operator_neg(self): testname = 'find_operator({!r}, {})'.format(key, numargs) actual = functions.find_operator(key, numargs) expected = functions.NegationOperator - print_test_message(testname, actual=actual, expected=expected, key=key, numargs=numargs) + print_test_message(testname, actual=actual, + expected=expected, key=key, numargs=numargs) self.assertEqual(actual, expected, '{} failed'.format(testname)) def test_operator_add(self): @@ -44,7 +45,8 @@ def test_operator_add(self): testname = 'find_operator({!r}, {})'.format(key, numargs) actual = functions.find_operator(key, numargs) expected = functions.AdditionOperator - print_test_message(testname, actual=actual, expected=expected, key=key, numargs=numargs) + print_test_message(testname, actual=actual, + expected=expected, key=key, numargs=numargs) self.assertEqual(actual, expected, '{} failed'.format(testname)) def test_operator_sub(self): @@ -53,7 +55,8 @@ def test_operator_sub(self): testname = 'find_operator({!r}, {})'.format(key, numargs) actual = functions.find_operator(key, numargs) expected = functions.SubtractionOperator - print_test_message(testname, actual=actual, expected=expected, key=key, numargs=numargs) + print_test_message(testname, actual=actual, + expected=expected, key=key, numargs=numargs) self.assertEqual(actual, expected, '{} failed'.format(testname)) def test_operator_mul(self): @@ -92,7 +95,8 @@ def test_operator_numargs_failure(self): numargs = 1 testname = 'find_operator({!r}, {})'.format(key, numargs) expected = KeyError - print_test_message(testname, key=key, numargs=numargs, expected=expected) + print_test_message(testname, key=key, + numargs=numargs, expected=expected) self.assertRaises(KeyError, functions.find_operator, key, numargs) def test_function_sqrt(self): @@ -125,7 +129,7 @@ def test_mul(self): expected = functions.MultiplicationOperator print_test_message(testname, key=key, actual=actual, expected=expected) self.assertEqual(actual, expected, '{} failed'.format(testname)) - + def test_key_failure(self): key = '?' testname = 'find({!r})'.format(key) @@ -138,14 +142,17 @@ def test_numargs_failure(self): numargs = 3 testname = 'find({!r}, {})'.format(key, numargs) expected = KeyError - print_test_message(testname, key=key, numargs=numargs, expected=expected) + print_test_message(testname, key=key, + numargs=numargs, expected=expected) self.assertRaises(KeyError, functions.find, key, numargs) def test_user_defined(self): class myfunc(functions.Function): key = 'myfunc' + def __init__(self, x): super(myfunc, self).__init__(x) + def __getitem__(self, index): return self.arguments[0][index] @@ -155,7 +162,7 @@ def __getitem__(self, index): expected = myfunc print_test_message(testname, key=key, actual=actual, expected=expected) self.assertEqual(actual, expected, '{} failed'.format(testname)) - + def test_list_operators(self): testname = 'list_operators()' actual = functions.list_operators() @@ -166,14 +173,15 @@ def test_list_operators(self): def test_list_functions(self): testname = 'list_functions()' actual = sorted(functions.list_functions()) - expected = sorted(['sqrt', 'mean', 'up', 'down', 'chunits', 'limit']) + expected = sorted(['chunits', 'down', 'limit', 'max', + 'mean', 'min', 'rmunits', 'sqrt', 'sum', 'up']) print_test_message(testname, actual=actual, expected=expected) self.assertEqual(actual, expected, '{} failed'.format(testname)) - -#=============================================================================== + +#========================================================================= # EvaluationTests -#=============================================================================== +#========================================================================= class EvaluationTests(unittest.TestCase): """ Unit tests for evaluating functions and operators @@ -187,7 +195,8 @@ def test_op_neg_int(self): func = funcref(indata) actual = func[:] expected = op.neg(indata) - print_test_message(testname, input=indata, actual=actual, expected=expected) + print_test_message(testname, input=indata, + actual=actual, expected=expected) self.assertEqual(actual, expected, '{} failed'.format(testname)) def test_op_neg_float(self): @@ -197,7 +206,8 @@ def test_op_neg_float(self): func = functions.find(key, 1) actual = func(indata)[:] expected = op.neg(indata) - print_test_message(testname, input=indata, actual=actual, expected=expected) + print_test_message(testname, input=indata, + actual=actual, expected=expected) self.assertEqual(actual, expected, '{} failed'.format(testname)) def test_op_neg_physarray(self): @@ -208,8 +218,10 @@ def test_op_neg_physarray(self): func = funcref(indata) actual = func[:] expected = PhysArray(-3, name='3', units='m') - print_test_message(testname, input=indata, actual=actual, expected=expected) - np.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) + print_test_message(testname, input=indata, + actual=actual, expected=expected) + np.testing.assert_array_equal( + actual, expected, '{} failed'.format(testname)) def test_op_add_int(self): key = '+' @@ -220,7 +232,8 @@ def test_op_add_int(self): func = funcref(left, right) actual = func[:] expected = 5 - print_test_message(testname, actual=actual, expected=expected, left=left, right=right) + print_test_message(testname, actual=actual, + expected=expected, left=left, right=right) self.assertEqual(actual, expected, '{} failed'.format(testname)) def test_op_add_float(self): @@ -232,7 +245,8 @@ def test_op_add_float(self): func = funcref(left, right) actual = func[:] expected = 5.6 - print_test_message(testname, actual=actual, expected=expected, left=left, right=right) + print_test_message(testname, actual=actual, + expected=expected, left=left, right=right) self.assertEqual(actual, expected, '{} failed'.format(testname)) def test_op_add_physarray(self): @@ -243,11 +257,15 @@ def test_op_add_physarray(self): funcref = functions.find(key, 2) func = funcref(x, y) actual = func[:] - expected = PhysArray(7901.5, name='(x+convert(y, from=km, to=m))', units='m') - print_test_message(testname, actual=actual, expected=expected, x=x, y=y) + expected = PhysArray( + 7901.5, name='(x+convert(y, from=km, to=m))', units='m') + print_test_message(testname, actual=actual, + expected=expected, x=x, y=y) self.assertEqual(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed - units'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.units, expected.units, + '{} failed - units'.format(testname)) def test_op_sub_int(self): key = '-' @@ -258,7 +276,8 @@ def test_op_sub_int(self): func = funcref(left, right) actual = func[:] expected = -1 - print_test_message(testname, actual=actual, expected=expected, left=left, right=right) + print_test_message(testname, actual=actual, + expected=expected, left=left, right=right) self.assertEqual(actual, expected, '{} failed'.format(testname)) def test_op_sub_float(self): @@ -270,7 +289,8 @@ def test_op_sub_float(self): func = funcref(left, right) actual = func[:] expected = 2.4 - 3.2 - print_test_message(testname, actual=actual, expected=expected, left=left, right=right) + print_test_message(testname, actual=actual, + expected=expected, left=left, right=right) self.assertEqual(actual, expected, '{} failed'.format(testname)) def test_op_sub_physarray(self): @@ -280,11 +300,15 @@ def test_op_sub_physarray(self): testname = '({} {} {})'.format(x, key, y) func = functions.find(key, 2) actual = func(x, y)[:] - expected = PhysArray(-7898.5, name='(x-convert(y, from=km, to=m))', units='m') - print_test_message(testname, actual=actual, expected=expected, x=x, y=y) + expected = PhysArray(-7898.5, + name='(x-convert(y, from=km, to=m))', units='m') + print_test_message(testname, actual=actual, + expected=expected, x=x, y=y) self.assertEqual(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed - units'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.units, expected.units, + '{} failed - units'.format(testname)) def test_op_mul_int(self): key = '*' @@ -295,7 +319,8 @@ def test_op_mul_int(self): func = funcref(left, right) actual = func[:] expected = 6 - print_test_message(testname, actual=actual, expected=expected, left=left, right=right) + print_test_message(testname, actual=actual, + expected=expected, left=left, right=right) self.assertEqual(actual, expected, '{} failed'.format(testname)) def test_op_mul_float(self): @@ -307,7 +332,8 @@ def test_op_mul_float(self): func = funcref(left, right) actual = func[:] expected = 2.4 * 3.2 - print_test_message(testname, actual=actual, expected=expected, left=left, right=right) + print_test_message(testname, actual=actual, + expected=expected, left=left, right=right) self.assertEqual(actual, expected, '{} failed'.format(testname)) def test_op_mul_physarray(self): @@ -318,10 +344,13 @@ def test_op_mul_physarray(self): func = functions.find(key, 2) actual = func(x, y)[:] expected = PhysArray(1.5 * 7.9, name='(x*y)', units='m-km') - print_test_message(testname, actual=actual, expected=expected, x=x, y=y) + print_test_message(testname, actual=actual, + expected=expected, x=x, y=y) self.assertEqual(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed - units'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.units, expected.units, + '{} failed - units'.format(testname)) def test_op_div_int(self): key = '/' @@ -332,7 +361,8 @@ def test_op_div_int(self): func = funcref(left, right) actual = func[:] expected = 2 - print_test_message(testname, actual=actual, expected=expected, left=left, right=right) + print_test_message(testname, actual=actual, + expected=expected, left=left, right=right) self.assertEqual(actual, expected, '{} failed'.format(testname)) def test_op_div_float(self): @@ -344,7 +374,8 @@ def test_op_div_float(self): func = funcref(left, right) actual = func[:] expected = 2.4 / 3.2 - print_test_message(testname, actual=actual, expected=expected, left=left, right=right) + print_test_message(testname, actual=actual, + expected=expected, left=left, right=right) self.assertEqual(actual, expected, '{} failed'.format(testname)) def test_op_div_physarray(self): @@ -355,11 +386,14 @@ def test_op_div_physarray(self): func = functions.find(key, 2) actual = func(x, y)[:] expected = PhysArray(1.5 / 7.9, name='(x/y)', units='0.001 1') - print_test_message(testname, actual=actual, expected=expected, x=x, y=y) + print_test_message(testname, actual=actual, + expected=expected, x=x, y=y) np.testing.assert_array_almost_equal(actual, expected, 16, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed - units'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.units, expected.units, + '{} failed - units'.format(testname)) def test_op_pow_int(self): key = '**' @@ -369,7 +403,8 @@ def test_op_pow_int(self): func = functions.find(key, 2) actual = func(left, right)[:] expected = 7 ** 3 - print_test_message(testname, actual=actual, expected=expected, left=left, right=right) + print_test_message(testname, actual=actual, + expected=expected, left=left, right=right) self.assertEqual(actual, expected, '{} failed'.format(testname)) def test_op_pow_float(self): @@ -380,7 +415,8 @@ def test_op_pow_float(self): func = functions.find(key, 2) actual = func(left, right)[:] expected = 2.4 ** 3.2 - print_test_message(testname, actual=actual, expected=expected, left=left, right=right) + print_test_message(testname, actual=actual, + expected=expected, left=left, right=right) self.assertEqual(actual, expected, '{} failed'.format(testname)) def test_op_pow_physarray(self): @@ -391,10 +427,13 @@ def test_op_pow_physarray(self): func = functions.find(key, 2) actual = func(x, y)[:] expected = PhysArray(4.3 ** 2, name='(x**y)', units=Unit('m') ** 2) - print_test_message(testname, actual=actual, expected=expected, x=x, y=y) + print_test_message(testname, actual=actual, + expected=expected, x=x, y=y) self.assertEqual(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed - units'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.units, expected.units, + '{} failed - units'.format(testname)) def test_func_sqrt_int(self): key = 'sqrt' @@ -403,9 +442,11 @@ def test_func_sqrt_int(self): func = functions.find(key) actual = func(indata)[:] expected = np.sqrt(indata) - print_test_message(testname, input=indata, actual=actual, expected=expected) + print_test_message(testname, input=indata, + actual=actual, expected=expected) self.assertEqual(actual, expected, '{} failed'.format(testname))\ + def test_func_sqrt_float(self): key = 'sqrt' indata = 4.0 @@ -414,7 +455,8 @@ def test_func_sqrt_float(self): fobj = func(indata) actual = fobj[:] expected = np.sqrt(indata) - print_test_message(testname, input=indata, actual=actual, expected=expected) + print_test_message(testname, input=indata, + actual=actual, expected=expected) self.assertEqual(actual, expected, '{} failed'.format(testname)) def test_func_sqrt_physarray(self): @@ -424,50 +466,70 @@ def test_func_sqrt_physarray(self): func = functions.find(key) actual = func(indata)[:] expected = PhysArray([3.0, 4.0, 2.0], name='sqrt(x)', units='m') - print_test_message(testname, indata=indata, actual=actual, expected=expected) - np.testing.assert_array_equal(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed - units'.format(testname)) + print_test_message(testname, indata=indata, + actual=actual, expected=expected) + np.testing.assert_array_equal( + actual, expected, '{} failed - data'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.units, expected.units, + '{} failed - units'.format(testname)) def test_func_mean_ndarray(self): key = 'mean' - indata = PhysArray([1.0, 2.0, 3.0], name='x', units='m', dimensions=('t',)) + indata = PhysArray([1.0, 2.0, 3.0], name='x', + units='m', dimensions=('t',)) testname = '{}({})'.format(key, indata) func = functions.find(key) fobj = func(indata, 't') actual = fobj[:] expected = PhysArray(2.0, name='mean(x, dims=[t])', units='m') - print_test_message(testname, indata=indata, actual=actual, expected=expected) - np.testing.assert_array_equal(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed - units'.format(testname)) - + print_test_message(testname, indata=indata, + actual=actual, expected=expected) + np.testing.assert_array_equal( + actual, expected, '{} failed - data'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.units, expected.units, + '{} failed - units'.format(testname)) + def test_func_mean_physarray(self): key = 'mean' - indata = PhysArray([1.0, 2.0, 3.0], mask=[False, False, True], name='x', units='m', dimensions=('t',)) + indata = PhysArray([1.0, 2.0, 3.0], mask=[ + False, False, True], name='x', units='m', dimensions=('t',)) testname = '{}({})'.format(key, indata) func = functions.find(key) fobj = func(indata, 't') actual = fobj[:] expected = PhysArray(1.5, name='mean(x, dims=[t])', units='m') - print_test_message(testname, indata=indata, actual=actual, expected=expected) - np.testing.assert_array_equal(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed - units'.format(testname)) + print_test_message(testname, indata=indata, + actual=actual, expected=expected) + np.testing.assert_array_equal( + actual, expected, '{} failed - data'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.units, expected.units, + '{} failed - units'.format(testname)) def test_func_mean_physarray_2d(self): key = 'mean' - indata = PhysArray([[1.0, 2.0], [3.0, 4.0]], mask=[[False, False], [True, False]], name='x', units='m', dimensions=('t','u')) + indata = PhysArray([[1.0, 2.0], [3.0, 4.0]], mask=[[False, False], [ + True, False]], name='x', units='m', dimensions=('t', 'u')) testname = '{}({})'.format(key, indata) func = functions.find(key) fobj = func(indata, 't') actual = fobj[:] - expected = PhysArray([1.0, 3.0], name='mean(x, dims=[t])', units='m', dimensions=('u',)) - print_test_message(testname, indata=indata, actual=actual, expected=expected) - np.testing.assert_array_equal(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed - units'.format(testname)) - + expected = PhysArray( + [1.0, 3.0], name='mean(x, dims=[t])', units='m', dimensions=('u',)) + print_test_message(testname, indata=indata, + actual=actual, expected=expected) + np.testing.assert_array_equal( + actual, expected, '{} failed - data'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.units, expected.units, + '{} failed - units'.format(testname)) + def test_func_sqrt_sumlike(self): key = 'sqrt' testname = '{}.sumlike_dimensions'.format(key) @@ -479,7 +541,8 @@ def test_func_sqrt_sumlike(self): def test_func_mean_sumlike(self): key = 'mean' - indata = PhysArray([1.0, 2.0, 3.0, 4.0, 5.0], name='x', units='m', dimensions=('t',)) + indata = PhysArray([1.0, 2.0, 3.0, 4.0, 5.0], + name='x', units='m', dimensions=('t',)) testname = '{}({}).sumlike_dimensions'.format(key, indata) func = functions.find(key) fobj = func(indata, 't') @@ -496,10 +559,14 @@ def test_func_up_physarray_none(self): func = functions.find(key) actual = func(indata)[:] expected = PhysArray(indata, name='up(x)', positive='up') - print_test_message(testname, indata=indata, actual=actual, expected=expected) - np.testing.assert_array_equal(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.positive, expected.positive, '{} failed - positive'.format(testname)) + print_test_message(testname, indata=indata, + actual=actual, expected=expected) + np.testing.assert_array_equal( + actual, expected, '{} failed - data'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.positive, expected.positive, + '{} failed - positive'.format(testname)) def test_func_up_physarray_up(self): key = 'up' @@ -508,11 +575,15 @@ def test_func_up_physarray_up(self): func = functions.find(key) actual = func(indata)[:] expected = PhysArray(indata, name='x', positive='up') - print_test_message(testname, indata=indata, actual=actual, expected=expected) - np.testing.assert_array_equal(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.positive, expected.positive, '{} failed - positive'.format(testname)) - + print_test_message(testname, indata=indata, + actual=actual, expected=expected) + np.testing.assert_array_equal( + actual, expected, '{} failed - data'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.positive, expected.positive, + '{} failed - positive'.format(testname)) + def test_func_up_physarray_down(self): key = 'up' indata = PhysArray(2.5, name='x', positive='down') @@ -520,10 +591,14 @@ def test_func_up_physarray_down(self): func = functions.find(key) actual = func(indata)[:] expected = PhysArray(-2.5, name='up(x)', positive='up') - print_test_message(testname, indata=indata, actual=actual, expected=expected) - np.testing.assert_array_equal(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.positive, expected.positive, '{} failed - positive'.format(testname)) + print_test_message(testname, indata=indata, + actual=actual, expected=expected) + np.testing.assert_array_equal( + actual, expected, '{} failed - data'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.positive, expected.positive, + '{} failed - positive'.format(testname)) def test_func_down_physarray_none(self): key = 'down' @@ -532,10 +607,14 @@ def test_func_down_physarray_none(self): func = functions.find(key) actual = func(indata)[:] expected = PhysArray(indata, name='down(x)', positive='down') - print_test_message(testname, indata=indata, actual=actual, expected=expected) - np.testing.assert_array_equal(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.positive, expected.positive, '{} failed - positive'.format(testname)) + print_test_message(testname, indata=indata, + actual=actual, expected=expected) + np.testing.assert_array_equal( + actual, expected, '{} failed - data'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.positive, expected.positive, + '{} failed - positive'.format(testname)) def test_func_down_physarray_down(self): key = 'down' @@ -544,10 +623,14 @@ def test_func_down_physarray_down(self): func = functions.find(key) actual = func(indata)[:] expected = PhysArray(2.5, name='x', positive='down') - print_test_message(testname, indata=indata, actual=actual, expected=expected) - np.testing.assert_array_equal(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.positive, expected.positive, '{} failed - positive'.format(testname)) + print_test_message(testname, indata=indata, + actual=actual, expected=expected) + np.testing.assert_array_equal( + actual, expected, '{} failed - data'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.positive, expected.positive, + '{} failed - positive'.format(testname)) def test_func_down_physarray_up(self): key = 'down' @@ -556,10 +639,14 @@ def test_func_down_physarray_up(self): func = functions.find(key) actual = func(indata)[:] expected = PhysArray(-2.5, name='down(x)', positive='down') - print_test_message(testname, indata=indata, actual=actual, expected=expected) - np.testing.assert_array_equal(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.positive, expected.positive, '{} failed - positive'.format(testname)) + print_test_message(testname, indata=indata, + actual=actual, expected=expected) + np.testing.assert_array_equal( + actual, expected, '{} failed - data'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.positive, expected.positive, + '{} failed - positive'.format(testname)) def test_func_chunits(self): key = 'chunits' @@ -569,72 +656,116 @@ def test_func_chunits(self): func = functions.find(key) actual = func(indata, units=new_units)[:] expected = PhysArray(2.5, name='chunits(x, units=kg)', units=new_units) - print_test_message(testname, indata=indata, actual=actual, expected=expected) - np.testing.assert_array_equal(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed - units'.format(testname)) + print_test_message(testname, indata=indata, + actual=actual, expected=expected) + np.testing.assert_array_equal( + actual, expected, '{} failed - data'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.units, expected.units, + '{} failed - units'.format(testname)) def test_func_chunits_calendar(self): key = 'chunits' - indata = PhysArray(2.5, name='t', units=Unit('days since 1850-01-01', calendar='noleap')) + indata = PhysArray(2.5, name='t', units=Unit( + 'days since 1850-01-01', calendar='noleap')) new_cal = 'gregorian' testname = '{}({}, calendar={})'.format(key, indata, new_cal) func = functions.find(key) actual = func(indata, calendar=new_cal)[:] expected = PhysArray(2.5, name='chunits(t, units=days since 1850-01-01|gregorian)', units=Unit('days since 1850-01-01', calendar=new_cal)) - print_test_message(testname, indata=indata, actual=actual, expected=expected) - np.testing.assert_array_equal(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed - units'.format(testname)) - + print_test_message(testname, indata=indata, + actual=actual, expected=expected) + np.testing.assert_array_equal( + actual, expected, '{} failed - data'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.units, expected.units, + '{} failed - units'.format(testname)) + def test_func_chunits_refdate(self): key = 'chunits' - indata = PhysArray(2.5, name='t', units=Unit('days since 1850-01-01', calendar='noleap')) + indata = PhysArray(2.5, name='t', units=Unit( + 'days since 1850-01-01', calendar='365_day')) new_ref = '0001-01-01' testname = '{}({}, refdate={})'.format(key, indata, new_ref) func = functions.find(key) actual = func(indata, refdate=new_ref)[:] - expected = PhysArray(2.5, name='chunits(t, units=days since {}|noleap)'.format(new_ref), + expected = PhysArray(2.5, name='chunits(t, units=days since {}|365_day)'.format(new_ref), units=Unit('days since {}'.format(new_ref), calendar='noleap')) - print_test_message(testname, indata=indata, actual=actual, expected=expected) - np.testing.assert_array_equal(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed - units'.format(testname)) - + print_test_message(testname, indata=indata, + actual=actual, expected=expected) + np.testing.assert_array_equal( + actual, expected, '{} failed - data'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.units, expected.units, + '{} failed - units'.format(testname)) + def test_func_chunits_refdate_calendar(self): key = 'chunits' - indata = PhysArray(2.5, name='t', units=Unit('days since 1850-01-01', calendar='noleap')) + indata = PhysArray(2.5, name='t', units=Unit( + 'days since 1850-01-01', calendar='365_day')) new_ref = '0001-01-01' new_cal = 'gregorian' - testname = '{}({}, refdate={}, calendar={})'.format(key, indata, new_ref, new_cal) + testname = '{}({}, refdate={}, calendar={})'.format( + key, indata, new_ref, new_cal) func = functions.find(key) actual = func(indata, refdate=new_ref, calendar=new_cal)[:] expected = PhysArray(2.5, name='chunits(t, units=days since {}|{})'.format(new_ref, new_cal), units=Unit('days since {}'.format(new_ref), calendar=new_cal)) - print_test_message(testname, indata=indata, actual=actual, expected=expected) - np.testing.assert_array_equal(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed - units'.format(testname)) - + print_test_message(testname, indata=indata, + actual=actual, expected=expected) + np.testing.assert_array_equal( + actual, expected, '{} failed - data'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.units, expected.units, + '{} failed - units'.format(testname)) + def test_func_limit(self): key = 'limit' - indata = PhysArray([2.5, 7.3, 8.2, 1.4], name='x', units='m', dimensions=('t',)) + indata = PhysArray([2.5, 7.3, 8.2, 1.4], name='x', + units='m', dimensions=('t',)) below_val = 3.0 above_val = 7.5 - testname = '{}({}, above={}, below={})'.format(key, indata, above_val, below_val) + testname = '{}({}, above={}, below={})'.format( + key, indata, above_val, below_val) func = functions.find(key) actual = func(indata, above=above_val, below=below_val)[:] - expected = PhysArray([3.0, 7.3, 7.5, 3.0], name=testname, units='m', dimensions=('t',)) - print_test_message(testname, indata=indata, actual=actual, expected=expected) - np.testing.assert_array_equal(actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, '{} failed - name'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed - units'.format(testname)) - - -#=============================================================================== + expected = PhysArray([3.0, 7.3, 7.5, 3.0], + name=testname, units='m', dimensions=('t',)) + print_test_message(testname, indata=indata, + actual=actual, expected=expected) + np.testing.assert_array_equal( + actual, expected, '{} failed - data'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.units, expected.units, + '{} failed - units'.format(testname)) + + def test_func_rmunits(self): + key = 'rmunits' + indata = PhysArray(2.5, name='x', units='m') + testname = '{}({})'.format(key, indata) + func = functions.find(key) + actual = func(indata)[:] + expected = PhysArray(2.5, name='rmunits(x)') + print_test_message(testname, indata=indata, + actual=actual, expected=expected) + np.testing.assert_array_equal( + actual, expected, '{} failed - data'.format(testname)) + self.assertEqual(actual.name, expected.name, + '{} failed - name'.format(testname)) + self.assertEqual(actual.units, expected.units, + '{} failed - units'.format(testname)) + + + +#========================================================================= # Command-Line Operation -#=============================================================================== +#========================================================================= if __name__ == "__main__": # import sys;sys.argv = ['', 'Test.testName'] unittest.main() From 24ced43a832493d62e494c4168124d9f50a7065b Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Tue, 25 Sep 2018 09:53:59 -0600 Subject: [PATCH 07/23] Adding remove units function (drops units, changes to 1) --- source/pyconform/functions.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/source/pyconform/functions.py b/source/pyconform/functions.py index 19eedcc9..f5db47d3 100644 --- a/source/pyconform/functions.py +++ b/source/pyconform/functions.py @@ -6,7 +6,7 @@ """ from abc import ABCMeta, abstractmethod -from pyconform.physarray import PhysArray, UnitsError +from pyconform.physarray import PhysArray, UnitsError, getname from numpy.ma import sqrt, where from cf_units import Unit import numpy as np @@ -351,9 +351,9 @@ def __getitem__(self, index): dimensions = self.arguments[1:] indims = [] for d in dimensions: - #print d, 'in', data.dimensions, '?' + # print d, 'in', data.dimensions, '?' if d in data.dimensions: - #print 'will append ', data.dimensions.index(d) + # print 'will append ', data.dimensions.index(d) indims.append(data.dimensions.index(d)) return np.sum(data, indims[0]) @@ -539,3 +539,19 @@ def __getitem__(self, index): new_name = 'limit({}{}{})'.format(data.name, above_str, below_str) return PhysArray(data, name=new_name) + + +#========================================================================= +# RemoveUnitsFunction +#========================================================================= +class RemoveUnitsFunction(Function): + key = 'rmunits' + + def __init__(self, data): + super(RemoveUnitsFunction, self).__init__(data) + + def __getitem__(self, index): + data = (self.arguments[0] if is_constant(self.arguments[0]) + else self.arguments[0][index]) + new_name = 'rmunits({!s})'.format(getname(data)) + return PhysArray(data, name=new_name, units=1) From ec5285a6e033c163465f73453f39d6da4cd032db Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Tue, 25 Sep 2018 14:17:56 -0600 Subject: [PATCH 08/23] Fix unit tests --- source/test/flownodesTests.py | 279 +++++++++------------ source/test/functionsTests.py | 75 +++--- source/test/physarrayTests.py | 447 +++++++++++++++++----------------- 3 files changed, 376 insertions(+), 425 deletions(-) diff --git a/source/test/flownodesTests.py b/source/test/flownodesTests.py index f9cfcb37..46a68b11 100644 --- a/source/test/flownodesTests.py +++ b/source/test/flownodesTests.py @@ -21,10 +21,34 @@ import netCDF4 +#======================================================================================================================= +# BaseTests +#======================================================================================================================= +class BaseTests(unittest.TestCase): + + def assertPhysArraysEqual(self, left, right, testname='Test', decimal=0): + if type(left) != type(right): + self.fail('{} failed - type') + elif isinstance(left, PhysArray): + ldata = numpy.ma.asarray(left) + rdata = numpy.ma.asarray(right) + if decimal == 0: + numpy.testing.assert_array_equal(ldata, rdata, '{} failed - data'.format(testname)) + else: + numpy.testing.assert_array_almost_equal(left, right, decimal, '{} failed - data'.format(testname)) + self.assertEqual(left.dtype, right.dtype, '{} failed - dtype'.format(testname)) + self.assertEqual(left.name, right.name, '{} failed - name'.format(testname)) + self.assertEqual(left.units, right.units, '{} failed - units'.format(testname)) + self.assertEqual(left.dimensions, right.dimensions, '{} failed - dimensions'.format(testname)) + self.assertEqual(left.positive, right.positive, '{} failed - positive'.format(testname)) + else: + self.assertEqual(left, right, '{} failed') + + #======================================================================================================================= # FlowNodeTests #======================================================================================================================= -class FlowNodeTests(unittest.TestCase): +class FlowNodeTests(BaseTests): """ Unit tests for the flownodes.FlowNode class """ @@ -69,7 +93,7 @@ def test_inputs(self): #======================================================================================================================= # DataNodeTests #======================================================================================================================= -class DataNodeTests(unittest.TestCase): +class DataNodeTests(BaseTests): """ Unit tests for the flownodes.DataNode class """ @@ -81,9 +105,7 @@ def test_getitem_all(self): actual = N[:] expected = indata print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_getitem_slice(self): indata = PhysArray(numpy.arange(10), units='m', dimensions=('x',)) @@ -92,9 +114,7 @@ def test_getitem_slice(self): actual = N[:5] expected = PhysArray(indata[:5], units='m', dimensions=('x',)) print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_getitem_dict(self): indata = PhysArray(numpy.arange(10), name=0, units='m', dimensions=('x',)) @@ -104,15 +124,13 @@ def test_getitem_dict(self): actual = N[indict] expected = PhysArray(indata[indict['x']], units='m', dimensions=('x',)) print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) #======================================================================================================================= # ReadNodeTests #======================================================================================================================= -class ReadNodeTests(unittest.TestCase): +class ReadNodeTests(BaseTests): """ Unit tests for the flownodes.ReadNode class """ @@ -127,13 +145,12 @@ def setUp(self): 'y': PhysArray(numpy.arange(self.shape['y'], dtype='f'), units='km', dimensions=('y',), name='x'), 'v': PhysArray(numpy.arange(self.shape['x'] * self.shape['y'], - dtype='d').reshape(self.shape['x'], - self.shape['y']), + dtype='d').reshape(self.shape['x'], self.shape['y']), units='K', dimensions=self.dimensions, name='v')} - dimdescs = {d:DimensionDesc(d, s) for d, s in self.shape.iteritems()} - vardescs = {vn:VariableDesc(vn, datatype=vd.dtype, attributes={'units': str(vd.units)}, - dimensions=[dimdescs[dd] for dd in vd.dimensions]) + dimdescs = {d: DimensionDesc(d, s) for d, s in self.shape.iteritems()} + vardescs = {vn: VariableDesc(vn, datatype=vd.dtype, attributes={'units': str(vd.units)}, + dimensions=[dimdescs[dd] for dd in vd.dimensions]) for vn, vd in self.vardata.iteritems()} self.filedesc = FileDesc(self.filename, variables=vardescs.values()) self.vardesc = self.filedesc.variables[self.varname] @@ -143,7 +160,7 @@ def setUp(self): ncfile.createDimension(d, self.shape[d]) for v in self.vardata: - ncv = ncfile.createVariable(v, 'f', self.vardata[v].dimensions) + ncv = ncfile.createVariable(v, self.vardata[v].dtype, self.vardata[v].dimensions) ncv.setncatts({'units': str(self.vardata[v].units)}) ncv[:] = self.vardata[v] @@ -157,9 +174,8 @@ def test_getitem_all(self): actual = N[:] expected = self.vardata[self.varname] print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + print actual.dtype, expected.dtype + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_getitem_slice(self): testname = 'ReadNode.__getitem__(:2)' @@ -167,21 +183,18 @@ def test_getitem_slice(self): actual = N[:2] expected = self.vardata[self.varname][:2] print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_getitem_none(self): testname = 'ReadNode.__getitem__(None)' N = ReadNode(self.vardesc) actual = N[None] expected = PhysArray(numpy.zeros((0,) * len(self.shape), dtype='d'), - units=self.vardata[self.varname].units, - dimensions=self.vardata[self.varname].dimensions) + units=self.vardata[self.varname].units, + dimensions=self.vardata[self.varname].dimensions, + name=self.varname) print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_getitem_tuple(self): intuple = (3, slice(2, 4)) @@ -190,9 +203,7 @@ def test_getitem_tuple(self): actual = N[intuple] expected = PhysArray(self.vardata[self.varname][intuple], dimensions=('y',)) print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_getitem_dict(self): indict = {'a': 4, 'x': slice(1, 5, 2)} @@ -201,9 +212,7 @@ def test_getitem_dict(self): actual = N[indict] expected = self.vardata[self.varname][slice(1, 5, 2)] print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_getitem_dict_2(self): indict = {'a': 4, 'y': slice(1, 5, 2)} @@ -212,15 +221,13 @@ def test_getitem_dict_2(self): actual = N[indict] expected = self.vardata[self.varname][:, slice(1, 5, 2)] print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) #======================================================================================================================= # EvalNodeTests #======================================================================================================================= -class EvalNodeTests(unittest.TestCase): +class EvalNodeTests(BaseTests): """ Unit tests for the flownodes.EvalNode class """ @@ -232,9 +239,7 @@ def test_getitem_all(self): actual = N[:] expected = indata print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_getitem_none(self): indata = PhysArray(range(10), units='m', dimensions=('x',)) @@ -242,11 +247,9 @@ def test_getitem_none(self): N = EvalNode(0, lambda x: x, indata) actual = N[None] expected = PhysArray(numpy.zeros((0,), dtype=indata.dtype), - units='m', dimensions=('x',)) + units='m', dimensions=('x',), name='[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]') print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_getitem_slice(self): indata = PhysArray(range(10), units='m', dimensions=('x',)) @@ -255,9 +258,7 @@ def test_getitem_slice(self): actual = N[:5] expected = indata[:5] print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_getitem_dict(self): indata = PhysArray(range(10), units='m', dimensions=('x',)) @@ -266,9 +267,7 @@ def test_getitem_dict(self): actual = N[{'x': slice(5, None), 'y': 6}] expected = indata[slice(5, None)] print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_getitem_add(self): d1 = PhysArray(numpy.arange(1, 5), name='X1', units='m', dimensions=('x',)) @@ -280,9 +279,7 @@ def test_getitem_add(self): actual = N3[:] expected = d1 + d2 print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_getitem_add_slice(self): d1 = PhysArray(numpy.arange(1, 5), name='X1', units='m', dimensions=('x',)) @@ -294,9 +291,7 @@ def test_getitem_add_slice(self): actual = N3[:2] expected = d1[:2] + d2[:2] print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_getitem_add_none(self): d1 = PhysArray(numpy.arange(1, 5), name='X1', units='m', dimensions=('x',)) @@ -306,18 +301,18 @@ def test_getitem_add_none(self): N3 = EvalNode(3, find_operator('+', numargs=2), N1, N2) testname = 'EvalNode.__getitem__(None)' actual = N3[None] - expected = PhysArray([], units='m', dimensions=('x',)) + expected = PhysArray([], dtype='int64', units='m', dimensions=('x',), name='(X1+X2)') print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_sumlike_dimensions(self): class myfunc(Function): key = 'myfunc' + def __init__(self, d, *dims): super(myfunc, self).__init__(d, *dims) self.add_sumlike_dimensions(*dims) + def __getitem__(self, _): return self.arguments[0] d = PhysArray(numpy.arange(1, 5), name='d', units='m', dimensions=('x',)) @@ -332,12 +327,12 @@ def __getitem__(self, _): expected = set(['x']) print_test_message(testname, actual=actual, expected=expected) self.assertEqual(actual, expected, '{} failed'.format(testname)) - + #======================================================================================================================= # MapNodeTests #======================================================================================================================= -class MapNodeTests(unittest.TestCase): +class MapNodeTests(BaseTests): """ Unit tests for the flownodes.MapNode class """ @@ -350,31 +345,26 @@ def test_getitem_all(self): testname = 'MapNode.__getitem__(:)' N = MapNode(0, self.indata, dmap={'x': 'y'}) actual = N[:] - expected = PhysArray(self.indata[:], dimensions=('y',)) + expected = PhysArray(self.indata[:], dimensions=('y',), name='map(0, from=[x], to=[y])') print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_getitem_slice(self): testname = 'MapNode.__getitem__(:3)' N = MapNode(0, self.indata, dmap={'x': 'y'}) actual = N[:3] - expected = PhysArray(self.indata[:3], dimensions=('y',)) + expected = PhysArray(self.indata[:3], dimensions=('y',), name='map(0, from=[x], to=[y])', + dtype=actual.dtype) print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_getitem_none(self): testname = 'MapNode.__getitem__(None)' N = MapNode(0, self.indata, dmap={'x': 'y'}) actual = N[None] - expected = PhysArray(numpy.arange(0), units='km', dimensions=('y',)) + expected = PhysArray(numpy.arange(0), units='km', dimensions=('y',), name='map(0, from=[x], to=[y])') print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_getitem_slice_no_dmap(self): testname = 'MapNode(dmap={}, dimensions=indims).__getitem__(:3)' @@ -382,15 +372,13 @@ def test_getitem_slice_no_dmap(self): actual = N[:3] expected = PhysArray(self.indata[:3], dimensions=('x',)) print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) #======================================================================================================================= # ValidateNodeTests #======================================================================================================================= -class ValidateNodeTests(unittest.TestCase): +class ValidateNodeTests(BaseTests): """ Unit tests for the flownodes.ValidateNode class """ @@ -403,9 +391,7 @@ def test_nothing(self): actual = N1[:] expected = N0[:] print_test_message(testname, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_units_ok(self): N0 = DataNode(PhysArray(numpy.arange(10), name='x', units='m', dimensions=('x',))) @@ -415,21 +401,18 @@ def test_units_ok(self): actual = N1[:] expected = N0[:] print_test_message(testname, indata=indata, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_time_units_ok(self): N0 = DataNode(PhysArray(numpy.arange(10), name='x', units='days since 2000-01-01 00:00:00', dimensions=('x',))) - indata = VariableDesc('validate(x)', dimensions=(DimensionDesc('x'),), attributes={'units': 'days since 2000-01-01 00:00:00', 'calendar': 'gregorian'}) + indata = VariableDesc('validate(x)', dimensions=(DimensionDesc('x'),), attributes={ + 'units': 'days since 2000-01-01 00:00:00', 'calendar': 'gregorian'}) testname = 'OK: ValidateNode({!r}).__getitem__(:)'.format(indata) N1 = ValidateNode(indata, N0) actual = N1[:] expected = N0[:] print_test_message(testname, indata=indata, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_dimensions_ok(self): N0 = DataNode(PhysArray(numpy.arange(10), name='x', units='m', dimensions=('x',))) @@ -439,9 +422,7 @@ def test_dimensions_ok(self): actual = N1[:] expected = N0[:] print_test_message(testname, indata=indata, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_min_ok(self): N0 = DataNode(PhysArray(numpy.arange(10), name='x', units='m', dimensions=('x',))) @@ -451,9 +432,7 @@ def test_min_ok(self): actual = N1[:] expected = N0[:] print_test_message(testname, indata=indata, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_max_ok(self): N0 = DataNode(PhysArray(numpy.arange(10), name='x', units='m', dimensions=('x',))) @@ -463,21 +442,18 @@ def test_max_ok(self): actual = N1[:] expected = N0[:] print_test_message(testname, indata=indata, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_minmax_getitem_none(self): N0 = DataNode(PhysArray(numpy.arange(10), name='x', units='m', dimensions=('x',))) - indata = VariableDesc('validate(x)', dimensions=(DimensionDesc('x'),), attributes={'valid_min': 0, 'valid_max': 2}) + indata = VariableDesc('validate(x)', dimensions=(DimensionDesc('x'),), + attributes={'valid_min': 0, 'valid_max': 2}) testname = 'OK: ValidateNode({!r}).__getitem__(:)'.format(indata) N1 = ValidateNode(indata, N0) actual = N1[None] expected = N0[None] print_test_message(testname, indata=indata, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_min_mean_abs_ok(self): N0 = DataNode(PhysArray(numpy.arange(-5, 10), name='x', units='m', dimensions=('x',))) @@ -487,9 +463,7 @@ def test_min_mean_abs_ok(self): actual = N1[:] expected = N0[:] print_test_message(testname, indata=indata, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_max_mean_abs_ok(self): N0 = DataNode(PhysArray(numpy.arange(-5, 10), name='x', units='m', dimensions=('x',))) @@ -499,9 +473,7 @@ def test_max_mean_abs_ok(self): actual = N1[:] expected = N0[:] print_test_message(testname, indata=indata, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_units_convert(self): N0 = DataNode(PhysArray(numpy.arange(10.0), name='x', units='m', dimensions=('x',))) @@ -514,9 +486,7 @@ def test_units_convert(self): expected.units = Unit('km') expected.mask = False print_test_message(testname, indata=indata, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_units_inherit(self): N0 = DataNode(PhysArray(numpy.arange(10.0), name='x', units='m', dimensions=('x',))) @@ -526,12 +496,10 @@ def test_units_inherit(self): actual = N1[:] expected = N0[:] print_test_message(testname, indata=indata, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_dimensions_transpose(self): - N0 = DataNode(PhysArray([[1.,2.],[3.,4.]], name='a', units='m', dimensions=('x', 'y'))) + N0 = DataNode(PhysArray([[1., 2.], [3., 4.]], name='a', units='m', dimensions=('x', 'y'))) indata = VariableDesc('validate(a)', dimensions=(DimensionDesc('y'), DimensionDesc('x'))) testname = 'TRANSPOSE: ValidateNode({!r}).__getitem__(:)'.format(indata) N1 = ValidateNode(indata, N0) @@ -542,15 +510,14 @@ def test_dimensions_transpose(self): def test_time_units_convert(self): N0 = DataNode(PhysArray(numpy.arange(10), name='x', units='days since 2000-01-01 00:00:00', dimensions=('x',))) - indata = VariableDesc('validate(x)', dimensions=(DimensionDesc('x'),), attributes={'units': 'hours since 2000-01-01 00:00:00', 'calendar': 'gregorian'}) + indata = VariableDesc('validate(x)', dimensions=(DimensionDesc('x'),), attributes={ + 'units': 'hours since 2000-01-01 00:00:00', 'calendar': 'gregorian'}) testname = 'CONVERT: ValidateNode({!r}).__getitem__(:)'.format(indata) N1 = ValidateNode(indata, N0) actual = N1[:] expected = N0[:].convert(Unit('hours since 2000-01-01 00:00:00')) print_test_message(testname, indata=indata, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_time_units_inherit(self): N0 = DataNode(PhysArray(numpy.arange(10), name='x', units='days since 2000-01-01 00:00:00', dimensions=('x',))) @@ -560,9 +527,7 @@ def test_time_units_inherit(self): actual = N1[:] expected = N0[:] print_test_message(testname, indata=indata, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_time_units_inherit_refdatetime(self): N0 = DataNode(PhysArray(numpy.arange(10), name='x', units='days since 2000-01-01 00:00:00', dimensions=('x',))) @@ -572,26 +537,24 @@ def test_time_units_inherit_refdatetime(self): actual = N1[:] expected = N0[:].convert('hours since 2000-01-01 00:00:00') print_test_message(testname, indata=indata, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_time_units_convert_nocal(self): - N0 = DataNode(PhysArray(numpy.arange(10), name='x', dimensions=('x',), + N0 = DataNode(PhysArray(numpy.arange(10), name='x', dimensions=('x',), units=Unit('days since 2000-01-01 00:00:00', calendar='noleap'))) - indata = VariableDesc('validate(x)', dimensions=(DimensionDesc('x'),), attributes={'units': 'hours since 2000-01-01 00:00:00'}) + indata = VariableDesc('validate(x)', dimensions=(DimensionDesc('x'),), + attributes={'units': 'hours since 2000-01-01 00:00:00'}) testname = 'CONVERT: ValidateNode({!r}).__getitem__(:)'.format(indata) N1 = ValidateNode(indata, N0) actual = N1[:] expected = N0[:].convert(Unit('hours since 2000-01-01 00:00:00', calendar='noleap')) print_test_message(testname, indata=indata, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) - + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) + def test_time_units_error_calendar(self): N0 = DataNode(PhysArray(numpy.arange(10), name='x', units='days since 2000-01-01 00:00:00', dimensions=('x',))) - indata = VariableDesc('validate(x)', dimensions=(DimensionDesc('x'),), attributes={'units': 'days since 2000-01-01 00:00:00', 'calendar': 'noleap'}) + indata = VariableDesc('validate(x)', dimensions=(DimensionDesc('x'),), attributes={ + 'units': 'days since 2000-01-01 00:00:00', 'calendar': 'noleap'}) testname = 'FAIL: ValidateNode({!r}).__getitem__(:)'.format(indata) N1 = ValidateNode(indata, N0) print_test_message(testname, indata=indata, expected=UnitsError) @@ -614,9 +577,7 @@ def test_min_warn(self): actual = N1[:] expected = N0[:] print_test_message(testname, indata=indata, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_max_warn(self): N0 = DataNode(PhysArray(numpy.arange(10), name='x', units='m', dimensions=('x',))) @@ -626,9 +587,7 @@ def test_max_warn(self): actual = N1[:] expected = N0[:] print_test_message(testname, indata=indata, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_min_mean_abs_warn(self): N0 = DataNode(PhysArray(numpy.arange(-5, 10), name='x', units='m', dimensions=('x',))) @@ -638,9 +597,7 @@ def test_min_mean_abs_warn(self): actual = N1[:] expected = N0[:] print_test_message(testname, indata=indata, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_max_mean_abs_warn(self): N0 = DataNode(PhysArray(numpy.arange(-5, 10), name='x', units='m', dimensions=('x',))) @@ -650,15 +607,13 @@ def test_max_mean_abs_warn(self): actual = N1[:] expected = N0[:] print_test_message(testname, indata=indata, actual=actual, expected=expected) - numpy.testing.assert_array_equal(actual, expected, '{} failed'.format(testname)) - self.assertEqual(actual.units, expected.units, '{} failed'.format(testname)) - self.assertEqual(actual.dimensions, expected.dimensions, '{} failed'.format(testname)) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) #======================================================================================================================= # WriteNodeTests #======================================================================================================================= -class WriteNodeTests(unittest.TestCase): +class WriteNodeTests(BaseTests): """ Unit tests for the flownodes.WriteNode class """ @@ -666,37 +621,37 @@ class WriteNodeTests(unittest.TestCase): def setUp(self): NX = 15 X0 = -5 - xdata = PhysArray(numpy.arange(X0, X0+NX, dtype='d'), name='X', units='m', dimensions=('x',)) - + xdata = PhysArray(numpy.arange(X0, X0 + NX, dtype='d'), name='X', units='m', dimensions=('x',)) + NY = 8 Y0 = 0 - ydata = PhysArray(numpy.arange(Y0, Y0+NY, dtype='d'), name='Y', units='m', dimensions=('y',)) - + ydata = PhysArray(numpy.arange(Y0, Y0 + NY, dtype='d'), name='Y', units='m', dimensions=('y',)) + NT = 3 tunits = Unit('days since 2000-01-01', calendar='noleap') tdata = PhysArray(numpy.arange(0, NT, dtype='d'), name='T', units=tunits, dimensions=('t',)) - - vdata = PhysArray(numpy.arange(0, NX*NY*NT, dtype='f').reshape(NX,NY,NT), name='V', units='K', + + vdata = PhysArray(numpy.arange(0, NX * NY * NT, dtype='f').reshape(NX, NY, NT), name='V', units='K', dimensions=('x', 'y', 't')) self.data = {'X': xdata, 'Y': ydata, 'T': tdata, 'V': vdata} self.atts = {'X': {'xa1': 'x attribute 1', 'xa2': 'x attribute 2', 'axis': 'X', 'units': str(xdata.units)}, - 'Y': {'ya1': 'y attribute 1', 'ya2': 'y attribute 2', 'axis': 'Y', + 'Y': {'ya1': 'y attribute 1', 'ya2': 'y attribute 2', 'axis': 'Y', 'direction': 'decreasing', 'units': str(ydata.units)}, 'T': {'axis': 'T', 'ta1': 'time attribute', 'units': str(tdata.units), 'calendar': tdata.units.calendar}, 'V': {'va1': 'v attribute 1', 'va2': 'v attribute 2', 'units': str(vdata.units)}} - dimdescs = {n:DimensionDesc(n, s) for x in self.data.itervalues() for n, s in zip(x.dimensions, x.shape)} - vardescs = {n:VariableDesc(n, datatype=self.data[n].dtype, attributes=self.atts[n], - dimensions=[dimdescs[d] for d in self.data[n].dimensions]) for n in self.data} + dimdescs = {n: DimensionDesc(n, s) for x in self.data.itervalues() for n, s in zip(x.dimensions, x.shape)} + vardescs = {n: VariableDesc(n, datatype=self.data[n].dtype, attributes=self.atts[n], + dimensions=[dimdescs[d] for d in self.data[n].dimensions]) for n in self.data} self.vardescs = vardescs - self.nodes = {n:ValidateNode(self.vardescs[n], DataNode(self.data[n])) for n in self.data} + self.nodes = {n: ValidateNode(self.vardescs[n], DataNode(self.data[n])) for n in self.data} def tearDown(self): for fname in glob('*.nc'): remove(fname) - + def test_init(self): filename = 'test.nc' testname = 'WriteNode.__init__({})'.format(filename) @@ -770,7 +725,7 @@ def test_chunk_iter_2D_reverse(self): def test_invert_dims(self): dsizes = OrderedDict([('x', 4), ('y', 5)]) - chunk = OrderedDict([('x', slice(0,2)), ('y', slice(1,3))]) + chunk = OrderedDict([('x', slice(0, 2)), ('y', slice(1, 3))]) idims = {'y'} testname = 'WriteNode._invert_dims({}, {}, idims={})'.format(dsizes, chunk, idims) actual = WriteNode._invert_dims_(dsizes, chunk, idims=idims) @@ -808,9 +763,9 @@ def test_execute_simple_autoparse(self): def test_execute_simple_autoparse_fail(self): filename = 'v.{%Y%m%d-%Y%m%d}.nc' testname = 'WriteNode({}).execute()'.format(filename) - vdescs = {n:self.vardescs[n] for n in self.vardescs if n != 'T'} + vdescs = {n: self.vardescs[n] for n in self.vardescs if n != 'T'} filedesc = FileDesc(filename, variables=vdescs.values(), attributes={'ga': 'global attribute'}) - vnodes = {n:self.nodes[n] for n in self.nodes if n != 'T'} + vnodes = {n: self.nodes[n] for n in self.nodes if n != 'T'} N = WriteNode(filedesc, inputs=vnodes.values()) N.enable_history() N.execute() @@ -819,7 +774,7 @@ def test_execute_simple_autoparse_fail(self): print_test_message(testname, actual=actual, expected=expected) self.assertEqual(actual, expected, '{} failed'.format(testname)) print_ncfile(filename) - + def test_execute_simple_nc3(self): filename = 'v_x_y_simple_nc3.nc' testname = 'WriteNode({}).execute()'.format(filename) diff --git a/source/test/functionsTests.py b/source/test/functionsTests.py index 57bb4662..942ce4be 100644 --- a/source/test/functionsTests.py +++ b/source/test/functionsTests.py @@ -187,6 +187,24 @@ class EvaluationTests(unittest.TestCase): Unit tests for evaluating functions and operators """ + def assertPhysArraysEqual(self, left, right, testname='Test', decimal=0): + if type(left) != type(right): + self.fail('{} failed - type') + elif isinstance(left, PhysArray): + ldata = np.ma.asarray(left) + rdata = np.ma.asarray(right) + if decimal == 0: + np.testing.assert_array_equal(ldata, rdata, '{} failed - data'.format(testname)) + else: + np.testing.assert_array_almost_equal(left, right, decimal, '{} failed - data'.format(testname)) + self.assertEqual(left.dtype, right.dtype, '{} failed - dtype'.format(testname)) + self.assertEqual(left.name, right.name, '{} failed - name'.format(testname)) + self.assertEqual(left.units, right.units, '{} failed - units'.format(testname)) + self.assertEqual(left.dimensions, right.dimensions, '{} failed - dimensions'.format(testname)) + self.assertEqual(left.positive, right.positive, '{} failed - positive'.format(testname)) + else: + self.assertEqual(left, right, '{} failed') + def test_op_neg_int(self): key = '-' indata = 3 @@ -466,14 +484,8 @@ def test_func_sqrt_physarray(self): func = functions.find(key) actual = func(indata)[:] expected = PhysArray([3.0, 4.0, 2.0], name='sqrt(x)', units='m') - print_test_message(testname, indata=indata, - actual=actual, expected=expected) - np.testing.assert_array_equal( - actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, - '{} failed - name'.format(testname)) - self.assertEqual(actual.units, expected.units, - '{} failed - units'.format(testname)) + print_test_message(testname, indata=indata, actual=actual, expected=expected) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_func_mean_ndarray(self): key = 'mean' @@ -513,22 +525,15 @@ def test_func_mean_physarray(self): def test_func_mean_physarray_2d(self): key = 'mean' - indata = PhysArray([[1.0, 2.0], [3.0, 4.0]], mask=[[False, False], [ - True, False]], name='x', units='m', dimensions=('t', 'u')) + indata = PhysArray([[1.0, 2.0], [3.0, 4.0]], mask=[[False, False], [True, False]], + name='x', units='m', dimensions=('t', 'u')) testname = '{}({})'.format(key, indata) func = functions.find(key) fobj = func(indata, 't') actual = fobj[:] - expected = PhysArray( - [1.0, 3.0], name='mean(x, dims=[t])', units='m', dimensions=('u',)) - print_test_message(testname, indata=indata, - actual=actual, expected=expected) - np.testing.assert_array_equal( - actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, - '{} failed - name'.format(testname)) - self.assertEqual(actual.units, expected.units, - '{} failed - units'.format(testname)) + expected = PhysArray([1.0, 3.0], name='mean(x, dims=[t])', units='m', dimensions=('u',)) + print_test_message(testname, indata=indata, actual=actual, expected=expected) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_func_sqrt_sumlike(self): key = 'sqrt' @@ -726,24 +731,15 @@ def test_func_chunits_refdate_calendar(self): def test_func_limit(self): key = 'limit' - indata = PhysArray([2.5, 7.3, 8.2, 1.4], name='x', - units='m', dimensions=('t',)) + indata = PhysArray([2.5, 7.3, 8.2, 1.4], name='x', units='m', dimensions=('t',)) below_val = 3.0 above_val = 7.5 - testname = '{}({}, above={}, below={})'.format( - key, indata, above_val, below_val) + testname = '{}({}, above={}, below={})'.format(key, indata, above_val, below_val) func = functions.find(key) actual = func(indata, above=above_val, below=below_val)[:] - expected = PhysArray([3.0, 7.3, 7.5, 3.0], - name=testname, units='m', dimensions=('t',)) - print_test_message(testname, indata=indata, - actual=actual, expected=expected) - np.testing.assert_array_equal( - actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, - '{} failed - name'.format(testname)) - self.assertEqual(actual.units, expected.units, - '{} failed - units'.format(testname)) + expected = PhysArray([3.0, 7.3, 7.5, 3.0], name=testname, units='m', dimensions=('t',)) + print_test_message(testname, indata=indata, actual=actual, expected=expected) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) def test_func_rmunits(self): key = 'rmunits' @@ -752,15 +748,8 @@ def test_func_rmunits(self): func = functions.find(key) actual = func(indata)[:] expected = PhysArray(2.5, name='rmunits(x)') - print_test_message(testname, indata=indata, - actual=actual, expected=expected) - np.testing.assert_array_equal( - actual, expected, '{} failed - data'.format(testname)) - self.assertEqual(actual.name, expected.name, - '{} failed - name'.format(testname)) - self.assertEqual(actual.units, expected.units, - '{} failed - units'.format(testname)) - + print_test_message(testname, indata=indata, actual=actual, expected=expected) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) #========================================================================= diff --git a/source/test/physarrayTests.py b/source/test/physarrayTests.py index 172a6b61..b0ca8971 100644 --- a/source/test/physarrayTests.py +++ b/source/test/physarrayTests.py @@ -16,9 +16,9 @@ from copy import deepcopy -#=============================================================================== +#======================================================================================================================= # PhysArrayTests -#=============================================================================== +#======================================================================================================================= class PhysArrayTests(unittest.TestCase): """ Unit tests for basic aspects of the PhysArray class @@ -28,8 +28,10 @@ def assertPhysArraysEqual(self, left, right, testname='Test', decimal=0): if type(left) != type(right): self.fail('{} failed - type') elif isinstance(left, PhysArray): + ldata = numpy.ma.asarray(left) + rdata = numpy.ma.asarray(right) if decimal == 0: - npt.assert_array_equal(left, right, '{} failed - data'.format(testname)) + npt.assert_array_equal(ldata, rdata, '{} failed - data'.format(testname)) else: npt.assert_array_almost_equal(left, right, decimal, '{} failed - data'.format(testname)) self.assertEqual(left.dtype, right.dtype, '{} failed - dtype'.format(testname)) @@ -53,7 +55,7 @@ def test_init(self): PhysArray(1.3), PhysArray((1, 2, 3)), PhysArray([1, 2, 3]), - PhysArray(numpy.array([1,2,3], dtype=numpy.float64)), + PhysArray(numpy.array([1, 2, 3], dtype=numpy.float64)), PhysArray([1, 2, 3]), CharArray('asfeasefa'), CharArray(['asfeasefa', 'asfe', 'e'])] @@ -94,17 +96,17 @@ def test_init_units_value_error(self): def test_init_dimensions_default(self): testname = 'PhysArray([[1,2],[3,4]], name="X").dimensions' - X = PhysArray([[1,2],[3,4]], name='X') + X = PhysArray([[1, 2], [3, 4]], name='X') actual = X.dimensions - expected = (1,0) + expected = (1, 0) print_test_message(testname, actual=actual, expected=expected) self.assertEqual(actual, expected, '{} failed'.format(testname)) - + def test_init_dimensions_valid(self): valid_input = [(1,), (1.4,), ('x',), [-1]] for indata in valid_input: testname = 'PhysArray([1,2,3], name="X", dimensions={!r}).dimensions'.format(indata) - X = PhysArray([1,2,3], name='X', dimensions=indata) + X = PhysArray([1, 2, 3], name='X', dimensions=indata) actual = X.dimensions expected = tuple(indata) print_test_message(testname, dimensions=indata, actual=actual, expected=expected) @@ -116,22 +118,22 @@ def test_init_dimensions_type_error(self): testname = 'PhysArray([1,2,3], name="X", dimensions={!r})'.format(indata) expected = TypeError print_test_message(testname, units=indata, expected=expected) - self.assertRaises(expected, PhysArray, [1,2,3], dimensions=indata, name='X') + self.assertRaises(expected, PhysArray, [1, 2, 3], dimensions=indata, name='X') def test_init_dimensions_value_error(self): - invalid_input = [(1,2), ['a', 'b', 'c'], []] + invalid_input = [(1, 2), ['a', 'b', 'c'], []] for indata in invalid_input: testname = 'PhysArray([1,2,3], name="X", dimensions={!r})'.format(indata) expected = ValueError print_test_message(testname, units=indata, expected=expected) - self.assertRaises(expected, PhysArray, [1,2,3], dimensions=indata, name='X') + self.assertRaises(expected, PhysArray, [1, 2, 3], dimensions=indata, name='X') def test_init_char(self): valid_input = [['asef', 'ae', 'dfht'], ('shfudnej', 'x')] for indata in valid_input: testname = 'PhysArray({}, name="X", dimensions=(x,n))'.format(indata) - actual = PhysArray(indata, name='X', dimensions=('x','n')) - expected = CharArray(indata, name='X', dimensions=('x','n')) + actual = PhysArray(indata, name='X', dimensions=('x', 'n')) + expected = CharArray(indata, name='X', dimensions=('x', 'n')) print_test_message(testname, actual=actual, expected=expected) self.assertPhysArraysEqual(actual, expected, testname) @@ -185,8 +187,8 @@ def test_cast(self): def test_cast_override(self): indata = PhysArray([1, 2, 3], name='X', units='m', dimensions=('x',), positive='up') - overrides = {'name':"Y", 'units':1, 'dimensions': (5,), 'positive': "down"} - overridestr = ','.join('{!s}={!r}'.format(k,overrides[k]) for k in overrides) + overrides = {'name': "Y", 'units': 1, 'dimensions': (5,), 'positive': "down"} + overridestr = ','.join('{!s}={!r}'.format(k, overrides[k]) for k in overrides) testname = 'PhysArray({!r}, {})'.format(indata, overridestr) actual = PhysArray(indata, **overrides) expected = PhysArray([1, 2, 3], **overrides) @@ -239,6 +241,8 @@ def test_down(self): #=============================================================================== # PhysArrayBinOpTests #=============================================================================== + + class PhysArrayBinOpTests(PhysArrayTests): """ Unit tests for binary operators of the PhysArray class @@ -263,253 +267,256 @@ def setUp(self): 15: PhysArray(3.0)} def _test_binary_operator_(self, binop, expvals, testname): - for i,j in expvals: - expected = expvals[(i,j)] + for i, j in expvals: + expected = expvals[(i, j)] X = PhysArray(deepcopy(self.vs[i]), name='X') if isinstance(self.vs[i], PhysArray) else deepcopy(self.vs[i]) Y = PhysArray(deepcopy(self.vs[j]), name='Y') if isinstance(self.vs[j], PhysArray) else deepcopy(self.vs[j]) - - print 'TEST ID: {}'.format((i,j)) + + print 'TEST ID: {}'.format((i, j)) if type(expected) is type and issubclass(expected, Exception): - print_test_message(testname, testid=(i,j), X=X, Y=Y, expected=expected) + print_test_message(testname, testid=(i, j), X=X, Y=Y, expected=expected) self.assertRaises(expected, binop, X, Y) else: actual = binop(X, Y) - print_test_message(testname, testid=(i,j), X=X, Y=Y, actual=actual, expected=expected) + print_test_message(testname, testid=(i, j), X=X, Y=Y, actual=actual, expected=expected) self.assertPhysArraysEqual(actual, expected, testname) def test_add(self): - expvals = {(0,1): PhysArray(2.0, name='(1.0+Y)'), - (1,0): PhysArray(2.0, name='(X+1.0)'), - (1,1): PhysArray(2.0, name='(X+Y)'), - (1,2): PhysArray(2.0, name='(up(X)+Y)', positive='up'), - (2,1): PhysArray(2.0, name='(X+up(Y))', positive='up'), - (1,3): PhysArray(2.0, name='(down(X)+Y)', positive='down'), - (2,3): PhysArray(0.0, name='(X+up(Y))', positive='up'), - (3,2): PhysArray(0.0, name='(X+down(Y))', positive='down'), - (4,5): PhysArray(1.01, name='(X+convert(Y, from=cm, to=m))', units='m'), - (6,7): PhysArray(1.02, name='(X+convert(Y, from=0.02, to=1))', units=1), - (1,8): PhysArray([2.0, 3.0, 4.0], name='(X+Y)', dimensions=('x',)), - (8,8): PhysArray([2.0, 4.0, 6.0], name='(X+Y)', dimensions=('x',)), - (8,9): PhysArray([[5., 6., 7.], [6., 7., 8.], [7., 8., 9.]], dimensions=('x','y'), - name='(broadcast(X, from=[x], to=[x,y])+transpose(broadcast(Y, from=[y], to=[y,x]), from=[y,x], to=[x,y]))'), - (8,10): DimensionsError, - (10,10): PhysArray([[2.0, 4.0], [6.0, 8.0]], name='(X+Y)', dimensions=('x', 'y')), - (10,11): PhysArray([[2.0, 5.0], [5.0, 8.0]], name='(X+transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), - (11,12): PhysArray([[2.0, 5.0], [5.0, 8.0]], name='(up(X)+transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), - (12,13): PhysArray([[0.0, -1.0], [1.0, 0.0]], name='(X+up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} + expvals = {(0, 1): PhysArray(2.0, name='(1.0+Y)'), + (1, 0): PhysArray(2.0, name='(X+1.0)'), + (1, 1): PhysArray(2.0, name='(X+Y)'), + (1, 2): PhysArray(2.0, name='(up(X)+Y)', positive='up'), + (2, 1): PhysArray(2.0, name='(X+up(Y))', positive='up'), + (1, 3): PhysArray(2.0, name='(down(X)+Y)', positive='down'), + (2, 3): PhysArray(0.0, name='(X+up(Y))', positive='up'), + (3, 2): PhysArray(0.0, name='(X+down(Y))', positive='down'), + (4, 5): PhysArray(1.01, name='(X+convert(Y, from=cm, to=m))', units='m'), + (6, 7): PhysArray(1.02, name='(X+convert(Y, from=0.02, to=1))', units=1), + (1, 8): PhysArray([2.0, 3.0, 4.0], name='(X+Y)', dimensions=('x',)), + (8, 8): PhysArray([2.0, 4.0, 6.0], name='(X+Y)', dimensions=('x',)), + (8, 9): PhysArray([[5., 6., 7.], [6., 7., 8.], [7., 8., 9.]], dimensions=('x', 'y'), + name='(broadcast(X, from=[x], to=[x,y])+transpose(broadcast(Y, from=[y], to=[y,x]), from=[y,x], to=[x,y]))'), + (8, 10): DimensionsError, + (10, 10): PhysArray([[2.0, 4.0], [6.0, 8.0]], name='(X+Y)', dimensions=('x', 'y')), + (10, 11): PhysArray([[2.0, 5.0], [5.0, 8.0]], name='(X+transpose(Y, from=[y,x], to=[x,y]))', + dimensions=('x', 'y')), + (11, 12): PhysArray([[2.0, 5.0], [5.0, 8.0]], name='(up(X)+transpose(Y, from=[x,y], to=[y,x]))', + dimensions=('y', 'x'), positive='up'), + (12, 13): PhysArray([[0.0, -1.0], [1.0, 0.0]], name='(X+up(transpose(Y, from=[y,x], to=[x,y])))', + dimensions=('x', 'y'), positive='up')} self._test_binary_operator_(operator.add, expvals, 'X + Y') def test_iadd(self): - expvals = {(0,1): PhysArray(2.0, name='(1.0+Y)'), - (1,0): PhysArray(2.0, name='(X+1.0)'), - (1,1): PhysArray(2.0, name='(X+Y)'), - (1,2): PhysArray(2.0, name='(up(X)+Y)', positive='up'), - (2,1): PhysArray(2.0, name='(X+up(Y))', positive='up'), - (1,3): PhysArray(2.0, name='(down(X)+Y)', positive='down'), - (2,3): PhysArray(0.0, name='(X+up(Y))', positive='up'), - (3,2): PhysArray(0.0, name='(X+down(Y))', positive='down'), - (4,5): PhysArray(1.01, name='(X+convert(Y, from=cm, to=m))', units='m'), - (6,7): PhysArray(1.02, name='(X+convert(Y, from=0.02, to=1))', units=1), - (1,8): DimensionsError, - (8,8): PhysArray([2.0, 4.0, 6.0], name='(X+Y)', dimensions=('x',)), - (8,9): DimensionsError, - (8,10): DimensionsError, - (10,10): PhysArray([[2.0, 4.0], [6.0, 8.0]], name='(X+Y)', dimensions=('x', 'y')), - (10,11): PhysArray([[2.0, 5.0], [5.0, 8.0]], name='(X+transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), - (11,12): PhysArray([[2.0, 5.0], [5.0, 8.0]], name='(up(X)+transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), - (12,13): PhysArray([[0.0, -1.0], [1.0, 0.0]], name='(X+up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} + expvals = {(0, 1): PhysArray(2.0, name='(1.0+Y)'), + (1, 0): PhysArray(2.0, name='(X+1.0)'), + (1, 1): PhysArray(2.0, name='(X+Y)'), + (1, 2): PhysArray(2.0, name='(up(X)+Y)', positive='up'), + (2, 1): PhysArray(2.0, name='(X+up(Y))', positive='up'), + (1, 3): PhysArray(2.0, name='(down(X)+Y)', positive='down'), + (2, 3): PhysArray(0.0, name='(X+up(Y))', positive='up'), + (3, 2): PhysArray(0.0, name='(X+down(Y))', positive='down'), + (4, 5): PhysArray(1.01, name='(X+convert(Y, from=cm, to=m))', units='m'), + (6, 7): PhysArray(1.02, name='(X+convert(Y, from=0.02, to=1))', units=1), + (1, 8): DimensionsError, + (8, 8): PhysArray([2.0, 4.0, 6.0], name='(X+Y)', dimensions=('x',)), + (8, 9): DimensionsError, + (8, 10): DimensionsError, + (10, 10): PhysArray([[2.0, 4.0], [6.0, 8.0]], name='(X+Y)', dimensions=('x', 'y')), + (10, 11): PhysArray([[2.0, 5.0], [5.0, 8.0]], name='(X+transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), + (11, 12): PhysArray([[2.0, 5.0], [5.0, 8.0]], name='(up(X)+transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), + (12, 13): PhysArray([[0.0, -1.0], [1.0, 0.0]], name='(X+up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} self._test_binary_operator_(operator.iadd, expvals, 'X += Y') def test_sub(self): - expvals = {(0,1): PhysArray(0.0, name='(1.0-Y)'), - (1,0): PhysArray(0.0, name='(X-1.0)'), - (1,1): PhysArray(0.0, name='(X-Y)'), - (1,2): PhysArray(0.0, name='(up(X)-Y)', positive='up'), - (2,1): PhysArray(0.0, name='(X-up(Y))', positive='up'), - (1,3): PhysArray(0.0, name='(down(X)-Y)', positive='down'), - (2,3): PhysArray(2.0, name='(X-up(Y))', positive='up'), - (3,2): PhysArray(2.0, name='(X-down(Y))', positive='down'), - (4,5): PhysArray(0.99, name='(X-convert(Y, from=cm, to=m))', units='m'), - (6,7): PhysArray(0.98, name='(X-convert(Y, from=0.02, to=1))', units=1), - (1,8): PhysArray([0.0, -1.0, -2.0], name='(X-Y)', dimensions=('x',)), - (8,8): PhysArray([0.0, 0.0, 0.0], name='(X-Y)', dimensions=('x',)), - (8,9): PhysArray([[-3., -4., -5.], [-2., -3., -4.], [-1., -2., -3.]], dimensions=('x','y'), - name='(broadcast(X, from=[x], to=[x,y])-transpose(broadcast(Y, from=[y], to=[y,x]), from=[y,x], to=[x,y]))'), - (8,10): DimensionsError, - (10,10): PhysArray([[0.0, 0.0], [0.0, 0.0]], name='(X-Y)', dimensions=('x', 'y')), - (10,11): PhysArray([[0.0, -1.0], [1.0, 0.0]], name='(X-transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), - (11,12): PhysArray([[0.0, -1.0], [1.0, 0.0]], name='(up(X)-transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), - (12,13): PhysArray([[2.0, 5.0], [5.0, 8.0]], name='(X-up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} + expvals = {(0, 1): PhysArray(0.0, name='(1.0-Y)'), + (1, 0): PhysArray(0.0, name='(X-1.0)'), + (1, 1): PhysArray(0.0, name='(X-Y)'), + (1, 2): PhysArray(0.0, name='(up(X)-Y)', positive='up'), + (2, 1): PhysArray(0.0, name='(X-up(Y))', positive='up'), + (1, 3): PhysArray(0.0, name='(down(X)-Y)', positive='down'), + (2, 3): PhysArray(2.0, name='(X-up(Y))', positive='up'), + (3, 2): PhysArray(2.0, name='(X-down(Y))', positive='down'), + (4, 5): PhysArray(0.99, name='(X-convert(Y, from=cm, to=m))', units='m'), + (6, 7): PhysArray(0.98, name='(X-convert(Y, from=0.02, to=1))', units=1), + (1, 8): PhysArray([0.0, -1.0, -2.0], name='(X-Y)', dimensions=('x',)), + (8, 8): PhysArray([0.0, 0.0, 0.0], name='(X-Y)', dimensions=('x',)), + (8, 9): PhysArray([[-3., -4., -5.], [-2., -3., -4.], [-1., -2., -3.]], dimensions=('x', 'y'), + name='(broadcast(X, from=[x], to=[x,y])-transpose(broadcast(Y, from=[y], to=[y,x]), from=[y,x], to=[x,y]))'), + (8, 10): DimensionsError, + (10, 10): PhysArray([[0.0, 0.0], [0.0, 0.0]], name='(X-Y)', dimensions=('x', 'y')), + (10, 11): PhysArray([[0.0, -1.0], [1.0, 0.0]], name='(X-transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), + (11, 12): PhysArray([[0.0, -1.0], [1.0, 0.0]], name='(up(X)-transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), + (12, 13): PhysArray([[2.0, 5.0], [5.0, 8.0]], name='(X-up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} self._test_binary_operator_(operator.sub, expvals, 'X - Y') def test_isub(self): - expvals = {(0,1): PhysArray(0.0, name='(1.0-Y)'), - (1,0): PhysArray(0.0, name='(X-1.0)'), - (1,1): PhysArray(0.0, name='(X-Y)'), - (1,2): PhysArray(0.0, name='(up(X)-Y)', positive='up'), - (2,1): PhysArray(0.0, name='(X-up(Y))', positive='up'), - (1,3): PhysArray(0.0, name='(down(X)-Y)', positive='down'), - (2,3): PhysArray(2.0, name='(X-up(Y))', positive='up'), - (3,2): PhysArray(2.0, name='(X-down(Y))', positive='down'), - (4,5): PhysArray(0.99, name='(X-convert(Y, from=cm, to=m))', units='m'), - (6,7): PhysArray(0.98, name='(X-convert(Y, from=0.02, to=1))', units=1), - (1,8): DimensionsError, - (8,8): PhysArray([0.0, 0.0, 0.0], name='(X-Y)', dimensions=('x',)), - (8,9): DimensionsError, - (8,10): DimensionsError, - (10,10): PhysArray([[0.0, 0.0], [0.0, 0.0]], name='(X-Y)', dimensions=('x', 'y')), - (10,11): PhysArray([[0.0, -1.0], [1.0, 0.0]], name='(X-transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), - (11,12): PhysArray([[0.0, -1.0], [1.0, 0.0]], name='(up(X)-transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), - (12,13): PhysArray([[2.0, 5.0], [5.0, 8.0]], name='(X-up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} + expvals = {(0, 1): PhysArray(0.0, name='(1.0-Y)'), + (1, 0): PhysArray(0.0, name='(X-1.0)'), + (1, 1): PhysArray(0.0, name='(X-Y)'), + (1, 2): PhysArray(0.0, name='(up(X)-Y)', positive='up'), + (2, 1): PhysArray(0.0, name='(X-up(Y))', positive='up'), + (1, 3): PhysArray(0.0, name='(down(X)-Y)', positive='down'), + (2, 3): PhysArray(2.0, name='(X-up(Y))', positive='up'), + (3, 2): PhysArray(2.0, name='(X-down(Y))', positive='down'), + (4, 5): PhysArray(0.99, name='(X-convert(Y, from=cm, to=m))', units='m'), + (6, 7): PhysArray(0.98, name='(X-convert(Y, from=0.02, to=1))', units=1), + (1, 8): DimensionsError, + (8, 8): PhysArray([0.0, 0.0, 0.0], name='(X-Y)', dimensions=('x',)), + (8, 9): DimensionsError, + (8, 10): DimensionsError, + (10, 10): PhysArray([[0.0, 0.0], [0.0, 0.0]], name='(X-Y)', dimensions=('x', 'y')), + (10, 11): PhysArray([[0.0, -1.0], [1.0, 0.0]], name='(X-transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), + (11, 12): PhysArray([[0.0, -1.0], [1.0, 0.0]], name='(up(X)-transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), + (12, 13): PhysArray([[2.0, 5.0], [5.0, 8.0]], name='(X-up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} self._test_binary_operator_(operator.isub, expvals, 'X -= Y') def test_mul(self): - expvals = {(0,1): PhysArray(1.0, name='(1.0*Y)'), - (1,0): PhysArray(1.0, name='(X*1.0)'), - (1,1): PhysArray(1.0, name='(X*Y)'), - (1,2): PhysArray(1.0, name='(up(X)*Y)', positive='up'), - (2,1): PhysArray(1.0, name='(X*up(Y))', positive='up'), - (1,3): PhysArray(1.0, name='(down(X)*Y)', positive='down'), - (2,3): PhysArray(-1.0, name='(X*up(Y))', positive='up'), - (3,2): PhysArray(-1.0, name='(X*down(Y))', positive='down'), - (4,5): PhysArray(1.0, name='(X*Y)', units='0.01 m^2'), - (6,7): PhysArray(1.0, name='(X*Y)', units='0.02'), - (1,8): PhysArray([1.0, 2.0, 3.0], name='(X*Y)', dimensions=('x',)), - (8,8): PhysArray([1.0, 4.0, 9.0], name='(X*Y)', dimensions=('x',)), - (8,9): PhysArray([[4., 5., 6.], [8., 10., 12.], [12., 15., 18.]], dimensions=('x','y'), - name='(broadcast(X, from=[x], to=[x,y])*transpose(broadcast(Y, from=[y], to=[y,x]), from=[y,x], to=[x,y]))'), - (8,10): DimensionsError, - (10,10): PhysArray([[1.0, 4.0], [9.0, 16.0]], name='(X*Y)', dimensions=('x', 'y')), - (10,11): PhysArray([[1.0, 6.0], [6.0, 16.0]], name='(X*transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), - (11,12): PhysArray([[1.0, 6.0], [6.0, 16.0]], name='(up(X)*transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), - (12,13): PhysArray([[-1.0, -6.0], [-6.0, -16.0]], name='(X*up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} + expvals = {(0, 1): PhysArray(1.0, name='(1.0*Y)'), + (1, 0): PhysArray(1.0, name='(X*1.0)'), + (1, 1): PhysArray(1.0, name='(X*Y)'), + (1, 2): PhysArray(1.0, name='(up(X)*Y)', positive='up'), + (2, 1): PhysArray(1.0, name='(X*up(Y))', positive='up'), + (1, 3): PhysArray(1.0, name='(down(X)*Y)', positive='down'), + (2, 3): PhysArray(-1.0, name='(X*up(Y))', positive='up'), + (3, 2): PhysArray(-1.0, name='(X*down(Y))', positive='down'), + (4, 5): PhysArray(1.0, name='(X*Y)', units='0.01 m^2'), + (6, 7): PhysArray(1.0, name='(X*Y)', units='0.02'), + (1, 8): PhysArray([1.0, 2.0, 3.0], name='(X*Y)', dimensions=('x',)), + (8, 8): PhysArray([1.0, 4.0, 9.0], name='(X*Y)', dimensions=('x',)), + (8, 9): PhysArray([[4., 5., 6.], [8., 10., 12.], [12., 15., 18.]], dimensions=('x', 'y'), + name='(broadcast(X, from=[x], to=[x,y])*transpose(broadcast(Y, from=[y], to=[y,x]), from=[y,x], to=[x,y]))'), + (8, 10): DimensionsError, + (10, 10): PhysArray([[1.0, 4.0], [9.0, 16.0]], name='(X*Y)', dimensions=('x', 'y')), + (10, 11): PhysArray([[1.0, 6.0], [6.0, 16.0]], name='(X*transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), + (11, 12): PhysArray([[1.0, 6.0], [6.0, 16.0]], name='(up(X)*transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), + (12, 13): PhysArray([[-1.0, -6.0], [-6.0, -16.0]], name='(X*up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} self._test_binary_operator_(operator.mul, expvals, 'X * Y') def test_imul(self): - expvals = {(0,1): PhysArray(1.0, name='(1.0*Y)'), - (1,0): PhysArray(1.0, name='(X*1.0)'), - (1,1): PhysArray(1.0, name='(X*Y)'), - (1,2): PhysArray(1.0, name='(up(X)*Y)', positive='up'), - (2,1): PhysArray(1.0, name='(X*up(Y))', positive='up'), - (1,3): PhysArray(1.0, name='(down(X)*Y)', positive='down'), - (2,3): PhysArray(-1.0, name='(X*up(Y))', positive='up'), - (3,2): PhysArray(-1.0, name='(X*down(Y))', positive='down'), - (4,5): PhysArray(1.0, name='(X*Y)', units='0.01 m^2'), - (6,7): PhysArray(1.0, name='(X*Y)', units='0.02'), - (1,8): DimensionsError, - (8,8): PhysArray([1.0, 4.0, 9.0], name='(X*Y)', dimensions=('x',)), - (8,9): DimensionsError, - (8,10): DimensionsError, - (10,10): PhysArray([[1.0, 4.0], [9.0, 16.0]], name='(X*Y)', dimensions=('x', 'y')), - (10,11): PhysArray([[1.0, 6.0], [6.0, 16.0]], name='(X*transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), - (11,12): PhysArray([[1.0, 6.0], [6.0, 16.0]], name='(up(X)*transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), - (12,13): PhysArray([[-1.0, -6.0], [-6.0, -16.0]], name='(X*up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} + expvals = {(0, 1): PhysArray(1.0, name='(1.0*Y)'), + (1, 0): PhysArray(1.0, name='(X*1.0)'), + (1, 1): PhysArray(1.0, name='(X*Y)'), + (1, 2): PhysArray(1.0, name='(up(X)*Y)', positive='up'), + (2, 1): PhysArray(1.0, name='(X*up(Y))', positive='up'), + (1, 3): PhysArray(1.0, name='(down(X)*Y)', positive='down'), + (2, 3): PhysArray(-1.0, name='(X*up(Y))', positive='up'), + (3, 2): PhysArray(-1.0, name='(X*down(Y))', positive='down'), + (4, 5): PhysArray(1.0, name='(X*Y)', units='0.01 m^2'), + (6, 7): PhysArray(1.0, name='(X*Y)', units='0.02'), + (1, 8): DimensionsError, + (8, 8): PhysArray([1.0, 4.0, 9.0], name='(X*Y)', dimensions=('x',)), + (8, 9): DimensionsError, + (8, 10): DimensionsError, + (10, 10): PhysArray([[1.0, 4.0], [9.0, 16.0]], name='(X*Y)', dimensions=('x', 'y')), + (10, 11): PhysArray([[1.0, 6.0], [6.0, 16.0]], name='(X*transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), + (11, 12): PhysArray([[1.0, 6.0], [6.0, 16.0]], name='(up(X)*transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), + (12, 13): PhysArray([[-1.0, -6.0], [-6.0, -16.0]], name='(X*up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} self._test_binary_operator_(operator.imul, expvals, 'X *= Y') def test_div(self): - expvals = {(0,1): PhysArray(1.0, name='(1.0/Y)'), - (1,0): PhysArray(1.0, name='(X/1.0)'), - (1,1): PhysArray(1.0, name='(X/Y)'), - (1,2): PhysArray(1.0, name='(up(X)/Y)', positive='up'), - (2,1): PhysArray(1.0, name='(X/up(Y))', positive='up'), - (1,3): PhysArray(1.0, name='(down(X)/Y)', positive='down'), - (2,3): PhysArray(-1.0, name='(X/up(Y))', positive='up'), - (3,2): PhysArray(-1.0, name='(X/down(Y))', positive='down'), - (4,5): PhysArray(1.0, name='(X/Y)', units='100'), - (6,7): PhysArray(1.0, name='(X/Y)', units='50'), - (1,8): PhysArray([1.0, 0.5, 1/3.], name='(X/Y)', dimensions=('x',)), - (8,8): PhysArray([1.0, 1.0, 1.0], name='(X/Y)', dimensions=('x',)), - (8,9): PhysArray([[.25, .2, 1/6.], [.5, .4, 1/3.], [.75, 3/5., .5]], dimensions=('x','y'), - name='(broadcast(X, from=[x], to=[x,y])/transpose(broadcast(Y, from=[y], to=[y,x]), from=[y,x], to=[x,y]))'), - (8,10): DimensionsError, - (10,10): PhysArray([[1.0, 1.0], [1.0, 1.0]], name='(X/Y)', dimensions=('x', 'y')), - (10,11): PhysArray([[1.0, 2/3.], [1.5, 1.0]], name='(X/transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), - (11,12): PhysArray([[1.0, 2/3.], [1.5, 1.0]], name='(up(X)/transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), - (12,13): PhysArray([[-1.0, -2/3.], [-1.5, -1.0]], name='(X/up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} + expvals = {(0, 1): PhysArray(1.0, name='(1.0/Y)'), + (1, 0): PhysArray(1.0, name='(X/1.0)'), + (1, 1): PhysArray(1.0, name='(X/Y)'), + (1, 2): PhysArray(1.0, name='(up(X)/Y)', positive='up'), + (2, 1): PhysArray(1.0, name='(X/up(Y))', positive='up'), + (1, 3): PhysArray(1.0, name='(down(X)/Y)', positive='down'), + (2, 3): PhysArray(-1.0, name='(X/up(Y))', positive='up'), + (3, 2): PhysArray(-1.0, name='(X/down(Y))', positive='down'), + (4, 5): PhysArray(1.0, name='(X/Y)', units='100'), + (6, 7): PhysArray(1.0, name='(X/Y)', units='50'), + (1, 8): PhysArray([1.0, 0.5, 1 / 3.], name='(X/Y)', dimensions=('x',)), + (8, 8): PhysArray([1.0, 1.0, 1.0], name='(X/Y)', dimensions=('x',)), + (8, 9): PhysArray([[.25, .2, 1 / 6.], [.5, .4, 1 / 3.], [.75, 3 / 5., .5]], dimensions=('x', 'y'), + name='(broadcast(X, from=[x], to=[x,y])/transpose(broadcast(Y, from=[y], to=[y,x]), from=[y,x], to=[x,y]))'), + (8, 10): DimensionsError, + (10, 10): PhysArray([[1.0, 1.0], [1.0, 1.0]], name='(X/Y)', dimensions=('x', 'y')), + (10, 11): PhysArray([[1.0, 2 / 3.], [1.5, 1.0]], name='(X/transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), + (11, 12): PhysArray([[1.0, 2 / 3.], [1.5, 1.0]], name='(up(X)/transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), + (12, 13): PhysArray([[-1.0, -2 / 3.], [-1.5, -1.0]], name='(X/up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} self._test_binary_operator_(operator.div, expvals, 'X / Y') def test_idiv(self): - expvals = {(0,1): PhysArray(1.0, name='(1.0/Y)'), - (1,0): PhysArray(1.0, name='(X/1.0)'), - (1,1): PhysArray(1.0, name='(X/Y)'), - (1,2): PhysArray(1.0, name='(up(X)/Y)', positive='up'), - (2,1): PhysArray(1.0, name='(X/up(Y))', positive='up'), - (1,3): PhysArray(1.0, name='(down(X)/Y)', positive='down'), - (2,3): PhysArray(-1.0, name='(X/up(Y))', positive='up'), - (3,2): PhysArray(-1.0, name='(X/down(Y))', positive='down'), - (4,5): PhysArray(1.0, name='(X/Y)', units='100'), - (6,7): PhysArray(1.0, name='(X/Y)', units='50'), - (1,8): DimensionsError, - (8,8): PhysArray([1.0, 1.0, 1.0], name='(X/Y)', dimensions=('x',)), - (8,9): DimensionsError, - (8,10): DimensionsError, - (10,10): PhysArray([[1.0, 1.0], [1.0, 1.0]], name='(X/Y)', dimensions=('x', 'y')), - (10,11): PhysArray([[1.0, 2/3.], [1.5, 1.0]], name='(X/transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), - (11,12): PhysArray([[1.0, 2/3.], [1.5, 1.0]], name='(up(X)/transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), - (12,13): PhysArray([[-1.0, -2/3.], [-1.5, -1.0]], name='(X/up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} + expvals = {(0, 1): PhysArray(1.0, name='(1.0/Y)'), + (1, 0): PhysArray(1.0, name='(X/1.0)'), + (1, 1): PhysArray(1.0, name='(X/Y)'), + (1, 2): PhysArray(1.0, name='(up(X)/Y)', positive='up'), + (2, 1): PhysArray(1.0, name='(X/up(Y))', positive='up'), + (1, 3): PhysArray(1.0, name='(down(X)/Y)', positive='down'), + (2, 3): PhysArray(-1.0, name='(X/up(Y))', positive='up'), + (3, 2): PhysArray(-1.0, name='(X/down(Y))', positive='down'), + (4, 5): PhysArray(1.0, name='(X/Y)', units='100'), + (6, 7): PhysArray(1.0, name='(X/Y)', units='50'), + (1, 8): DimensionsError, + (8, 8): PhysArray([1.0, 1.0, 1.0], name='(X/Y)', dimensions=('x',)), + (8, 9): DimensionsError, + (8, 10): DimensionsError, + (10, 10): PhysArray([[1.0, 1.0], [1.0, 1.0]], name='(X/Y)', dimensions=('x', 'y')), + (10, 11): PhysArray([[1.0, 2 / 3.], [1.5, 1.0]], name='(X/transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), + (11, 12): PhysArray([[1.0, 2 / 3.], [1.5, 1.0]], name='(up(X)/transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), + (12, 13): PhysArray([[-1.0, -2 / 3.], [-1.5, -1.0]], name='(X/up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} self._test_binary_operator_(operator.idiv, expvals, 'X /= Y') def test_floordiv(self): - expvals = {(0,1): PhysArray(1.0, name='(1.0//Y)'), - (1,0): PhysArray(1.0, name='(X//1.0)'), - (1,1): PhysArray(1.0, name='(X//Y)'), - (1,2): PhysArray(1.0, name='(up(X)//Y)', positive='up'), - (2,1): PhysArray(1.0, name='(X//up(Y))', positive='up'), - (1,3): PhysArray(1.0, name='(down(X)//Y)', positive='down'), - (2,3): PhysArray(-1.0, name='(X//up(Y))', positive='up'), - (3,2): PhysArray(-1.0, name='(X//down(Y))', positive='down'), - (4,5): PhysArray(1.0, name='(X//Y)', units='100'), - (6,7): PhysArray(1.0, name='(X//Y)', units='50'), - (1,8): PhysArray([1.0, 0., 0.], name='(X//Y)', dimensions=('x',)), - (8,8): PhysArray([1.0, 1.0, 1.0], name='(X//Y)', dimensions=('x',)), - (8,9): PhysArray([[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], dimensions=('x','y'), - name='(broadcast(X, from=[x], to=[x,y])//transpose(broadcast(Y, from=[y], to=[y,x]), from=[y,x], to=[x,y]))'), - (8,10): DimensionsError, - (10,10): PhysArray([[1.0, 1.0], [1.0, 1.0]], name='(X//Y)', dimensions=('x', 'y')), - (10,11): PhysArray([[1., 0.], [1., 1.]], name='(X//transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), - (11,12): PhysArray([[1., 0.], [1., 1.]], name='(up(X)//transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), - (12,13): PhysArray([[-1., -1.], [-2., -1.]], name='(X//up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} + expvals = {(0, 1): PhysArray(1.0, name='(1.0//Y)'), + (1, 0): PhysArray(1.0, name='(X//1.0)'), + (1, 1): PhysArray(1.0, name='(X//Y)'), + (1, 2): PhysArray(1.0, name='(up(X)//Y)', positive='up'), + (2, 1): PhysArray(1.0, name='(X//up(Y))', positive='up'), + (1, 3): PhysArray(1.0, name='(down(X)//Y)', positive='down'), + (2, 3): PhysArray(-1.0, name='(X//up(Y))', positive='up'), + (3, 2): PhysArray(-1.0, name='(X//down(Y))', positive='down'), + (4, 5): PhysArray(1.0, name='(X//Y)', units='100'), + (6, 7): PhysArray(1.0, name='(X//Y)', units='50'), + (1, 8): PhysArray([1.0, 0., 0.], name='(X//Y)', dimensions=('x',)), + (8, 8): PhysArray([1.0, 1.0, 1.0], name='(X//Y)', dimensions=('x',)), + (8, 9): PhysArray([[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]], dimensions=('x', 'y'), + name='(broadcast(X, from=[x], to=[x,y])//transpose(broadcast(Y, from=[y], to=[y,x]), from=[y,x], to=[x,y]))'), + (8, 10): DimensionsError, + (10, 10): PhysArray([[1.0, 1.0], [1.0, 1.0]], name='(X//Y)', dimensions=('x', 'y')), + (10, 11): PhysArray([[1., 0.], [1., 1.]], name='(X//transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), + (11, 12): PhysArray([[1., 0.], [1., 1.]], name='(up(X)//transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), + (12, 13): PhysArray([[-1., -1.], [-2., -1.]], name='(X//up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} self._test_binary_operator_(operator.floordiv, expvals, 'X // Y') def test_ifloordiv(self): - expvals = {(0,1): PhysArray(1.0, name='(1.0//Y)'), - (1,0): PhysArray(1.0, name='(X//1.0)'), - (1,1): PhysArray(1.0, name='(X//Y)'), - (1,2): PhysArray(1.0, name='(up(X)//Y)', positive='up'), - (2,1): PhysArray(1.0, name='(X//up(Y))', positive='up'), - (1,3): PhysArray(1.0, name='(down(X)//Y)', positive='down'), - (2,3): PhysArray(-1.0, name='(X//up(Y))', positive='up'), - (3,2): PhysArray(-1.0, name='(X//down(Y))', positive='down'), - (4,5): PhysArray(1.0, name='(X//Y)', units='100'), - (6,7): PhysArray(1.0, name='(X//Y)', units='50'), - (1,8): DimensionsError, - (8,8): PhysArray([1.0, 1.0, 1.0], name='(X//Y)', dimensions=('x',)), - (8,9): DimensionsError, - (8,10): DimensionsError, - (10,10): PhysArray([[1., 1.], [1.0, 1.0]], name='(X//Y)', dimensions=('x', 'y')), - (10,11): PhysArray([[1., 0.], [1.0, 1.0]], name='(X//transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), - (11,12): PhysArray([[1., 0.], [1.0, 1.0]], name='(up(X)//transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), - (12,13): PhysArray([[-1., -1.], [-2.0, -1.0]], name='(X//up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} + expvals = {(0, 1): PhysArray(1.0, name='(1.0//Y)'), + (1, 0): PhysArray(1.0, name='(X//1.0)'), + (1, 1): PhysArray(1.0, name='(X//Y)'), + (1, 2): PhysArray(1.0, name='(up(X)//Y)', positive='up'), + (2, 1): PhysArray(1.0, name='(X//up(Y))', positive='up'), + (1, 3): PhysArray(1.0, name='(down(X)//Y)', positive='down'), + (2, 3): PhysArray(-1.0, name='(X//up(Y))', positive='up'), + (3, 2): PhysArray(-1.0, name='(X//down(Y))', positive='down'), + (4, 5): PhysArray(1.0, name='(X//Y)', units='100'), + (6, 7): PhysArray(1.0, name='(X//Y)', units='50'), + (1, 8): DimensionsError, + (8, 8): PhysArray([1.0, 1.0, 1.0], name='(X//Y)', dimensions=('x',)), + (8, 9): DimensionsError, + (8, 10): DimensionsError, + (10, 10): PhysArray([[1., 1.], [1.0, 1.0]], name='(X//Y)', dimensions=('x', 'y')), + (10, 11): PhysArray([[1., 0.], [1.0, 1.0]], name='(X//transpose(Y, from=[y,x], to=[x,y]))', dimensions=('x', 'y')), + (11, 12): PhysArray([[1., 0.], [1.0, 1.0]], name='(up(X)//transpose(Y, from=[x,y], to=[y,x]))', dimensions=('y', 'x'), positive='up'), + (12, 13): PhysArray([[-1., -1.], [-2.0, -1.0]], name='(X//up(transpose(Y, from=[y,x], to=[x,y])))', dimensions=('x', 'y'), positive='up')} self._test_binary_operator_(operator.ifloordiv, expvals, 'X //= Y') def test_mod(self): - expvals = {(0,1): NotImplementedError, - (1,0): NotImplementedError, - (1,1): NotImplementedError} + expvals = {(0, 1): NotImplementedError, + (1, 0): NotImplementedError, + (1, 1): NotImplementedError} self._test_binary_operator_(operator.mod, expvals, 'X % Y') self._test_binary_operator_(operator.imod, expvals, 'X %= Y') def test_pow(self): - expvals = {(1,14): PhysArray(1.0, name='(X**2)'), - (15,14): PhysArray(9.0, name='(X**2)'), - (8,14): PhysArray([1., 4., 9.], name='(X**2)', dimensions=('x',)), - (8,8): DimensionsError, - (8,4): UnitsError, - (2,14): PhysArray(1., name='(X**2)'), - (2,15): PhysArray(1., name='(X**Y)', positive='up')} + expvals = {(1, 14): PhysArray(1.0, name='(X**2)'), + (15, 14): PhysArray(9.0, name='(X**2)'), + (8, 14): PhysArray([1., 4., 9.], name='(X**2)', dimensions=('x',)), + (8, 8): DimensionsError, + (8, 4): UnitsError, + (2, 14): PhysArray(1., name='(X**2)'), + (2, 15): PhysArray(1., name='(X**Y)', positive='up')} self._test_binary_operator_(operator.pow, expvals, 'X ** Y') self._test_binary_operator_(operator.ipow, expvals, 'X **= Y') - + def test_convert(self): xdata = numpy.array(2., dtype='d') X = PhysArray(xdata, name='X', units='km') @@ -538,7 +545,7 @@ def test_transpose_dims(self): actual = X.transpose(*indata) new_name = "transpose({}, from=[u,v], to=[v,u])".format(X.name, indata) expected = PhysArray([[1., 3.], [2., 4.]], units=X.units, - name=new_name, dimensions=indata) + name=new_name, dimensions=indata) print_test_message(testname, actual=actual, expected=expected, X=X) self.assertPhysArraysEqual(actual, expected, testname=testname) @@ -551,7 +558,7 @@ def test_transpose_axes(self): new_dims = ('v', 'u') new_name = "transpose({}, from=[u,v], to=[v,u])".format(X.name) expected = PhysArray([[1., 3.], [2., 4.]], units=X.units, - name=new_name, dimensions=new_dims) + name=new_name, dimensions=new_dims) print_test_message(testname, actual=actual, expected=expected, X=X) self.assertPhysArraysEqual(actual, expected, testname=testname) @@ -564,7 +571,7 @@ def test_transpose_axes_tuple(self): new_dims = ('v', 'u') new_name = "transpose({}, from=[u,v], to=[v,u])".format(X.name, new_dims) expected = PhysArray([[1., 3.], [2., 4.]], units=X.units, - name=new_name, dimensions=new_dims) + name=new_name, dimensions=new_dims) print_test_message(testname, actual=actual, expected=expected, X=X) self.assertPhysArraysEqual(actual, expected, testname=testname) From 62b32f15faa59776cff646be53b84528326f9b07 Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Thu, 27 Sep 2018 12:36:01 -0600 Subject: [PATCH 09/23] Generalizing and fixing bugs --- source/pyconform/modules/dynvarmipdiags.py | 99 ++++++++----------- .../pyconform/modules/dynvarmipfunctions.py | 56 +++++------ 2 files changed, 69 insertions(+), 86 deletions(-) diff --git a/source/pyconform/modules/dynvarmipdiags.py b/source/pyconform/modules/dynvarmipdiags.py index 4e2dc495..05012b7f 100644 --- a/source/pyconform/modules/dynvarmipdiags.py +++ b/source/pyconform/modules/dynvarmipdiags.py @@ -18,125 +18,112 @@ def _init_wtem(time, levi, lat, wzm, vthzm, thzm): ntime = len(time) - nlevi = len(levi) - nlat = len(lat) latrad = (lat / 180.0) * numpy.pi coslat = numpy.cos(latrad) levi100 = 100.0 * levi - _wzm = -1.0 * numpy.einsum('ijk,j->ijk', wzm, levi100) / H + _wzm = -1.0 * numpy.einsum('ij...,j->ij...', wzm, levi100) / H - wtem = numpy.zeros((ntime, nlevi, nlat), dtype='d') + wtem = numpy.zeros(wzm.shape, dtype='d') for itime in range(ntime): - dthdp = deriv(levi100, thzm[itime, :, :]) - psieddy = vthzm[itime, :, :] / dthdp + dthdp = deriv(levi100, thzm[itime, ...]) + psieddy = vthzm[itime, ...] / dthdp dpsidy = deriv(latrad, psieddy * coslat, axis=1) / (a * coslat) - wtem[itime, :, :] = _wzm[itime, :, :] + dpsidy + wtem[itime, ...] = _wzm[itime, ...] + dpsidy return wtem def wtem(time, levi, lat, wzm, vthzm, thzm): levi100 = 100.0 * levi wtem = _init_wtem(time, levi, lat, wzm, vthzm, thzm) - wtem = -1.0 * numpy.einsum('ijk,j->ijk', wtem, H / levi100) + wtem = -1.0 * numpy.einsum('ij...,j->ij...', wtem, H / levi100) return wtem def vtem(time, levi, lat, vzm, vthzm, thzm): ntime = len(time) - nlevi = len(levi) - nlat = len(lat) levi100 = 100.0 * levi - vtem = numpy.zeros((ntime, nlevi, nlat), dtype='d') + vtem = numpy.zeros(vzm.shape, dtype='d') for itime in range(ntime): - dthdp = deriv(levi100, thzm[itime, :, :]) - psieddy = vthzm[itime, :, :] / dthdp + dthdp = deriv(levi100, thzm[itime, ...]) + psieddy = vthzm[itime, ...] / dthdp dpsidp = deriv(levi100, psieddy) - vtem[itime, :, :] = vzm[itime, :, :] - dpsidp + vtem[itime, ...] = vzm[itime, ...] - dpsidp return vtem def utendvtem(time, levi, lat, uzm, vzm, vthzm, thzm): ntime = len(time) - nlevi = len(levi) - nlat = len(lat) latrad = (lat / 180.0) * numpy.pi coslat = numpy.cos(latrad) f = 2 * om * numpy.sin(latrad) vtem = vtem(time, levi, lat, vzm, vthzm, thzm) - utendvtem = numpy.zeros((ntime, nlevi, nlat), dtype='d') + utendvtem = numpy.zeros(uzm.shape, dtype='d') for itime in range(ntime): - ucos = numpy.einsum('ij,j->ij', uzm[itime, :, :], coslat) + ucos = numpy.einsum('ij...,j->ij...', uzm[itime, ...], coslat) dudphi = deriv(latrad, ucos, axis=1) / a - utendvtem[itime, :, :] = vtem[itime, :, :] * (f - dudphi) + utendvtem[itime, ...] = vtem[itime, ...] * (f - dudphi) return utendvtem def utendwtem(time, levi, lat, uzm, wzm, vthzm, thzm): ntime = len(time) - nlevi = len(levi) - nlat = len(lat) levi100 = 100.0 * levi wtem = _init_wtem(time, levi, lat, wzm, vthzm, thzm) - utendwtem = numpy.zeros((ntime, nlevi, nlat), dtype='d') + utendwtem = numpy.zeros(uzm.shape, dtype='d') for itime in range(ntime): - dudp = deriv(levi100, uzm[itime, :, :]) - utendwtem[itime, :, :] = -1.0 * wtem[itime, :, :] * dudp + dudp = deriv(levi100, uzm[itime, ...]) + utendwtem[itime, ...] = -1.0 * wtem[itime, ...] * dudp return utendwtem def _init_epfy(time, levi, lat, uzm, uvzm, vthzm, thzm): ntime = len(time) - nlevi = len(levi) - nlat = len(lat) latrad = (lat / 180.0) * numpy.pi coslat = numpy.cos(latrad) levi100 = 100.0 * levi - epfy = numpy.zeros((ntime, nlevi, nlat), dtype='d') + epfy = numpy.zeros(uzm.shape, dtype='d') for itime in range(ntime): - dthdp = deriv(levi100, thzm[itime, :, :]) - dudp = deriv(levi100, uzm[itime, :, :]) - psieddy = vthzm[itime, :, :] / dthdp - epfy[itime, :, :] = a * coslat * (dudp * psieddy - uvzm[itime, :, :]) + dthdp = deriv(levi100, thzm[itime, ...]) + dudp = deriv(levi100, uzm[itime, ...]) + psieddy = vthzm[itime, ...] / dthdp + epfy[itime, ...] = a * coslat * (dudp * psieddy - uvzm[itime, ...]) return epfy def epfy(time, levi, lat, uzm, uvzm, vthzm, thzm): levi100 = 100.0 * levi epfy = _init_epfy(time, levi, lat, uzm, uvzm, vthzm, thzm) - epfy = numpy.einsum('ijk,j->ijk', epfy, levi100) / p0 + epfy = numpy.einsum('ij...,j->ij...', epfy, levi100) / p0 return epfy def _init_epfz(time, levi, lat, uzm, uwzm, vthzm, thzm): ntime = len(time) - nlevi = len(levi) - nlat = len(lat) latrad = (lat / 180.0) * numpy.pi coslat = numpy.cos(latrad) f = 2 * om * numpy.sin(latrad) levi100 = 100.0 * levi - _uwzm = -1.0 * numpy.einsum('ijk,j->ijk', uwzm, levi100) / H + _uwzm = -1.0 * numpy.einsum('ij...,j->ij...', uwzm, levi100) / H - epfz = numpy.zeros((ntime, nlevi, nlat), dtype='d') + epfz = numpy.zeros(uzm.shape, dtype='d') for itime in range(ntime): - ucos = numpy.einsum('ij,j->ij', uzm[itime, :, :], coslat) + ucos = numpy.einsum('ij...,j->ij...', uzm[itime, ...], coslat) dudphi = deriv(latrad, ucos, axis=1) / a - dthdp = deriv(levi100, thzm[itime, :, :]) - psieddy = vthzm[itime, :, :] / dthdp - epfz[itime, :, :] = a * coslat * \ - (((f - dudphi) * psieddy) - _uwzm[itime, :, :]) + dthdp = deriv(levi100, thzm[itime, ...]) + psieddy = vthzm[itime, ...] / dthdp + epfz[itime, ...] = a * coslat * (((f - dudphi) * psieddy) - _uwzm[itime, ...]) return epfz @@ -148,8 +135,6 @@ def epfz(time, levi, lat, uzm, uwzm, vthzm, thzm): def utendepfd(time, levi, lat, uzm, uvzm, uwzm, vthzm, thzm): ntime = len(time) - nlevi = len(levi) - nlat = len(lat) latrad = (lat / 180.0) * numpy.pi coslat = numpy.cos(latrad) @@ -158,36 +143,34 @@ def utendepfd(time, levi, lat, uzm, uvzm, uwzm, vthzm, thzm): epfy = _init_epfy(time, levi, lat, uzm, uvzm, vthzm, thzm) epfz = _init_epfz(time, levi, lat, uzm, uwzm, vthzm, thzm) - utendepfd = numpy.zeros((ntime, nlevi, nlat), dtype='d') + utendepfd = numpy.zeros(uzm.shape, dtype='d') for itime in range(ntime): - depfydphi = deriv(latrad, epfy[itime, :, :] - * coslat, axis=1) / (a * coslat) - depfzdp = deriv(levi100, epfz[itime, :, :]) - utendepfd[itime, :, :] = (depfydphi + depfzdp) / (a * coslat) + depfydphi = deriv(latrad, epfy[itime, ...] * coslat, axis=1) / (a * coslat) + depfzdp = deriv(levi100, epfz[itime, ...]) + utendepfd[itime, ...] = (depfydphi + depfzdp) / (a * coslat) return utendepfd def psitem(time, levi, lat, vzm, vthzm, thzm): ntime = len(time) nlevi = len(levi) - nlat = len(lat) latrad = (lat / 180.0) * numpy.pi coslat = numpy.cos(latrad) levi100 = levi * 100.0 - psitem = numpy.zeros((ntime, nlevi, nlat), dtype='d') - vzmwithzero = numpy.zeros((nlevi + 1, nlat), dtype='d') + psitem = numpy.zeros(vzm.shape, dtype='d') + vzm_shape_1 = list(vzm.shape[1:]) + vzm_shape_1[0] += 1 + vzmwithzero = numpy.zeros(vzm_shape_1, dtype='d') levi100withzero = numpy.zeros((nlevi + 1,), dtype='d') levi100withzero[1:] = levi100 for itime in range(ntime): - dthdp = deriv(levi100, thzm[itime, :, :]) - psieddy = vthzm[itime, :, :] / dthdp - vzmwithzero[1:, :] = vzm[itime, :, :] + dthdp = deriv(levi100, thzm[itime, ...]) + psieddy = vthzm[itime, ...] / dthdp + vzmwithzero[1:, ...] = vzm[itime, ...] for ilev in range(1, nlevi + 1): - result = int_tabulated( - levi100withzero[0:ilev + 1], vzmwithzero[0:ilev + 1, :]) - psitem[itime, ilev - 1, - :] = ((2 * numpy.pi * a * coslat) / g0) * (result - psieddy[ilev - 1, :]) + result = int_tabulated(levi100withzero[0:ilev + 1], vzmwithzero[0:ilev + 1, ...]) + psitem[itime, ilev - 1, ...] = ((2 * numpy.pi * a * coslat) / g0) * (result - psieddy[ilev - 1, ...]) return psitem diff --git a/source/pyconform/modules/dynvarmipfunctions.py b/source/pyconform/modules/dynvarmipfunctions.py index 3c9b3a34..0a37283c 100644 --- a/source/pyconform/modules/dynvarmipfunctions.py +++ b/source/pyconform/modules/dynvarmipfunctions.py @@ -17,109 +17,109 @@ class DynVarMIPFunction(Function): key = 'dynvarmipfunc' - def __init__(self, *args): - super(DynVarMIPFunction, self).__init__(*args) + def __init__(self, time, lev, lat, *args): + super(DynVarMIPFunction, self).__init__(time, lev, lat, *args) self._dynvarmip_func = None def __getitem__(self, index): - args = [self.arguments[i][index] for i in len(self.arguments)] + args = [arg[index] for arg in self.arguments] arg_names = ','.join([arg.name for arg in args]) data = self._dynvarmip_func(*args) name = '{}({})'.format(self.key, arg_names) - return PhysArray(data, name=name) + return PhysArray(data, name=name, dimensions=args[-1].dimensions) #========================================================================= # wtemDynVarMIPFunction #========================================================================= -class wtemDynVarMIPFunction(Function): +class wtemDynVarMIPFunction(DynVarMIPFunction): key = 'dynvarmip_wtem' - def __init__(self, time, levi, lat, wzm, vthzm, thzm): + def __init__(self, time, lev, lat, wzm, vthzm, thzm): super(wtemDynVarMIPFunction, self).__init__( - time, levi, lat, wzm, vthzm, thzm) + time, lev, lat, wzm, vthzm, thzm) self._dynvarmip_func = dvmd.wtem #========================================================================= # utendwtemDynVarMIPFunction #========================================================================= -class utendwtemDynVarMIPFunction(Function): +class utendwtemDynVarMIPFunction(DynVarMIPFunction): key = 'dynvarmip_utendwtem' - def __init__(self, time, levi, lat, uzm, wzm, vthzm, thzm): + def __init__(self, time, lev, lat, uzm, wzm, vthzm, thzm): super(utendwtemDynVarMIPFunction, self).__init__( - time, levi, lat, uzm, wzm, vthzm, thzm) + time, lev, lat, uzm, wzm, vthzm, thzm) self._dynvarmip_func = dvmd.utendwtem #========================================================================= # vtemDynVarMIPFunction #========================================================================= -class vtemDynVarMIPFunction(Function): +class vtemDynVarMIPFunction(DynVarMIPFunction): key = 'dynvarmip_vtem' - def __init__(self, time, levi, lat, vzm, vthzm, thzm): + def __init__(self, time, lev, lat, vzm, vthzm, thzm): super(vtemDynVarMIPFunction, self).__init__( - time, levi, lat, vzm, vthzm, thzm) + time, lev, lat, vzm, vthzm, thzm) self._dynvarmip_func = dvmd.vtem #========================================================================= # utendvtemDynVarMIPFunction #========================================================================= -class utendvtemDynVarMIPFunction(Function): +class utendvtemDynVarMIPFunction(DynVarMIPFunction): key = 'dynvarmip_utendvtem' - def __init__(self, time, levi, lat, uzm, vzm, vthzm, thzm): + def __init__(self, time, lev, lat, uzm, vzm, vthzm, thzm): super(utendvtemDynVarMIPFunction, self).__init__( - time, levi, lat, uzm, vzm, vthzm, thzm) + time, lev, lat, uzm, vzm, vthzm, thzm) self._dynvarmip_func = dvmd.utendvtem #========================================================================= # epfyDynVarMIPFunction #========================================================================= -class epfyDynVarMIPFunction(Function): +class epfyDynVarMIPFunction(DynVarMIPFunction): key = 'dynvarmip_epfy' - def __init__(self, time, levi, lat, uzm, uvzm, vthzm, thzm): + def __init__(self, time, lev, lat, uzm, uvzm, vthzm, thzm): super(epfyDynVarMIPFunction, self).__init__( - time, levi, lat, uzm, uvzm, vthzm, thzm) + time, lev, lat, uzm, uvzm, vthzm, thzm) self._dynvarmip_func = dvmd.epfy #========================================================================= # epfzDynVarMIPFunction #========================================================================= -class epfzDynVarMIPFunction(Function): +class epfzDynVarMIPFunction(DynVarMIPFunction): key = 'dynvarmip_epfz' - def __init__(self, time, levi, lat, uzm, uwzm, vthzm, thzm): + def __init__(self, time, lev, lat, uzm, uwzm, vthzm, thzm): super(epfzDynVarMIPFunction, self).__init__( - time, levi, lat, uzm, uwzm, vthzm, thzm) + time, lev, lat, uzm, uwzm, vthzm, thzm) self._dynvarmip_func = dvmd.epfz #========================================================================= # utendepfdDynVarMIPFunction #========================================================================= -class utendepfdDynVarMIPFunction(Function): +class utendepfdDynVarMIPFunction(DynVarMIPFunction): key = 'dynvarmip_utendepfd' - def __init__(self, time, levi, lat, uzm, uvzm, uwzm, vthzm, thzm): + def __init__(self, time, lev, lat, uzm, uvzm, uwzm, vthzm, thzm): super(utendepfdDynVarMIPFunction, self).__init__( - time, levi, lat, uzm, uvzm, uwzm, vthzm, thzm) + time, lev, lat, uzm, uvzm, uwzm, vthzm, thzm) self._dynvarmip_func = dvmd.utendepfd #========================================================================= # psitemDynVarMIPFunction #========================================================================= -class psitemDynVarMIPFunction(Function): +class psitemDynVarMIPFunction(DynVarMIPFunction): key = 'dynvarmip_psitem' - def __init__(self, time, levi, lat, vzm, vthzm, thzm): + def __init__(self, time, lev, lat, vzm, vthzm, thzm): super(psitemDynVarMIPFunction, self).__init__( - time, levi, lat, vzm, vthzm, thzm) + time, lev, lat, vzm, vthzm, thzm) self._dynvarmip_func = dvmd.psitem From dcde1b6695b3840cad4326ebe6733558df2f4180 Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Thu, 27 Sep 2018 13:16:52 -0600 Subject: [PATCH 10/23] Add chdims function --- source/pyconform/functions.py | 21 +++++++++++++++++++++ source/test/functionsTests.py | 13 ++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/source/pyconform/functions.py b/source/pyconform/functions.py index f5db47d3..5745af25 100644 --- a/source/pyconform/functions.py +++ b/source/pyconform/functions.py @@ -555,3 +555,24 @@ def __getitem__(self, index): else self.arguments[0][index]) new_name = 'rmunits({!s})'.format(getname(data)) return PhysArray(data, name=new_name, units=1) + + +#========================================================================= +# RenameDimensionsFunction +#========================================================================= +class RenameDimensionsFunction(Function): + key = 'chdims' + + def __init__(self, data, *dims): + super(RenameDimensionsFunction, self).__init__(data, *dims) + + def __getitem__(self, index): + data = self.arguments[0] if is_constant(self.arguments[0]) else self.arguments[0][index] + if len(self.arguments) == 1: + return data + dims = self.arguments[1:] + dim_names = ', '.join([repr(dim) for dim in dims]) + new_name = 'chdims({!s}, {!s})'.format(getname(data), dim_names) + new_dims = list(data.dimensions) + new_dims[:len(dims)] = dims + return PhysArray(data, name=new_name, dimensions=new_dims) \ No newline at end of file diff --git a/source/test/functionsTests.py b/source/test/functionsTests.py index 942ce4be..78e613d1 100644 --- a/source/test/functionsTests.py +++ b/source/test/functionsTests.py @@ -173,7 +173,7 @@ def test_list_operators(self): def test_list_functions(self): testname = 'list_functions()' actual = sorted(functions.list_functions()) - expected = sorted(['chunits', 'down', 'limit', 'max', + expected = sorted(['chdims', 'chunits', 'down', 'limit', 'max', 'mean', 'min', 'rmunits', 'sqrt', 'sum', 'up']) print_test_message(testname, actual=actual, expected=expected) self.assertEqual(actual, expected, '{} failed'.format(testname)) @@ -751,6 +751,17 @@ def test_func_rmunits(self): print_test_message(testname, indata=indata, actual=actual, expected=expected) self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) + def test_func_chdims(self): + key = 'chdims' + indata = PhysArray([[1, 2], [3, 4]], name='x', dimensions=('a', 'b')) + inargs = ['A'] + testname = "{}({}, 'A')".format(key, indata) + func = functions.find(key) + actual = func(indata, *inargs)[:] + expected = PhysArray([[1, 2], [3, 4]], name="chdims(x, 'A')", dimensions=('A', 'b')) + print_test_message(testname, indata=indata, actual=actual, expected=expected) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) + #========================================================================= # Command-Line Operation From 7c86037b9b3b4e1a83cdeb41049c3e1403921453 Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Thu, 27 Sep 2018 13:20:57 -0600 Subject: [PATCH 11/23] Add tests for corner cases --- source/pyconform/functions.py | 3 ++- source/test/functionsTests.py | 24 +++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/source/pyconform/functions.py b/source/pyconform/functions.py index 5745af25..93f31175 100644 --- a/source/pyconform/functions.py +++ b/source/pyconform/functions.py @@ -574,5 +574,6 @@ def __getitem__(self, index): dim_names = ', '.join([repr(dim) for dim in dims]) new_name = 'chdims({!s}, {!s})'.format(getname(data), dim_names) new_dims = list(data.dimensions) - new_dims[:len(dims)] = dims + dlen = min(len(dims), len(new_dims)) + new_dims[:dlen] = dims[:dlen] return PhysArray(data, name=new_name, dimensions=new_dims) \ No newline at end of file diff --git a/source/test/functionsTests.py b/source/test/functionsTests.py index 78e613d1..762261f4 100644 --- a/source/test/functionsTests.py +++ b/source/test/functionsTests.py @@ -751,7 +751,7 @@ def test_func_rmunits(self): print_test_message(testname, indata=indata, actual=actual, expected=expected) self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) - def test_func_chdims(self): + def test_func_chdims_short(self): key = 'chdims' indata = PhysArray([[1, 2], [3, 4]], name='x', dimensions=('a', 'b')) inargs = ['A'] @@ -762,6 +762,28 @@ def test_func_chdims(self): print_test_message(testname, indata=indata, actual=actual, expected=expected) self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) + def test_func_chdims_full(self): + key = 'chdims' + indata = PhysArray([[1, 2], [3, 4]], name='x', dimensions=('a', 'b')) + inargs = ['A', 'B'] + testname = "{}({}, 'A', 'B')".format(key, indata) + func = functions.find(key) + actual = func(indata, *inargs)[:] + expected = PhysArray([[1, 2], [3, 4]], name="chdims(x, 'A', 'B')", dimensions=('A', 'B')) + print_test_message(testname, indata=indata, actual=actual, expected=expected) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) + + def test_func_chdims_long(self): + key = 'chdims' + indata = PhysArray([[1, 2], [3, 4]], name='x', dimensions=('a', 'b')) + inargs = ['A', 'B', 'C'] + testname = "{}({}, 'A', 'B', 'C)".format(key, indata) + func = functions.find(key) + actual = func(indata, *inargs)[:] + expected = PhysArray([[1, 2], [3, 4]], name="chdims(x, 'A', 'B', 'C')", dimensions=('A', 'B')) + print_test_message(testname, indata=indata, actual=actual, expected=expected) + self.assertPhysArraysEqual(actual, expected, '{} failed'.format(testname)) + #========================================================================= # Command-Line Operation From 7d0321f08a6781a7cc8bccdccc1652128b907ab7 Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Wed, 3 Oct 2018 15:49:44 -0600 Subject: [PATCH 12/23] Add tests for IDL functions --- source/pyconform/modules/idl.py | 17 +++++++--- source/test/modules/__init__.py | 0 source/test/modules/idlTests.py | 60 +++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 source/test/modules/__init__.py create mode 100644 source/test/modules/idlTests.py diff --git a/source/pyconform/modules/idl.py b/source/pyconform/modules/idl.py index 8b7627fa..982fdb8d 100644 --- a/source/pyconform/modules/idl.py +++ b/source/pyconform/modules/idl.py @@ -19,10 +19,17 @@ _signature_letters = 'ijklmnabcdefgh' -def deriv(x1, y1, axis=0): +def deriv(x, y=None, axis=0): """ Python version of GDL deriv function """ + if y is None: + y1 = numpy.array(x) + x1 = numpy.arange(len(x), dtype=y1.dtype) + else: + y1 = numpy.array(y) + x1 = numpy.array(x) + x0 = numpy.roll(x1, 1) x2 = numpy.roll(x1, -1) y0 = numpy.roll(y1, 1, axis=axis) @@ -126,10 +133,10 @@ def spl_interp(xa, ya, y2a, x, axis=0): jhi = [khi if k == axis else slice(None) for k in range(numpy.ndim(ya))] jlo = [klo if k == axis else slice(None) for k in range(numpy.ndim(ya))] - yahi = ya[jhi] - yalo = ya[jlo] - y2ahi = y2a[jhi] - y2alo = y2a[jlo] + yahi = ya[tuple(jhi)] + yalo = ya[tuple(jlo)] + y2ahi = y2a[tuple(jhi)] + y2alo = y2a[tuple(jlo)] a = (xahi - x) / h b = (x - xalo) / h diff --git a/source/test/modules/__init__.py b/source/test/modules/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/source/test/modules/idlTests.py b/source/test/modules/idlTests.py new file mode 100644 index 00000000..763a3db5 --- /dev/null +++ b/source/test/modules/idlTests.py @@ -0,0 +1,60 @@ +""" +Unit Tests for IDL Functions + +Copyright 2017-2018, University Corporation for Atmospheric Research +LICENSE: See the LICENSE.rst file for details +""" + +import unittest +import numpy as np + +from pyconform.modules.idl import deriv, spl_init, spl_interp, int_tabulated + + +class Tests(unittest.TestCase): + + def test_deriv_list_no_y(self): + x = [1.2345, 2.34567, 3.45678, 4.56789, 5.678901] + actual = deriv(x) + expected = [1.11120, 1.11114, 1.11111, 1.11106, 1.11096] + np.testing.assert_array_almost_equal(actual, expected, 6) + + def test_deriv_list(self): + x = [2., 5., 7., 3., 4.] + y = [3., 5., 4., 7., 8.] + actual = deriv(x, y) + expected = [1.3666666, -0.033333302, -0.25000000, 1.5833340, 0.41666698] + np.testing.assert_array_almost_equal(actual, expected, 6) + + def test_deriv_array(self): + x = np.array([2., 5., 7., 3., 4.]) + y = np.array([3., 5., 4., 7., 8.]) + actual = deriv(x, y) + expected = np.array([1.3666666, -0.033333302, -0.25000000, 1.5833340, 0.41666698]) + np.testing.assert_array_almost_equal(actual, expected, 6) + + def test_spl_init_array(self): + x = np.array([2., 5., 7., 3., 4.]) + y = np.array([3., 5., 4., 7., 8.]) + actual = spl_init(x, y) + expected = np.array([0.0000000, -1.5192308, 4.0961542, -4.4807696, 0.0000000]) + np.testing.assert_array_almost_equal(actual, expected, 6) + + def test_spl_interp_array(self): + x = np.array([2., 5., 7., 8., 13.]) + y = np.array([3., 5., 4., 7., 8.]) + xi = np.array([3., 6., 7.5, 9.]) + actual = spl_interp(x, y, spl_init(x, y), xi) + expected = np.array([4.3612623, 3.8121600, 5.3403325, 9.3114195]) + np.testing.assert_array_almost_equal(actual, expected, 6) + + def test_int_tabulated_array(self): + x = np.array([2., 5., 7., 8., 13.]) + y = np.array([3., 5., 4., 7., 8.]) + actual = int_tabulated(x, y) + expected = 77.627388 + np.testing.assert_array_almost_equal(actual, expected, 5) + + +if __name__ == "__main__": + unittest.main() From a72109bb88f55773d9e96ae0b4c8b7c6dabbe09f Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Wed, 3 Oct 2018 15:50:28 -0600 Subject: [PATCH 13/23] Add comment --- source/pyconform/modules/idl.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/pyconform/modules/idl.py b/source/pyconform/modules/idl.py index 982fdb8d..8e235726 100644 --- a/source/pyconform/modules/idl.py +++ b/source/pyconform/modules/idl.py @@ -8,7 +8,9 @@ IDL code for these functions can be found in the GDL, open-source version of IDL, available at https://github.com/gnudatalanguage/gdl. Obviously, the code was not copied, but the Python code was based on the original IDL -code to determine operation order and bit-for-bit correctness. +code to determine operation order and bit-for-bit correctness. + +NOTE: All of these functions return numpy arrays! Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details From 82dba0de057fcd8f8c44875d4e73fc551300b11d Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Thu, 4 Oct 2018 17:10:13 -0600 Subject: [PATCH 14/23] Modifications for integer dimension names --- source/pyconform/physarray.py | 55 ++++++++++++++++++----------------- source/test/physarrayTests.py | 23 +++++++++++++-- 2 files changed, 49 insertions(+), 29 deletions(-) diff --git a/source/pyconform/physarray.py b/source/pyconform/physarray.py index 9de9bfc0..1bd6c0de 100644 --- a/source/pyconform/physarray.py +++ b/source/pyconform/physarray.py @@ -22,6 +22,8 @@ #=================================================================================================== # UnitsError #=================================================================================================== + + class UnitsError(ValueError): """Exception indicating an error involving units of a PhysArray object""" @@ -154,11 +156,11 @@ class PhysArray(numpy.ma.MaskedArray): """ def __new__(cls, indata, name=None, units=None, dimensions=None, positive='', **kwds): - makwds = {k:kwds[k] for k in ['dtype'] if k in kwds} + makwds = {k: kwds[k] for k in ['dtype'] if k in kwds} obj = numpy.ma.asarray(indata, **makwds).view(cls) if obj.dtype.char in ('S', 'U'): return CharArray(indata, name=name, dimensions=dimensions) - + # Add the mask if specified if 'mask' in kwds: obj.mask = kwds['mask'] @@ -218,7 +220,7 @@ def units(self): def units(self, u): """Units of the data""" self._optinfo['units'] = u if isinstance(u, Unit) else Unit(u) - + @staticmethod def _safe_convert_(obj, units1, units2): # Because netcdftime datetime conversion always returns an NDArray, even if the @@ -240,7 +242,7 @@ def _safe_convert_(obj, units1, units2): def convert(self, units): """ Return a new PhysArray with new units - + Parameters: units (Unit): The new units to which to convert the PhysArray """ @@ -255,7 +257,7 @@ def convert(self, units): @property def dimensions(self): """Named dimensions of the data""" - return self._optinfo['dimensions'] + return self._optinfo['dimensions'] @dimensions.setter def dimensions(self, dims): @@ -265,13 +267,13 @@ def dimensions(self, dims): if len(dims) != len(self.shape): raise ValueError('Dimensions {} must have same length as shape {}'.format(dims, self.shape)) self._optinfo['dimensions'] = tuple(dims) - + def transpose(self, *dims): """ Return a new PhysArray with dimensions transposed in the order given - + Does nothing if no transpose is necesary - + Parameters: dims (tuple): Tuple of dimension names in the new order """ @@ -288,8 +290,8 @@ def transpose(self, *dims): if new_dims == self.dimensions: return self else: - old_dims_str = ','.join(self.dimensions) - new_dims_str = ','.join(new_dims) + old_dims_str = ','.join([str(d) for d in self.dimensions]) + new_dims_str = ','.join([str(d) for d in new_dims]) new_name = 'transpose({}, from=[{}], to=[{}])'.format(self.name, old_dims_str, new_dims_str) return PhysArray(super(PhysArray, self).transpose(*axes), dimensions=new_dims, name=new_name) @@ -313,7 +315,7 @@ def positive(self, pos): def flip(self): """ Flip the direction of the positive attribute, if set, and correspondingly multiply by -1 - + Does nothing if the positive attribute is not set (i.e., equals None) """ if self.positive is not None: @@ -326,7 +328,7 @@ def flip(self): def up(self): """ Set the direction of the positive attribute to 'up' and multiply by -1, if necessary - + Only multiplies by -1 if the positive attribute is already set to 'down'. """ if self.positive is None: @@ -339,7 +341,7 @@ def up(self): def down(self): """ Set the direction of the positive attribute to 'down' and multiply by -1, if necessary - + Only multiplies by -1 if the positive attribute is already set to 'up'. """ if self.positive is None: @@ -365,7 +367,7 @@ def __setitem__(self, index, values): if isinstance(values, PhysArray): values = values.convert(self.units).transpose(self.dimensions) super(PhysArray, self).__setitem__(idx, values) - + def _broadcast_(self, other): for d in set(self.dimensions).intersection(set(other.dimensions)): if self.shape[self.dimensions.index(d)] != other.shape[other.dimensions.index(d)]: @@ -376,13 +378,13 @@ def _broadcast_(self, other): other_dims = other.dimensions + tuple(d for d in self.dimensions if d not in other.dimensions) other.shape = tuple(other.shape[other.dimensions.index(d)] if d in other.dimensions else 1 for d in other_dims) if len(self.dimensions) > 0 and self_dims != self.dimensions: - fromdims = ','.join(self.dimensions) - todims = ','.join(self_dims) + fromdims = ','.join([str(d) for d in self.dimensions]) + todims = ','.join([str(d) for d in self_dims]) self.name = 'broadcast({}, from=[{}], to=[{}])'.format(self.name, fromdims, todims) self.dimensions = self_dims if len(other.dimensions) > 0 and other_dims != other.dimensions: - fromdims = ','.join(other.dimensions) - todims = ','.join(other_dims) + fromdims = ','.join([str(d) for d in other.dimensions]) + todims = ','.join([str(d) for d in other_dims]) other.name = 'broadcast({}, from=[{}], to=[{}])'.format(other.name, fromdims, todims) other.dimensions = other_dims return other.transpose(self_dims) @@ -402,7 +404,7 @@ def _match_positive_(self, other): other.down() else: other.flip() - + def _check_inplace_(self, other): other = PhysArray(other) sdims = set(self.dimensions) @@ -417,7 +419,7 @@ def _add_sub_init_(self, other): other = self._broadcast_(PhysArray(other)).convert(self.units) self._match_positive_(other) return other - + def __add__(self, other): result = PhysArray(self) other = result._add_sub_init_(other) @@ -559,7 +561,7 @@ def __rpow__(self, other): def __ipow__(self, other): other = self._check_exponent_(PhysArray(other)) super(PhysArray, self).__ipow__(other) - self.name='({!s}**{!s})'.format(self, other) + self.name = '({!s}**{!s})'.format(self, other) self.units **= other self.positive = None if other.data % 2 == 0 else self.positive return self @@ -613,7 +615,7 @@ def sum(self, dimensions=None, **kwds): dim_str = ','.join(str(d) for d in dims) return PhysArray(sumval, name='sum({}, dims=[{}])'.format(self.name, dim_str), dimensions=new_dims, positive=self.positive, units=self.units) - + #======================================================================================================================= # CharArray @@ -633,7 +635,7 @@ def __new__(cls, indata, name=None, dimensions=None): obj = obj.view('S1').reshape(shape) obj = numpy.ma.masked_where(obj == '', obj).view(cls) obj.fill_value = '' - + # Store a name associated with the object if name is None: obj.name = getname(indata) @@ -668,7 +670,7 @@ def _chararray_(indata): @staticmethod def _strarray_(indata): return numpy.asarray(indata, dtype='S') - + def __repr__(self): if self.shape[-1] > 0: prndat = self.data.view('S{}'.format(self.shape[-1])).reshape(self.shape[:-1]) @@ -677,11 +679,12 @@ def __repr__(self): datstr = str(prndat).replace(linesep, ' ') return ('{!s}(data={!s}, name={!r}, dimensions=' '{!s})').format(self.__class__.__name__, datstr, self.name, self.dimensions) + @property def units(self): """Units of the data""" return self._optinfo['units'] - + @units.setter def units(self, units): new_units = units if isinstance(units, Unit) else Unit(units) @@ -718,7 +721,7 @@ def invert(self): def stretch(self, newlen): if newlen > self.shape[-1]: - pad = numpy.zeros((self.shape[:-1] + (newlen-self.shape[-1],)), dtype='S') + pad = numpy.zeros((self.shape[:-1] + (newlen - self.shape[-1],)), dtype='S') pad = numpy.ma.masked_where(pad == '', pad) return CharArray(numpy.ma.concatenate((self, pad), axis=-1), name=self.name, dimensions=self.dimensions) else: diff --git a/source/test/physarrayTests.py b/source/test/physarrayTests.py index b0ca8971..ce32997b 100644 --- a/source/test/physarrayTests.py +++ b/source/test/physarrayTests.py @@ -238,16 +238,33 @@ def test_down(self): print_test_message(testname, actual=actual, expected=expected) self.assertPhysArraysEqual(actual, expected, testname=testname) + #=============================================================================== # PhysArrayBinOpTests #=============================================================================== - - -class PhysArrayBinOpTests(PhysArrayTests): +class PhysArrayBinOpTests(unittest.TestCase): """ Unit tests for binary operators of the PhysArray class """ + def assertPhysArraysEqual(self, left, right, testname='Test', decimal=0): + if type(left) != type(right): + self.fail('{} failed - type') + elif isinstance(left, PhysArray): + ldata = numpy.ma.asarray(left) + rdata = numpy.ma.asarray(right) + if decimal == 0: + npt.assert_array_equal(ldata, rdata, '{} failed - data'.format(testname)) + else: + npt.assert_array_almost_equal(left, right, decimal, '{} failed - data'.format(testname)) + self.assertEqual(left.dtype, right.dtype, '{} failed - dtype'.format(testname)) + self.assertEqual(left.name, right.name, '{} failed - name'.format(testname)) + self.assertEqual(left.units, right.units, '{} failed - units'.format(testname)) + self.assertEqual(left.dimensions, right.dimensions, '{} failed - dimensions'.format(testname)) + self.assertEqual(left.positive, right.positive, '{} failed - positive'.format(testname)) + else: + self.assertEqual(left, right, '{} failed') + def setUp(self): self.vs = {0: 1.0, 1: PhysArray(1.0), From 37406b20541b85aa9379164dd65bece9db9411f8 Mon Sep 17 00:00:00 2001 From: sherimickelson Date: Mon, 8 Oct 2018 09:01:54 -0600 Subject: [PATCH 15/23] Updates - Remove scale factor - Add missing value attribute to variable - Add/modified these user functions: yeartomonth, sftof, and nowoodyveg --- source/pyconform/flownodes.py | 8 +- source/pyconform/miptableparser.py | 4 +- .../modules/CLM_pft_to_CMIP6_vegtype.py | 1 - source/pyconform/modules/commonfunctions.py | 194 +++++++++++++++--- 4 files changed, 165 insertions(+), 42 deletions(-) diff --git a/source/pyconform/flownodes.py b/source/pyconform/flownodes.py index cd767120..3d017fd7 100644 --- a/source/pyconform/flownodes.py +++ b/source/pyconform/flownodes.py @@ -272,13 +272,7 @@ def __getitem__(self, index): # Compute the joined index object index12 = join(shape0, index1, index2) - # Retrieve the data from file, unpacking if necessary - if 'scale_factor' in attrs or 'add_offset' in attrs and not ncvar.scale: - scale_factor = attrs.get('scale_factor', 1) - add_offset = attrs.get('add_offset', 0) - data = scale_factor * ncvar[index12] + add_offset - else: - data = ncvar[index12] + data = ncvar[index12] # Upconvert, if possible if issubclass(ncvar.dtype.type, numpy.float) and ncvar.dtype.itemsize < 8: diff --git a/source/pyconform/miptableparser.py b/source/pyconform/miptableparser.py index 57df9745..cb3a353c 100644 --- a/source/pyconform/miptableparser.py +++ b/source/pyconform/miptableparser.py @@ -420,8 +420,8 @@ def parse_table(self,exp,mips,tables,v_list,table_var_fields,table_axes_fields,t if hasattr(c_var,'mipTable'): var['mipTable']=c_var.mipTable if c_var.mipTable in tables or '--ALL--' in tables: - var["_FillValue"] = "1e+20" - #var["missing_value"] = "1e+20" + var["_FillValue"] = "1.0E20" + var["missing_value"] = 1.0E20 if hasattr(c_var,'deflate'): var['deflate']= c_var.deflate if hasattr(c_var,'deflate_level'): diff --git a/source/pyconform/modules/CLM_pft_to_CMIP6_vegtype.py b/source/pyconform/modules/CLM_pft_to_CMIP6_vegtype.py index 73875501..40b5a5d4 100644 --- a/source/pyconform/modules/CLM_pft_to_CMIP6_vegtype.py +++ b/source/pyconform/modules/CLM_pft_to_CMIP6_vegtype.py @@ -177,7 +177,6 @@ def __getitem__(self, index): ppfts1d_lon.name, ppfts1d_lat.name, ppfts1d_active.name, ppfts1d_itype_veg.name, ppfts1d_wtgcell.name, ppfts1d_wtlunit.name) - print 'FINISHED FUNCTION' varo_vegType[varo_vegType>=1e+16] = 1e+20 ma_varo_vegType = np.ma.masked_values(varo_vegType, 1e+20) diff --git a/source/pyconform/modules/commonfunctions.py b/source/pyconform/modules/commonfunctions.py index e3a26c1d..bc75d559 100644 --- a/source/pyconform/modules/commonfunctions.py +++ b/source/pyconform/modules/commonfunctions.py @@ -46,7 +46,7 @@ def __init__(self, data, bdim='bnds', location=1, endpoints=1, idata=None): if len(data_info.dimensions) != 1: raise DimensionsError('bounds: data can only be 1D') self._mod_end = bool(endpoints) - self.add_sumlike_dimensions(data_info.dimensions[0]) +# self.add_sumlike_dimensions(data_info.dimensions[0]) if idata is None: self._compute_idata = True else: @@ -56,7 +56,7 @@ def __init__(self, data, bdim='bnds', location=1, endpoints=1, idata=None): raise TypeError('bounds: interface-data must be a PhysArray') if len(idata_info.dimensions) != 1: raise DimensionsError('bounds: interface-data can only be 1D') - self.add_sumlike_dimensions(idata_info.dimensions[0]) +# self.add_sumlike_dimensions(idata_info.dimensions[0]) def __getitem__(self, index): data = self.arguments[0][index] @@ -195,16 +195,68 @@ def __getitem__(self, index): time = p_time.data lat = p_lat.data lon = p_lon.data + + if time[0] == 0: + a = np.ma.zeros(((len(time)-1)*12,len(lat),len(lon))) + else: + a = np.ma.zeros((len(time)*12,len(lat),len(lon))) - a = np.zeros((len(time) * 12, len(lat), len(lon))) + k=0 for i in range(len(time)): - for j in range(12): - a[((i * 12) + j), :, :] = data[i, :, :] + if time[i] != 0: + for j in range(12): + a[((k*12)+j),:,:] = data[i,:,:] + k+=1 - new_name = 'yeartomonth_data({}{}{}{})'.format( - p_data.name, p_time.name, p_lat.name, p_lon.name) + a[a>=1e+16] = 1e+20 + a = np.ma.masked_values(a, 1e+20) + new_name = 'yeartomonth_data({}{}{}{})'.format(p_data.name, p_time.name, p_lat.name, p_lon.name) - return PhysArray(a, name=new_name, units=p_data.units) + return PhysArray(a, name = new_name, dimensions=[p_time.dimensions[0],p_lat.dimensions[0],p_lon.dimensions[0]], units=p_data.units) + + +#=================================================================================================== +# yeartomonth_data3DFunction +#=================================================================================================== +class YeartoMonth_data3DFunction(Function): + key = 'yeartomonth_data3D' + + def __init__(self, data, time, lat, lon, v): + super(YeartoMonth_data3DFunction, self).__init__(data, time, lat, lon, v) + + def __getitem__(self, index): + p_data = self.arguments[0][index] + p_time = self.arguments[1][index] + p_lat = self.arguments[2][index] + p_lon = self.arguments[3][index] + p_v = self.arguments[4][index] + + if index is None: + return PhysArray(np.zeros((0,0,0,0)), dimensions=[p_time.dimensions[0],p_v.dimensions[0],p_lat.dimensions[0],p_lon.dimensions[0]]) + + data = p_data.data + time = p_time.data + lat = p_lat.data + lon = p_lon.data + v = p_v.data + + if time[0] == 0: + a = np.ma.zeros(((len(time)-1)*12,len(v),len(lat),len(lon))) + else: + a = np.ma.zeros((len(time)*12,len(v),len(lat),len(lon))) + + k=0 + for i in range(len(time)): + if time[i] != 0: + for j in range(12): + a[((k*12)+j),:,:,:] = data[i,:,:,:] + k+=1 + + a[a>=1e+16] = 1e+20 + a = np.ma.masked_values(a, 1e+20) + new_name = 'yeartomonth_data({}{}{}{}{})'.format(p_data.name, p_time.name, p_lat.name, p_lon.name, p_v.name) + + return PhysArray(a, name = new_name, dimensions=[p_time.dimensions[0],p_v.dimensions[0],p_lat.dimensions[0],p_lon.dimensions[0]], units=p_data.units) #========================================================================= # yeartomonth_timeFunction @@ -227,17 +279,24 @@ def __getitem__(self, index): monLens = [31.0, 28.0, 31.0, 30.0, 31.0, 30.0, 31.0, 31.0, 30.0, 31.0, 30.0, 31.0] - a = np.zeros((len(time) * 12)) + a = np.zeros((len(time)*12)) + + k=0 for i in range(len(time)): prev = 0 - for j in range(12): - a[((i * 12) + j)] = float((time[i] - 365) + - prev + float(monLens[j] / 2.0)) - prev += monLens[j] - + if time[i] != 0: + for j in range(12): + a[((k*12)+j)] = float((time[i]-365)+prev+float(monLens[j]/2.0)) + prev += monLens[j] + k+=1 + + if a[-1] == 0 and not np.all(a==0): + b = np.resize(a,((len(time)-1)*12)) + else: + b = a new_name = 'yeartomonth_time({})'.format(p_time.name) - return PhysArray(a, name=new_name, dimensions=[p_time.dimensions[0]], units=p_time.units, calendar='noleap') + return PhysArray(b, name = new_name, dimensions=[p_time.dimensions[0]], units=p_time.units, calendar='noleap') #========================================================================= @@ -269,7 +328,7 @@ def __getitem__(self, index): return PhysArray(a, name=new_name, units=p_data.units) - + #========================================================================= # diff_axis1_ind0bczero_4dFunction #========================================================================= @@ -302,6 +361,7 @@ def __getitem__(self, index): return PhysArray(a, name=new_name, units=new_units, dimensions=new_dims) + #========================================================================= # sftofFunction #========================================================================= @@ -365,7 +425,7 @@ def __getitem__(self, index): # print a2[t] new_name = 'POP_bottom_layer_multadd({}{}{})'.format( - KMT.name, data1.name, data2.name) + p_KMT.name, p_data1.name, p_data2.name) new_units = p_data1.units * p_data2.units return PhysArray(a2, name=new_name, dimensions=[p_data2.dimensions[0]], units=new_units) @@ -489,49 +549,76 @@ def __getitem__(self, index): p_siline = self.arguments[5][index] multiple = self.arguments[6] - aice = p_aice - uvel = p_uvel - vvel = p_vvel - HTE = p_HTE - HTN = p_HTN - siline = p_siline - a = np.ma.zeros((aice.data.shape[0], siline.data.shape[0])) - - for t in range(aice.data.shape[0]): + aice = p_aice.data + uvel = p_uvel.data + vvel = p_vvel.data + HTE = p_HTE.data + HTN = p_HTN.data + siline = p_siline.data + a = np.ma.zeros((aice.shape[0], siline.shape[0])) + + uvel[uvel >= 1e+16] = 0.0 + vvel[vvel >= 1e+16] = 0.0 + + for t in range(aice.shape[0]): # 1 i = 92 for j in range(370, 381): - if aice[t, j, i] < 1e+16 and aice[t, j, i + 1] < 1e+16 and HTE[j, i] < 1e+16 and uvel[t, j, i] < 1e+16 and uvel[t, j - 1, i] < 1e+16: + if aice[t, j, i] >= 1e+16: + print "CICE aice WARNING: this point should not contain a missing value ",t, j, i + elif aice[t, j, i + 1] >= 1e+16: + print "CICE aice WARNING: this point should not contain a missing value ",t, j, i + 1 + else: a[t, 0] += 0.5 * (aice[t, j, i] + aice[t, j, i + 1]) * 0.5 * ( HTE[j, i] * uvel[t, j, i] + HTE[j, i] * uvel[t, j - 1, i]) # 2 i = 214 for j in range(375, 377): - if aice[t, j, i] < 1e+16 and aice[t, j, i + 1] < 1e+16 and HTE[j, i] < 1e+16 and uvel[t, j, i] < 1e+16 and uvel[t, j - 1, i] < 1e+16 and aice[t, j + 1, i] < 1e+16 and HTN[j, i] < 1e+16 and vvel[t, j, i] < 1e+16 and vvel[t, j, i - 1] < 1e+16: + if aice[t, j, i] >= 1e+16: + print "CICE aice WARNING: this point should not contain a missing value ",t, j, i + elif aice[t, j, i + 1] >= 1e+16: + print "CICE aice WARNING: this point should not contain a missing value ",t, j, i + 1 + elif aice[t, j + 1, i] >= 1e+16: + print "CICE aice WARNING: this point should not contain a missing value ",t, j + 1, i + else: a[t, 1] += 0.5 * (aice[t, j, i] + aice[t, j, i + 1]) * 0.5 * (HTE[j, i] * uvel[t, j, i] + HTE[j, i] * uvel[t, j - 1, i]) + 0.5 * ( aice[t, j, i] + aice[t, j + 1, i]) * 0.5 * (HTN[j, i] * vvel[t, j, i] + HTN[j, i] * vvel[t, j, i - 1]) j = 366 for i in range(240, 244): - if aice[t, j, i] < 1e+16 and aice[t, j, i + 1] < 1e+16 and HTE[j, i] < 1e+16 and uvel[t, j, i] < 1e+16 and uvel[t, j - 1, i] < 1e+16 and aice[t, j + 1, i] < 1e+16 and HTN[j, i] < 1e+16 and vvel[t, j, i] < 1e+16 and vvel[t, j, i - 1] < 1e+16: + if aice[t, j, i] >= 1e+16: + print "CICE aice WARNING: this point should not contain a missing value ",t, j, i + elif aice[t, j, i + 1] >= 1e+16: + print "CICE aice WARNING: this point should not contain a missing value ",t, j, i + 1 + elif aice[t, j + 1, i] >= 1e+16: + print "CICE aice WARNING: this point should not contain a missing value ",t, j + 1, i + else: a[t, 1] += 0.5 * (aice[t, j, i] + aice[t, j, i + 1]) * 0.5 * (HTE[j, i] * uvel[t, j, i] + HTE[j, i] * uvel[t, j - 1, i]) + 0.5 * ( aice[t, j, i] + aice[t, j + 1, i]) * 0.5 * (HTN[j, i] * vvel[t, j, i] + HTN[j, i] * vvel[t, j, i - 1]) # 3 i = 85 for j in range(344, 366): - if aice[t, j, i] < 1e+16 and aice[t, j, i + 1] < 1e+16 and HTE[j, i] < 1e+16 and uvel[t, j, i] < 1e+16 and uvel[t, j - 1, i] < 1e+16: + if aice[t, j, i] >= 1e+16: + print "CICE aice WARNING: this point should not contain a missing value ",t, j, i + elif aice[t, j, i + 1] >= 1e+16: + print "CICE aice WARNING: this point should not contain a missing value ",t, j, i + 1 + else: a[t, 2] += 0.5 * (aice[t, j, i] + aice[t, j, i + 1]) * 0.5 * ( HTE[j, i] * uvel[t, j, i] + HTE[j, i] * uvel[t, j - 1, i]) # 4 j = 333 for i in range(198, 201): - if aice[t, j, i] < 1e+16 and aice[t, j + 1, i] < 1e+16 and HTN[j, i] < 1e+16 and vvel[t, j, i] < 1e+16 and vvel[t, j, i - 1] < 1e+16: + if aice[t, j, i] >= 1e+16: + print "CICE aice WARNING: this point should not contain a missing value ",t, j, i + elif aice[t, j + 1, i] >= 1e+16: + print "CICE aice WARNING: this point should not contain a missing value ",t, j + 1, i + else: a[t, 3] += 0.5 * (aice[t, j, i] + aice[t, j + 1, i]) * 0.5 * ( HTN[j, i] * vvel[t, j, i] + HTN[j, i] * vvel[t, j, i - 1]) a = a * multiple new_name = 'cice_regions()'.format() - return PhysArray(a, name=new_name, dimensions=[p_aice.dimensions[0], p_siline.dimensions[0]], units=uvel.units) + return PhysArray(a, name=new_name, dimensions=[p_aice.dimensions[0], p_siline.dimensions[0]], units=p_uvel.units) #========================================================================= @@ -607,6 +694,49 @@ def __getitem__(self, index): return PhysArray(data, name=new_name, dimensions=[p_data1.dimensions[0], p_soilpool.dimensions[0], p_data1.dimensions[1], p_data1.dimensions[2]], units=p_data1.units) +#========================================================================= +# nonwoodyvegFunction +#========================================================================= +class get_nonwoodyvegFunction(Function): + key = 'get_nonwoodyveg' + + def __init__(self, p_pct_nat_pft, p_landfrac, p_landUse): + super(get_nonwoodyvegFunction, self).__init__( + p_pct_nat_pft,p_landfrac,p_landUse) + + def __getitem__(self, index): + p_pct_nat_pft = self.arguments[0][index] + p_landfrac = self.arguments[1][index] + p_landUse = self.arguments[2][index] + + pct_nat_pft = p_pct_nat_pft.data + landfrac = p_landfrac.data + landUse = p_landUse.data + + data = np.ma.zeros((p_pct_nat_pft.shape[0], 4, p_pct_nat_pft.shape[2], p_pct_nat_pft.shape[3])) + if index is None: + return data + + data[:, 0, :, :] = pct_nat_pft[:,12,:,:]+pct_nat_pft[:,13,:,:]+pct_nat_pft[:,14,:,:] + for i in range(p_pct_nat_pft.shape[2]): + for j in range(p_pct_nat_pft.shape[3]): + if landfrac[i,j] <= 1.0: + data[:, 1, i, j] = 1.0 + data[:, 2, i, j] = 0.0 + data[:, 3, i, j] = 0.0 + else: + data[:, 1, i, j] = 1e+20 + data[:, 2, i, j] = 1e+20 + data[:, 3, i, j] = 1e+20 + + data[data >= 1e+16] = 1e+20 + data = np.ma.masked_values(data, 1e+20) + + new_name = 'get_nonwoodyveg({})'.format( + p_pct_nat_pft.name) + return PhysArray(data, name=new_name, dimensions=[p_pct_nat_pft.dimensions[0], p_landUse.dimensions[0], p_pct_nat_pft.dimensions[1], p_pct_nat_pft.dimensions[2]], units=p_pct_nat_pft.units) + + #========================================================================= # expand_latlonFunction #========================================================================= From 2dc94105da3f40c1bc12288a96fc6797b083ad58 Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Mon, 8 Oct 2018 15:55:09 -0600 Subject: [PATCH 16/23] Tests and fixes to work with diagnostics w/ lon/extra dimensions --- source/pyconform/modules/dynvarmipdiags.py | 29 ++++++---- source/test/modules/dynvarmipdiagsTests.py | 63 ++++++++++++++++++++++ 2 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 source/test/modules/dynvarmipdiagsTests.py diff --git a/source/pyconform/modules/dynvarmipdiags.py b/source/pyconform/modules/dynvarmipdiags.py index 05012b7f..a286d33e 100644 --- a/source/pyconform/modules/dynvarmipdiags.py +++ b/source/pyconform/modules/dynvarmipdiags.py @@ -1,6 +1,8 @@ """ Basic functions for the DynVarMIP Diagnostics +NOTE: All of these functions return numpy arrays! + Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ @@ -29,7 +31,8 @@ def _init_wtem(time, levi, lat, wzm, vthzm, thzm): for itime in range(ntime): dthdp = deriv(levi100, thzm[itime, ...]) psieddy = vthzm[itime, ...] / dthdp - dpsidy = deriv(latrad, psieddy * coslat, axis=1) / (a * coslat) + tmp = numpy.einsum('ij...,j->ij...', psieddy, coslat) + dpsidy = numpy.einsum('ij...,j->ij...', deriv(latrad, tmp, axis=1), 1.0 / (a * coslat)) wtem[itime, ...] = _wzm[itime, ...] + dpsidy return wtem @@ -60,14 +63,15 @@ def utendvtem(time, levi, lat, uzm, vzm, vthzm, thzm): latrad = (lat / 180.0) * numpy.pi coslat = numpy.cos(latrad) - f = 2 * om * numpy.sin(latrad) - vtem = vtem(time, levi, lat, vzm, vthzm, thzm) + fshape = (1, len(lat)) + tuple([1] * (uzm.ndim - 3)) + f = 2 * om * numpy.sin(latrad).reshape(fshape) + _vtem = vtem(time, levi, lat, vzm, vthzm, thzm) utendvtem = numpy.zeros(uzm.shape, dtype='d') for itime in range(ntime): ucos = numpy.einsum('ij...,j->ij...', uzm[itime, ...], coslat) dudphi = deriv(latrad, ucos, axis=1) / a - utendvtem[itime, ...] = vtem[itime, ...] * (f - dudphi) + utendvtem[itime, ...] = _vtem[itime, ...] * (f - dudphi) return utendvtem @@ -96,7 +100,7 @@ def _init_epfy(time, levi, lat, uzm, uvzm, vthzm, thzm): dthdp = deriv(levi100, thzm[itime, ...]) dudp = deriv(levi100, uzm[itime, ...]) psieddy = vthzm[itime, ...] / dthdp - epfy[itime, ...] = a * coslat * (dudp * psieddy - uvzm[itime, ...]) + epfy[itime, ...] = a * numpy.einsum('j,ij...->ij...', coslat, (dudp * psieddy - uvzm[itime, ...])) return epfy @@ -112,7 +116,8 @@ def _init_epfz(time, levi, lat, uzm, uwzm, vthzm, thzm): latrad = (lat / 180.0) * numpy.pi coslat = numpy.cos(latrad) - f = 2 * om * numpy.sin(latrad) + fshape = (1, len(lat)) + tuple([1] * (uzm.ndim - 3)) + f = 2 * om * numpy.sin(latrad).reshape(fshape) levi100 = 100.0 * levi _uwzm = -1.0 * numpy.einsum('ij...,j->ij...', uwzm, levi100) / H @@ -123,7 +128,7 @@ def _init_epfz(time, levi, lat, uzm, uwzm, vthzm, thzm): dudphi = deriv(latrad, ucos, axis=1) / a dthdp = deriv(levi100, thzm[itime, ...]) psieddy = vthzm[itime, ...] / dthdp - epfz[itime, ...] = a * coslat * (((f - dudphi) * psieddy) - _uwzm[itime, ...]) + epfz[itime, ...] = a * numpy.einsum('j,ij...->ij...', coslat, (f - dudphi) * psieddy - _uwzm[itime, ...]) return epfz @@ -139,15 +144,17 @@ def utendepfd(time, levi, lat, uzm, uvzm, uwzm, vthzm, thzm): latrad = (lat / 180.0) * numpy.pi coslat = numpy.cos(latrad) levi100 = 100.0 * levi + iacoslat = 1.0 / (a * coslat) epfy = _init_epfy(time, levi, lat, uzm, uvzm, vthzm, thzm) epfz = _init_epfz(time, levi, lat, uzm, uwzm, vthzm, thzm) utendepfd = numpy.zeros(uzm.shape, dtype='d') for itime in range(ntime): - depfydphi = deriv(latrad, epfy[itime, ...] * coslat, axis=1) / (a * coslat) + tmp = numpy.einsum('ij...,j->ij...', epfy[itime, ...], coslat) + depfydphi = numpy.einsum('ij...,j->ij...', deriv(latrad, tmp, axis=1), iacoslat) depfzdp = deriv(levi100, epfz[itime, ...]) - utendepfd[itime, ...] = (depfydphi + depfzdp) / (a * coslat) + utendepfd[itime, ...] = numpy.einsum('ij...,j->ij...', depfydphi + depfzdp, iacoslat) return utendepfd @@ -171,6 +178,8 @@ def psitem(time, levi, lat, vzm, vthzm, thzm): vzmwithzero[1:, ...] = vzm[itime, ...] for ilev in range(1, nlevi + 1): result = int_tabulated(levi100withzero[0:ilev + 1], vzmwithzero[0:ilev + 1, ...]) - psitem[itime, ilev - 1, ...] = ((2 * numpy.pi * a * coslat) / g0) * (result - psieddy[ilev - 1, ...]) + tmp1 = (2 * numpy.pi * a * coslat) / g0 + tmp2 = result - psieddy[ilev - 1, ...] + psitem[itime, ilev - 1, ...] = numpy.einsum('i,i...->i...', tmp1, tmp2) return psitem diff --git a/source/test/modules/dynvarmipdiagsTests.py b/source/test/modules/dynvarmipdiagsTests.py new file mode 100644 index 00000000..d867f569 --- /dev/null +++ b/source/test/modules/dynvarmipdiagsTests.py @@ -0,0 +1,63 @@ +""" +DynVarMIP Diagnostics Functions Unit Tests + +Copyright 2017-2018, University Corporation for Atmospheric Research +LICENSE: See the LICENSE.rst file for details +""" + +import unittest +import numpy as np +import pyconform.modules.dynvarmipdiags as dvmd + + +class Test(unittest.TestCase): + + def setUp(self): + self.ntime = 5 + self.nlevi = 4 + self.nlat = 3 + self.nlon = 2 + + self.shape = (self.ntime, self.nlevi, self.nlat, self.nlon) + self.size = np.prod(self.shape) + + self.time = np.arange(self.ntime) * 30.0 + self.levi = np.arange(self.nlevi) * 100.0 + 1.0 + self.lat = (2.0 * np.arange(self.nlat) / (self.nlat - 1) - 1.0) * 80.0 + + self.uzm = np.random.rand(*self.shape) + self.vzm = np.random.rand(*self.shape) + self.wzm = np.random.rand(*self.shape) + self.uvzm = np.random.rand(*self.shape) + self.uwzm = np.random.rand(*self.shape) + self.thzm = np.random.rand(*self.shape) + self.vthzm = np.random.rand(*self.shape) + + def test_vtem(self): + dvmd.vtem(self.time, self.levi, self.lat, self.vzm, self.vthzm, self.thzm) + + def test_wtem(self): + dvmd.wtem(self.time, self.levi, self.lat, self.wzm, self.vthzm, self.thzm) + + def test_utendvtem(self): + dvmd.utendvtem(self.time, self.levi, self.lat, self.uzm, self.vzm, self.vthzm, self.thzm) + + def test_utendwtem(self): + dvmd.utendwtem(self.time, self.levi, self.lat, self.uzm, self.wzm, self.vthzm, self.thzm) + + def test_epfy(self): + dvmd.epfy(self.time, self.levi, self.lat, self.uzm, self.uvzm, self.vthzm, self.thzm) + + def test_epfz(self): + dvmd.epfz(self.time, self.levi, self.lat, self.uzm, self.uwzm, self.vthzm, self.thzm) + + def test_utendepfd(self): + dvmd.utendepfd(self.time, self.levi, self.lat, self.uzm, self.uvzm, self.uwzm, self.vthzm, self.thzm) + + def test_psitem(self): + dvmd.psitem(self.time, self.levi, self.lat, self.vzm, self.vthzm, self.thzm) + + +if __name__ == "__main__": + #import sys;sys.argv = ['', 'Test.testName'] + unittest.main() From e582b98a3a71c11e9256d5c42485fdba3fbacadb Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Mon, 8 Oct 2018 15:56:07 -0600 Subject: [PATCH 17/23] Update copyright date --- source/test/physarrayTests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/test/physarrayTests.py b/source/test/physarrayTests.py index ce32997b..5960f764 100644 --- a/source/test/physarrayTests.py +++ b/source/test/physarrayTests.py @@ -1,7 +1,7 @@ """ Physical Array Unit Tests -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ From 705bb2f2ba0dd2cf4ca33acf366cb90130fd0234 Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Mon, 8 Oct 2018 15:58:06 -0600 Subject: [PATCH 18/23] Update copyright dates --- CHANGES.rst | 2 +- docs/_modules/index.html | 2 +- docs/_modules/pyconform/dataflow.html | 4 ++-- docs/_modules/pyconform/datasets.html | 4 ++-- docs/_modules/pyconform/flownodes.html | 4 ++-- docs/_modules/pyconform/functions.html | 4 ++-- docs/_modules/pyconform/indexing.html | 4 ++-- docs/_modules/pyconform/parsing.html | 4 ++-- docs/_modules/pyconform/physarray.html | 4 ++-- docs/changelog.html | 4 ++-- docs/dataflow.html | 4 ++-- docs/datasets.html | 4 ++-- docs/flownodes.html | 4 ++-- docs/functions.html | 4 ++-- docs/genindex.html | 2 +- docs/index.html | 2 +- docs/indexing.html | 4 ++-- docs/license.html | 2 +- docs/manual.html | 2 +- docs/parsing.html | 4 ++-- docs/physarray.html | 4 ++-- docs/py-modindex.html | 2 +- docs/pyconform.html | 2 +- docs/readme.html | 2 +- docs/search.html | 2 +- examples/CESM/CMIP5/utilities/anlz_dirpatterns.py | 2 +- examples/CESM/CMIP5/utilities/anlz_stdfile.py | 2 +- examples/CESM/CMIP5/utilities/anlz_varmeta.py | 2 +- examples/CESM/CMIP5/utilities/insert_defs.py | 2 +- examples/CESM/CMIP5/utilities/make_dirpatterns.py | 2 +- examples/CESM/CMIP5/utilities/make_stdfile.py | 2 +- examples/CESM/CMIP5/utilities/make_varmeta.py | 2 +- examples/CESM/CMIP5/utilities/ncdiff.py | 2 +- setup.py | 2 +- source/pyconform/climIO.py | 2 +- source/pyconform/datasets.py | 2 +- source/pyconform/flownodes.py | 2 +- source/pyconform/functions.py | 2 +- source/pyconform/indexing.py | 2 +- source/pyconform/mapdates.py | 2 +- source/pyconform/miptableparser.py | 2 +- source/pyconform/physarray.py | 2 +- source/test/climIOTests.py | 2 +- source/test/dataflowTests.py | 2 +- source/test/datasetsTests.py | 2 +- source/test/flownodesTests.py | 2 +- source/test/functionsTests.py | 2 +- source/test/indexingTests.py | 2 +- source/test/makeTestData.py | 2 +- source/test/mapdatesTests.py | 2 +- source/test/testutils.py | 2 +- 51 files changed, 66 insertions(+), 66 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 1ddc1dd9..9c0b3253 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,7 @@ PyConform ChangeLog =================== -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research See the LICENSE.rst file for details VERSION 0.0.1 diff --git a/docs/_modules/index.html b/docs/_modules/index.html index e96ef275..38f12c3c 100644 --- a/docs/_modules/index.html +++ b/docs/_modules/index.html @@ -178,7 +178,7 @@

All modules for which code is available

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/_modules/pyconform/dataflow.html b/docs/_modules/pyconform/dataflow.html index 75ab2915..1bc35852 100644 --- a/docs/_modules/pyconform/dataflow.html +++ b/docs/_modules/pyconform/dataflow.html @@ -174,7 +174,7 @@

Source code for pyconform.dataflow

 The action associated with each node is not performed until the data is
 "requested" with the __getitem__ interface, via Node[key].  
 
-Copyright 2017, University Corporation for Atmospheric Research
+Copyright 2017-2018, University Corporation for Atmospheric Research
 LICENSE: See the LICENSE.rst file for details
 """
 
@@ -534,7 +534,7 @@ 

Source code for pyconform.dataflow

 
   

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/_modules/pyconform/datasets.html b/docs/_modules/pyconform/datasets.html index 7fa4a3f8..fc6bdfe2 100644 --- a/docs/_modules/pyconform/datasets.html +++ b/docs/_modules/pyconform/datasets.html @@ -166,7 +166,7 @@

Source code for pyconform.datasets

 This file contains the interface classes to the input and output multi-file
 datasets.
 
-Copyright 2017, University Corporation for Atmospheric Research
+Copyright 2017-2018, University Corporation for Atmospheric Research
 LICENSE: See the LICENSE.rst file for details
 """
 
@@ -1012,7 +1012,7 @@ 

Source code for pyconform.datasets

 
   

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/_modules/pyconform/flownodes.html b/docs/_modules/pyconform/flownodes.html index e67288b7..888c7009 100644 --- a/docs/_modules/pyconform/flownodes.html +++ b/docs/_modules/pyconform/flownodes.html @@ -165,7 +165,7 @@

Source code for pyconform.flownodes

 
 This module contains the classes and functions needed to define nodes in Data Flows.
 
-Copyright 2017, University Corporation for Atmospheric Research
+Copyright 2017-2018, University Corporation for Atmospheric Research
 LICENSE: See the LICENSE.rst file for details
 """
 
@@ -1081,7 +1081,7 @@ 

Source code for pyconform.flownodes

 
   

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/_modules/pyconform/functions.html b/docs/_modules/pyconform/functions.html index 5352a75e..10a5d107 100644 --- a/docs/_modules/pyconform/functions.html +++ b/docs/_modules/pyconform/functions.html @@ -163,7 +163,7 @@

Source code for pyconform.functions

 """
 Functions for FunctionEvaluator Actions
 
-Copyright 2017, University Corporation for Atmospheric Research
+Copyright 2017-2018, University Corporation for Atmospheric Research
 LICENSE: See the LICENSE.rst file for details
 """
 
@@ -602,7 +602,7 @@ 

Source code for pyconform.functions

 
   

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/_modules/pyconform/indexing.html b/docs/_modules/pyconform/indexing.html index 32c29805..7a34eb5b 100644 --- a/docs/_modules/pyconform/indexing.html +++ b/docs/_modules/pyconform/indexing.html @@ -191,7 +191,7 @@

Source code for pyconform.indexing

 
 ----------------------------------------------------------------------------------------------------
 
-Copyright 2017, University Corporation for Atmospheric Research
+Copyright 2017-2018, University Corporation for Atmospheric Research
 LICENSE: See the LICENSE.rst file for details
 """
 
@@ -348,7 +348,7 @@ 

Source code for pyconform.indexing

 
   

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/_modules/pyconform/parsing.html b/docs/_modules/pyconform/parsing.html index e01df9e4..3152214e 100644 --- a/docs/_modules/pyconform/parsing.html +++ b/docs/_modules/pyconform/parsing.html @@ -166,7 +166,7 @@

Source code for pyconform.parsing

 This module defines the necessary elements to parse a string variable definition
 into the recognized elements that are used to construct an Operation Graph.
 
-Copyright 2017, University Corporation for Atmospheric Research
+Copyright 2017-2018, University Corporation for Atmospheric Research
 LICENSE: See the LICENSE.rst file for details
 """
 
@@ -360,7 +360,7 @@ 

Source code for pyconform.parsing

 
   

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/_modules/pyconform/physarray.html b/docs/_modules/pyconform/physarray.html index 13dba733..3bac1cf6 100644 --- a/docs/_modules/pyconform/physarray.html +++ b/docs/_modules/pyconform/physarray.html @@ -168,7 +168,7 @@

Source code for pyconform.physarray

 its data the units associated with the data, the dimensions associated with each axis of
 the data.
 
-Copyright 2017, University Corporation for Atmospheric Research
+Copyright 2017-2018, University Corporation for Atmospheric Research
 LICENSE: See the LICENSE.rst file for details
 """
 
@@ -900,7 +900,7 @@ 

Source code for pyconform.physarray

 
   

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/changelog.html b/docs/changelog.html index 529955e9..4028b58a 100644 --- a/docs/changelog.html +++ b/docs/changelog.html @@ -169,7 +169,7 @@

PyConform ChangeLog

-

Copyright 2017, University Corporation for Atmospheric Research +

Copyright 2017-2018, University Corporation for Atmospheric Research See the LICENSE.rst file for details

diff --git a/docs/dataflow.html b/docs/dataflow.html index 3caff815..52b8d76f 100644 --- a/docs/dataflow.html +++ b/docs/dataflow.html @@ -186,7 +186,7 @@ graph edges is assumed to a Numpy.NDArray-like object.

The action associated with each node is not performed until the data is “requested” with the __getitem__ interface, via Node[key].

-

Copyright 2017, University Corporation for Atmospheric Research +

Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details

@@ -259,7 +259,7 @@

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/datasets.html b/docs/datasets.html index 2b50e78f..b7697707 100644 --- a/docs/datasets.html +++ b/docs/datasets.html @@ -180,7 +180,7 @@

DatasetDesc Interface Class

This file contains the interface classes to the input and output multi-file datasets.

-

Copyright 2017, University Corporation for Atmospheric Research +

Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details

@@ -525,7 +525,7 @@

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/flownodes.html b/docs/flownodes.html index e5172e78..e3be776a 100644 --- a/docs/flownodes.html +++ b/docs/flownodes.html @@ -179,7 +179,7 @@

pyconform.flownodes

Data Flow Node Classes and Functions

This module contains the classes and functions needed to define nodes in Data Flows.

-

Copyright 2017, University Corporation for Atmospheric Research +

Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details

@@ -400,7 +400,7 @@

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/functions.html b/docs/functions.html index 3f8ecf21..fab22e0a 100644 --- a/docs/functions.html +++ b/docs/functions.html @@ -178,7 +178,7 @@

pyconform.functions

Functions for FunctionEvaluator Actions

-

Copyright 2017, University Corporation for Atmospheric Research +

Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details

@@ -444,7 +444,7 @@

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/genindex.html b/docs/genindex.html index d8811d6b..f07a3820 100644 --- a/docs/genindex.html +++ b/docs/genindex.html @@ -683,7 +683,7 @@

W

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/index.html b/docs/index.html index e92a6c16..13eeaaa8 100644 --- a/docs/index.html +++ b/docs/index.html @@ -227,7 +227,7 @@

Indices and tables

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/indexing.html b/docs/indexing.html index 436a8723..e52bcbb8 100644 --- a/docs/indexing.html +++ b/docs/indexing.html @@ -202,7 +202,7 @@ reducing the amount of data read, as well as limiting the possibility of overrunning memory with a large read.


-

Copyright 2017, University Corporation for Atmospheric Research +

Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details

@@ -277,7 +277,7 @@

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/license.html b/docs/license.html index 0a2d1f69..6c6265df 100644 --- a/docs/license.html +++ b/docs/license.html @@ -240,7 +240,7 @@

Contributor License Agreement

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/manual.html b/docs/manual.html index fb7f9b7c..d171a4a1 100644 --- a/docs/manual.html +++ b/docs/manual.html @@ -836,7 +836,7 @@

I/O Nodes

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/parsing.html b/docs/parsing.html index 066a4c9a..5b4e9358 100644 --- a/docs/parsing.html +++ b/docs/parsing.html @@ -180,7 +180,7 @@

Parsing Module

This module defines the necessary elements to parse a string variable definition into the recognized elements that are used to construct an Operation Graph.

-

Copyright 2017, University Corporation for Atmospheric Research +

Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details

@@ -239,7 +239,7 @@

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/physarray.html b/docs/physarray.html index c263b092..4f51864f 100644 --- a/docs/physarray.html +++ b/docs/physarray.html @@ -182,7 +182,7 @@ edges of a Data Flow graph. It is a subclass of the Numpy MaskedArray, and carries with its data the units associated with the data, the dimensions associated with each axis of the data.

-

Copyright 2017, University Corporation for Atmospheric Research +

Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details

@@ -414,7 +414,7 @@

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/py-modindex.html b/docs/py-modindex.html index 90731873..c79755d8 100644 --- a/docs/py-modindex.html +++ b/docs/py-modindex.html @@ -226,7 +226,7 @@

Python Module Index

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/pyconform.html b/docs/pyconform.html index 168fbcab..bcd0be57 100644 --- a/docs/pyconform.html +++ b/docs/pyconform.html @@ -228,7 +228,7 @@

Modules

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/readme.html b/docs/readme.html index 6bf13e78..2c979e88 100644 --- a/docs/readme.html +++ b/docs/readme.html @@ -315,7 +315,7 @@

Instructions & Use

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/docs/search.html b/docs/search.html index 7d719c9f..b5ffabec 100644 --- a/docs/search.html +++ b/docs/search.html @@ -180,7 +180,7 @@

- © Copyright 2017, University Corporation for Atmospheric Research. + © Copyright 2017-2018, University Corporation for Atmospheric Research.

diff --git a/examples/CESM/CMIP5/utilities/anlz_dirpatterns.py b/examples/CESM/CMIP5/utilities/anlz_dirpatterns.py index 5474cd04..ec94f98c 100755 --- a/examples/CESM/CMIP5/utilities/anlz_dirpatterns.py +++ b/examples/CESM/CMIP5/utilities/anlz_dirpatterns.py @@ -4,7 +4,7 @@ Command-Line Utility to analyze data directory patterns in CMIP5 data -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/examples/CESM/CMIP5/utilities/anlz_stdfile.py b/examples/CESM/CMIP5/utilities/anlz_stdfile.py index f7de5007..1991e4f1 100755 --- a/examples/CESM/CMIP5/utilities/anlz_stdfile.py +++ b/examples/CESM/CMIP5/utilities/anlz_stdfile.py @@ -4,7 +4,7 @@ Command-Line Utility to show attributes/definitions/etc in a standardization file -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/examples/CESM/CMIP5/utilities/anlz_varmeta.py b/examples/CESM/CMIP5/utilities/anlz_varmeta.py index 565de05b..b842c83e 100755 --- a/examples/CESM/CMIP5/utilities/anlz_varmeta.py +++ b/examples/CESM/CMIP5/utilities/anlz_varmeta.py @@ -4,7 +4,7 @@ Command-Line Utility to analyze variable metadata in CMIP5 data -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/examples/CESM/CMIP5/utilities/insert_defs.py b/examples/CESM/CMIP5/utilities/insert_defs.py index ed97b817..00a79252 100755 --- a/examples/CESM/CMIP5/utilities/insert_defs.py +++ b/examples/CESM/CMIP5/utilities/insert_defs.py @@ -4,7 +4,7 @@ Command-Line Utility to push definitions into a standardization file -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/examples/CESM/CMIP5/utilities/make_dirpatterns.py b/examples/CESM/CMIP5/utilities/make_dirpatterns.py index 15cb8c42..3f386b10 100755 --- a/examples/CESM/CMIP5/utilities/make_dirpatterns.py +++ b/examples/CESM/CMIP5/utilities/make_dirpatterns.py @@ -4,7 +4,7 @@ Command-Line Utility to write all CMIP5 file directory patterns to a file -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/examples/CESM/CMIP5/utilities/make_stdfile.py b/examples/CESM/CMIP5/utilities/make_stdfile.py index d44c90c1..34da9663 100755 --- a/examples/CESM/CMIP5/utilities/make_stdfile.py +++ b/examples/CESM/CMIP5/utilities/make_stdfile.py @@ -4,7 +4,7 @@ Command-Line Utility to make a standardization file from a set of "correct" output files -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/examples/CESM/CMIP5/utilities/make_varmeta.py b/examples/CESM/CMIP5/utilities/make_varmeta.py index 3c30cf89..37963b81 100755 --- a/examples/CESM/CMIP5/utilities/make_varmeta.py +++ b/examples/CESM/CMIP5/utilities/make_varmeta.py @@ -4,7 +4,7 @@ Command-Line Utility to extract variable attributes in CMIP5 data -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/examples/CESM/CMIP5/utilities/ncdiff.py b/examples/CESM/CMIP5/utilities/ncdiff.py index 7e92df96..ad519872 100755 --- a/examples/CESM/CMIP5/utilities/ncdiff.py +++ b/examples/CESM/CMIP5/utilities/ncdiff.py @@ -4,7 +4,7 @@ Command-Line Utility to show the differences between two NetCDF files -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/setup.py b/setup.py index ac741e37..5bd86bfa 100755 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ """ PyConform -- Setup Script -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research See the LICENSE.rst file for details """ diff --git a/source/pyconform/climIO.py b/source/pyconform/climIO.py index 983b73cd..56de0e1a 100644 --- a/source/pyconform/climIO.py +++ b/source/pyconform/climIO.py @@ -4,7 +4,7 @@ This defines the facade for netCDF I/O operations, either with netCDF4 or PyNIO. -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/source/pyconform/datasets.py b/source/pyconform/datasets.py index d478163b..d8915f2f 100644 --- a/source/pyconform/datasets.py +++ b/source/pyconform/datasets.py @@ -4,7 +4,7 @@ This file contains the interface classes to the input and output multi-file datasets. -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/source/pyconform/flownodes.py b/source/pyconform/flownodes.py index 3d017fd7..4337da73 100644 --- a/source/pyconform/flownodes.py +++ b/source/pyconform/flownodes.py @@ -3,7 +3,7 @@ This module contains the classes and functions needed to define nodes in Data Flows. -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/source/pyconform/functions.py b/source/pyconform/functions.py index 93f31175..1270ae55 100644 --- a/source/pyconform/functions.py +++ b/source/pyconform/functions.py @@ -1,7 +1,7 @@ """ Functions for FunctionEvaluator Actions -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/source/pyconform/indexing.py b/source/pyconform/indexing.py index e2b4157d..883c1cca 100644 --- a/source/pyconform/indexing.py +++ b/source/pyconform/indexing.py @@ -29,7 +29,7 @@ ---------------------------------------------------------------------------------------------------- -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/source/pyconform/mapdates.py b/source/pyconform/mapdates.py index 7d14703a..0c26cafe 100644 --- a/source/pyconform/mapdates.py +++ b/source/pyconform/mapdates.py @@ -4,7 +4,7 @@ This evaluates the time slices in the file(s) to define the time period between steps and verifies consistancy and sequential steps between files. -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/source/pyconform/miptableparser.py b/source/pyconform/miptableparser.py index cb3a353c..381c2d1f 100644 --- a/source/pyconform/miptableparser.py +++ b/source/pyconform/miptableparser.py @@ -1,7 +1,7 @@ """ MIP Table Parser -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/source/pyconform/physarray.py b/source/pyconform/physarray.py index 1bd6c0de..ccf5dab6 100644 --- a/source/pyconform/physarray.py +++ b/source/pyconform/physarray.py @@ -6,7 +6,7 @@ its data the units associated with the data, the dimensions associated with each axis of the data. -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/source/test/climIOTests.py b/source/test/climIOTests.py index eef524a9..a81d028f 100644 --- a/source/test/climIOTests.py +++ b/source/test/climIOTests.py @@ -1,7 +1,7 @@ """ Tests for the climIO class -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research See the LICENSE.rst file for details """ diff --git a/source/test/dataflowTests.py b/source/test/dataflowTests.py index e424b5cf..ac18d4a5 100644 --- a/source/test/dataflowTests.py +++ b/source/test/dataflowTests.py @@ -1,7 +1,7 @@ """ DataFlow Unit Tests -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/source/test/datasetsTests.py b/source/test/datasetsTests.py index 964a99d8..77fbd0c0 100644 --- a/source/test/datasetsTests.py +++ b/source/test/datasetsTests.py @@ -1,7 +1,7 @@ """ DatasetDesc Unit Tests -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/source/test/flownodesTests.py b/source/test/flownodesTests.py index 46a68b11..0e793a2a 100644 --- a/source/test/flownodesTests.py +++ b/source/test/flownodesTests.py @@ -1,7 +1,7 @@ """ FlowNode Unit Tests -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/source/test/functionsTests.py b/source/test/functionsTests.py index 762261f4..2f656b48 100644 --- a/source/test/functionsTests.py +++ b/source/test/functionsTests.py @@ -1,7 +1,7 @@ """ Functions Unit Tests -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/source/test/indexingTests.py b/source/test/indexingTests.py index f83c66a8..e7ff8f5c 100644 --- a/source/test/indexingTests.py +++ b/source/test/indexingTests.py @@ -1,7 +1,7 @@ """ Indexing Unit Tests -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/source/test/makeTestData.py b/source/test/makeTestData.py index 0f861bd0..7f69cac3 100644 --- a/source/test/makeTestData.py +++ b/source/test/makeTestData.py @@ -1,7 +1,7 @@ """ Make Testing Data -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/source/test/mapdatesTests.py b/source/test/mapdatesTests.py index b980fe83..481d1727 100644 --- a/source/test/mapdatesTests.py +++ b/source/test/mapdatesTests.py @@ -1,7 +1,7 @@ """ MapDates Unit Tests -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ diff --git a/source/test/testutils.py b/source/test/testutils.py index cd11cf6d..da707205 100644 --- a/source/test/testutils.py +++ b/source/test/testutils.py @@ -1,7 +1,7 @@ """ Unit Testing Utilities -Copyright 2017, University Corporation for Atmospheric Research +Copyright 2017-2018, University Corporation for Atmospheric Research LICENSE: See the LICENSE.rst file for details """ From 2735f71f08131ab0ce3a6909015288ab556be25b Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Mon, 8 Oct 2018 16:39:45 -0600 Subject: [PATCH 19/23] Adding knowledge of units and attributes to functions Also making each function act on ndarrays only, instead of PhysArrays. --- .../pyconform/modules/dynvarmipfunctions.py | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/source/pyconform/modules/dynvarmipfunctions.py b/source/pyconform/modules/dynvarmipfunctions.py index 0a37283c..831e621d 100644 --- a/source/pyconform/modules/dynvarmipfunctions.py +++ b/source/pyconform/modules/dynvarmipfunctions.py @@ -9,6 +9,7 @@ from pyconform.functions import Function import pyconform.modules.dynvarmipdiags as dvmd +import numpy as np #========================================================================= @@ -20,13 +21,16 @@ class DynVarMIPFunction(Function): def __init__(self, time, lev, lat, *args): super(DynVarMIPFunction, self).__init__(time, lev, lat, *args) self._dynvarmip_func = None + self._units = None + self._positive = None def __getitem__(self, index): - args = [arg[index] for arg in self.arguments] + args = [np.ma.asarray(arg[index]) for arg in self.arguments] arg_names = ','.join([arg.name for arg in args]) data = self._dynvarmip_func(*args) name = '{}({})'.format(self.key, arg_names) - return PhysArray(data, name=name, dimensions=args[-1].dimensions) + return PhysArray(data, name=name, dimensions=args[-1].dimensions, + units=self._units, positive=self._positive) #========================================================================= @@ -39,6 +43,8 @@ def __init__(self, time, lev, lat, wzm, vthzm, thzm): super(wtemDynVarMIPFunction, self).__init__( time, lev, lat, wzm, vthzm, thzm) self._dynvarmip_func = dvmd.wtem + self._units = 'm s-1' + self._positive = None #========================================================================= @@ -51,6 +57,8 @@ def __init__(self, time, lev, lat, uzm, wzm, vthzm, thzm): super(utendwtemDynVarMIPFunction, self).__init__( time, lev, lat, uzm, wzm, vthzm, thzm) self._dynvarmip_func = dvmd.utendwtem + self._units = 'm s-1 d-1' + self._positive = None #========================================================================= @@ -63,6 +71,8 @@ def __init__(self, time, lev, lat, vzm, vthzm, thzm): super(vtemDynVarMIPFunction, self).__init__( time, lev, lat, vzm, vthzm, thzm) self._dynvarmip_func = dvmd.vtem + self._units = 'm s-1' + self._positive = None #========================================================================= @@ -75,6 +85,8 @@ def __init__(self, time, lev, lat, uzm, vzm, vthzm, thzm): super(utendvtemDynVarMIPFunction, self).__init__( time, lev, lat, uzm, vzm, vthzm, thzm) self._dynvarmip_func = dvmd.utendvtem + self._units = 'm s-1 d-1' + self._positive = None #========================================================================= @@ -87,6 +99,8 @@ def __init__(self, time, lev, lat, uzm, uvzm, vthzm, thzm): super(epfyDynVarMIPFunction, self).__init__( time, lev, lat, uzm, uvzm, vthzm, thzm) self._dynvarmip_func = dvmd.epfy + self._units = 'm3 s-2' + self._positive = None #========================================================================= @@ -99,6 +113,8 @@ def __init__(self, time, lev, lat, uzm, uwzm, vthzm, thzm): super(epfzDynVarMIPFunction, self).__init__( time, lev, lat, uzm, uwzm, vthzm, thzm) self._dynvarmip_func = dvmd.epfz + self._units = 'm3 s-2' + self._positive = 'up' #========================================================================= @@ -111,6 +127,8 @@ def __init__(self, time, lev, lat, uzm, uvzm, uwzm, vthzm, thzm): super(utendepfdDynVarMIPFunction, self).__init__( time, lev, lat, uzm, uvzm, uwzm, vthzm, thzm) self._dynvarmip_func = dvmd.utendepfd + self._units = 'm s-2' + self._positive = None #========================================================================= @@ -123,3 +141,5 @@ def __init__(self, time, lev, lat, vzm, vthzm, thzm): super(psitemDynVarMIPFunction, self).__init__( time, lev, lat, vzm, vthzm, thzm) self._dynvarmip_func = dvmd.psitem + self._units = 'kg s-1' + self._positive = None From 5f58b1d028d44723a870aae244484e6a2ffefa23 Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Mon, 15 Oct 2018 09:17:25 -0600 Subject: [PATCH 20/23] Adding modules to the install scripts --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 5bd86bfa..b080190c 100755 --- a/setup.py +++ b/setup.py @@ -18,8 +18,8 @@ url='https://github.com/NCAR/PyConform', download_url='https://github.com/NCAR/PyConform/tarball/v' + __version__, license='https://github.com/NCAR/PyConform/blob/master/LICENSE.rst', - packages=['pyconform'], - package_dir={'pyconform': 'source/pyconform'}, + packages=['pyconform', 'pyconform.modules'], + package_dir={'pyconform': 'source/pyconform', 'pyconform.modules': 'source/pyconform/modules'}, package_data={'pyconform': ['LICENSE.rst']}, scripts=['scripts/iconform', 'scripts/xconform', 'scripts/vardeps'], install_requires=['asaptools', 'netCDF4', 'ply'] From be534a25d3e0ede7a74d1ea1eab7ffa1f60fd194 Mon Sep 17 00:00:00 2001 From: Kevin Paul Date: Mon, 15 Oct 2018 16:24:40 -0600 Subject: [PATCH 21/23] Formatting only --- source/pyconform/functions.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/source/pyconform/functions.py b/source/pyconform/functions.py index 1270ae55..b35eb7ff 100644 --- a/source/pyconform/functions.py +++ b/source/pyconform/functions.py @@ -11,19 +11,17 @@ from cf_units import Unit import numpy as np + #========================================================================= # is_constant - Determine if an argument is a constant (number or string) #========================================================================= - - def is_constant(arg): return isinstance(arg, (basestring, float, int)) or arg is None + #========================================================================= # Find a function or operator based on key and number of arguments #========================================================================= - - def find(key, numargs=None): try: fop = find_operator(key, numargs=numargs) @@ -221,6 +219,7 @@ def __getitem__(self, index): '*': {2: MultiplicationOperator}, '/': {2: DivisionOperator}} + ########################################################################## ##### FUNCTIONS ########################################################## ########################################################################## @@ -228,10 +227,9 @@ def __getitem__(self, index): #========================================================================= # Recursively return all subclasses of a given class #========================================================================= - - def _all_subclasses_(cls): - return cls.__subclasses__() + [c for s in cls.__subclasses__() for c in _all_subclasses_(s)] + return cls.__subclasses__() + [c for s in cls.__subclasses__() + for c in _all_subclasses_(s)] #========================================================================= @@ -378,13 +376,16 @@ def __getitem__(self, index): dimensions = self.arguments[1:] indims = [] if index is None: - return PhysArray(np.zeros((0, 0, 0)), dimensions=[data.dimensions[0], data.dimensions[2], data.dimensions[3]]) + return PhysArray(np.zeros((0, 0, 0)), dimensions=[data.dimensions[0], + data.dimensions[2], + data.dimensions[3]]) for d in dimensions: if d in data.dimensions: indims.append(data.dimensions.index(d)) new_name = 'min({},{})'.format(data.name, dimensions) m = np.amin(data, axis=indims[0]) - return PhysArray(m, name=new_name, positive=data.positive, units=data.units, dimensions=[data.dimensions[0], data.dimensions[2], data.dimensions[3]]) + return PhysArray(m, name=new_name, positive=data.positive, units=data.units, + dimensions=[data.dimensions[0], data.dimensions[2], data.dimensions[3]]) #========================================================================= @@ -407,13 +408,16 @@ def __getitem__(self, index): dimensions = self.arguments[1:] indims = [] if index is None: - return PhysArray(np.zeros((0, 0, 0)), units=data.units, dimensions=[data.dimensions[0], data.dimensions[2], data.dimensions[3]]) + return PhysArray(np.zeros((0, 0, 0)), units=data.units, dimensions=[data.dimensions[0], + data.dimensions[2], + data.dimensions[3]]) for d in dimensions: if d in data.dimensions: indims.append(data.dimensions.index(d)) new_name = 'max({},{})'.format(data.name, dimensions[0]) m = np.amax(data, axis=indims[0]) - return PhysArray(m, name=new_name, positive=data.positive, units=data.units, dimensions=[data.dimensions[0], data.dimensions[2], data.dimensions[3]]) + return PhysArray(m, name=new_name, positive=data.positive, units=data.units, + dimensions=[data.dimensions[0], data.dimensions[2], data.dimensions[3]]) #========================================================================= @@ -576,4 +580,4 @@ def __getitem__(self, index): new_dims = list(data.dimensions) dlen = min(len(dims), len(new_dims)) new_dims[:dlen] = dims[:dlen] - return PhysArray(data, name=new_name, dimensions=new_dims) \ No newline at end of file + return PhysArray(data, name=new_name, dimensions=new_dims) From dee4c2d96ae82710bc669bdcc9ad1a184c156e98 Mon Sep 17 00:00:00 2001 From: sherimickelson Date: Wed, 24 Oct 2018 13:22:44 -0600 Subject: [PATCH 22/23] Change to use the altlabels for dims in the cmip6 data request instead of the labels --- scripts/iconform | 37 ++++++++++++++++++++++++++---- source/pyconform/miptableparser.py | 4 +++- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/scripts/iconform b/scripts/iconform index 70325a0f..822f1bd2 100755 --- a/scripts/iconform +++ b/scripts/iconform @@ -151,9 +151,31 @@ def fill_missing_glob_attributes(attr, table, v, grids): elif "external_variables" in a: if "cell_measures" in v.keys(): if "areacello" in v["cell_measures"]: - attr["external_variables"] = "areacello" - elif "areacella" in v["cell_measures"]: - attr["external_variables"] = "areacella" + if "external_variables" not in attr.keys() or "FILL" in attr["external_variables"]: + attr["external_variables"] = "areacello" + else: + attr["external_variables"] = attr["external_variables"]+" areacello" + if "areacella" in v["cell_measures"]: + if "external_variables" not in attr.keys() or "FILL" in attr["external_variables"]: + attr["external_variables"] = "areacella" + else: + attr["external_variables"] = attr["external_variables"]+" areacella" + if "areacellr" in v["cell_measures"]: + if "external_variables" not in attr.keys() or "FILL" in attr["external_variables"]: + attr["external_variables"] = "areacellr" + else: + attr["external_variables"] = attr["external_variables"]+" areacellr" + if "areacellg" in v["cell_measures"]: + if "external_variables" not in attr.keys() or "FILL" in attr["external_variables"]: + attr["external_variables"] = "areacellg" + else: + attr["external_variables"] = attr["external_variables"]+" areacellg" + if "volcello" in v["cell_measures"]: + if "external_variables" not in attr.keys() or "FILL" in attr["external_variables"]: + attr["external_variables"] = "volcello" + else: + attr["external_variables"] = attr["external_variables"]+" volcello" + elif "frequency" in a: attr["frequency"] = v["frequency"] elif "realm" in a and "realm" in v.keys(): @@ -164,6 +186,10 @@ def fill_missing_glob_attributes(attr, table, v, grids): attr["tracking_id"] = "hdl:21.14100/"+str(uuid.uuid4()) elif "variable_id" in a and "variable_id" in v.keys(): attr["variable_id"] = v["variable_id"] + + if "FILL" in attr["external_variables"]: + attr.pop("external_variables") + if "branch_method" in attr.keys(): if "none" not in attr["branch_method"]: if "branch_time_in_child" in attr.keys(): @@ -364,7 +390,8 @@ def defineVar(v, varName, attr, table_info, definition, ig, experiment, out_dir) var['definition'] = v['requested'] if 'coordinates' in v.keys(): var["dimensions"] = list(reversed(v['coordinates'].split('|'))) - var["attributes"].pop('coordinates') + var["attributes"]['coordinates'] = ' '.join(list(reversed(v['coordinates'].split('|')))) + #var["attributes"].pop('coordinates') else: var["dimensions"] = [] @@ -517,7 +544,7 @@ def create_output(exp_dict, definitions, input_glob, attributes, output_path, ar with open(f, 'w') as outfile: json.dump(t, outfile, sort_keys=True, indent=4) else: - ignore = ['latitude','longitude','olevel','plev19','time','time1','alevhalf','ygre','xgre','vegtype','spectband','areacellg','alevel','xant','yant','rho','tau','plev3','gridlatitude','plev39','plev4','plev27','plev3','plev7h','plev7c','plev8','plev7h','sdepth','siline','basin','olevel','site','soilpools','snowdepth','snowband','vegtype'] + ignore = ['latitude','longitude','olevel','plev19','time','time1','alevhalf','ygre','xgre','vegtype','spectband','areacellg','alevel','xant','yant','rho','tau','plev3','gridlatitude','plev39','plev4','plev27','plev3','plev7h','plev7c','plev8','plev7h','sdepth','siline','basin','olevel','site','soilpools','snowdepth','snowband','vegtype','lat','lon','lev'] for n,t in TableSpec.iteritems(): for vn,var in t.iteritems(): if vn not in ignore: diff --git a/source/pyconform/miptableparser.py b/source/pyconform/miptableparser.py index 381c2d1f..ad4d9811 100644 --- a/source/pyconform/miptableparser.py +++ b/source/pyconform/miptableparser.py @@ -480,7 +480,7 @@ def parse_table(self,exp,mips,tables,v_list,table_var_fields,table_axes_fields,t if hasattr(s_var,'coords'): #if len(s_var.coords)>0: if isinstance(s_var.cids,list) : - var['coordinate'] = dq.inx.uid[s_var.cids[0]].label + var['coordinates'] = dq.inx.uid[s_var.cids[0]].label c = dq.inx.uid[s_var.cids[0]] if c not in axes_list and c != '' and c != 'None': axes_list.append(c) @@ -596,6 +596,8 @@ def parse_table(self,exp,mips,tables,v_list,table_var_fields,table_axes_fields,t ax['bounds'] = v.label+"_bnds" if hasattr(v,'requested'): ax['requested'] = v.requested + if hasattr(v,'altLabel'): + ax['altLabel'] = v.altLabel #if hasattr(v,'boundsValues'): # ax['boundsValues'] = v.boundsValues if hasattr(v,'coords'): From c8a8bf97b8f16f03959ad6429f1b60d9bac24e07 Mon Sep 17 00:00:00 2001 From: sherimickelson Date: Wed, 24 Oct 2018 13:29:32 -0600 Subject: [PATCH 23/23] Update version number to 0.2.7 --- source/pyconform/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/pyconform/version.py b/source/pyconform/version.py index d0c9d782..0f306be1 100644 --- a/source/pyconform/version.py +++ b/source/pyconform/version.py @@ -1,2 +1,2 @@ # Single place for version information -__version__ = '0.2.6' +__version__ = '0.2.7'