Advertisement
Guest User

Untitled

a guest
Aug 21st, 2019
111
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.43 KB | None | 0 0
  1. from cf_units import Unit
  2. import xarray as xr
  3. import numpy as np
  4.  
  5. @xr.register_dataarray_accessor('ilamb')
  6. class ilamb_variable:
  7. def __init__(self, xarray_obj):
  8. self._obj = xarray_obj
  9. self.measure = None
  10. self.bounds = None
  11.  
  12. def convert(self,unit,density=998.2):
  13. """Convert the variable to a given unit.
  14.  
  15. We use the UDUNITS2 library via the cf_units python interface
  16. to convert the unit. Additional support is provided for unit
  17. conversions in which substance information is required, such
  18. as in precipitation where it is common to have data in a mass
  19. rate per unit area [kg s-1 m-2] and desire it in a linear rate
  20. [m s-1]. Conversion occurs in place, but also returns the
  21. DataArray object so that the user can chain calls together.
  22.  
  23. Parameters
  24. ----------
  25. unit : str
  26. the desired unit
  27. density : float, optional
  28. the mass density in [kg m-3] to use when converting,
  29. defaults to that of water
  30.  
  31. Returns
  32. -------
  33. self : xarray.DataArray
  34. """
  35. if 'units' not in self._obj.attrs:
  36. msg = "Cannot convert the units of a DataArray lacking the 'units' attribute"
  37. raise ValueError(msg)
  38. src_unit = Unit(self._obj.units)
  39. tar_unit = Unit(unit)
  40.  
  41. # Define some generic quantities
  42. linear = Unit("m")
  43. linear_rate = Unit("m s-1")
  44. area_density = Unit("kg m-2")
  45. area_density_rate = Unit("kg m-2 s-1")
  46. mass_density = Unit("kg m-3")
  47.  
  48. # Do we need to multiply by density?
  49. if ( (src_unit.is_convertible(linear_rate) and tar_unit.is_convertible(area_density_rate)) or
  50. (src_unit.is_convertible(linear ) and tar_unit.is_convertible(area_density )) ):
  51. with np.errstate(over='ignore',under='ignore'):
  52. self._obj.data *= density
  53. src_unit *= mass_density
  54.  
  55. # Do we need to divide by density?
  56. if ( (tar_unit.is_convertible(linear_rate) and src_unit.is_convertible(area_density_rate)) or
  57. (tar_unit.is_convertible(linear ) and src_unit.is_convertible(area_density )) ):
  58. with np.errstate(over='ignore',under='ignore'):
  59. self._obj.data /= density
  60. src_unit /= mass_density
  61.  
  62. # Convert the unit
  63. with np.errstate(over='ignore',under='ignore'):
  64. src_unit.convert(self._obj.data,tar_unit,inplace=True)
  65. self._obj.attrs['units'] = unit
  66. return self._obj
  67.  
  68. def add_measure(self,area_filename,fraction_filename=None):
  69.  
  70. # check that the measure is in the attributes
  71. if 'cell_measures' not in self._obj.attrs:
  72. msg = "DataArray does not contain the 'cell_measures' attribute"
  73. raise ValueError(msg)
  74.  
  75. # try to get the cell areas from the file
  76. measure_name = self._obj.cell_measures.split(":")[1].strip()
  77. with xr.open_dataset(area_filename) as ds:
  78. if measure_name not in ds:
  79. msg = "The cell_measures: %s is not found in %s" % (measure_name,area_filename)
  80. raise ValueError(msg)
  81. self.measure = ds[measure_name]
  82.  
  83. # optionally multiply by cell fractions
  84. if fraction_filename is None: return
  85. fraction_name = "sftlf"
  86. with xr.open_dataset(fraction_filename) as ds:
  87. if fraction_name not in ds:
  88. msg = "The fraction variable sftlf is not found in %s" % (area_filename)
  89. raise ValueError(msg)
  90. self.measure *= ds[fraction_name].ilamb.convert("1")
  91.  
  92. def integrate_space(self,mean=False):
  93.  
  94. if self.measure is None:
  95. msg = "Must call ilamb.add_measure() before you can call ilamb.integrate_space()"
  96. raise ValueError(msg)
  97.  
  98. # area weighted sum, divided by area if taking a mean
  99. out = (self._obj*self.measure).sum(self.measure.dims)
  100. if mean: out /= self.measure.sum()
  101.  
  102. # handle unit shifts, measure assumed in m2 if not given
  103. unit = Unit(self._obj.units)
  104. if not mean:
  105. unit *= Unit(self.measure.units if "units" in self.measure.attrs else "m2")
  106. out.attrs['units'] = str(unit).replace("."," ")
  107. out.ilamb.bounds = self.bounds
  108.  
  109. return out
  110.  
  111. def integrate_time(self,initial_time=None,final_time=None,mean=False):
  112.  
  113. if self.bounds is None:
  114. msg = "Must call ilamb.add_bounds() before you can call ilamb.integrate_time()"
  115. raise ValueError(msg)
  116.  
  117. # do we need to subset?
  118. if initial_time is not None or final_time is not None:
  119. data = self._obj.loc[initial_time:final_time]
  120. dt = self.bounds.loc[initial_time:final_time]
  121. else:
  122. data = self._obj
  123. dt = self.bounds
  124.  
  125. # area weighted sum, divided by area if taking a mean
  126. out = (data*dt).sum('time')
  127. if mean: out /= dt.sum()
  128.  
  129. # handle unit shifts, bounds assumed in d if not given
  130. unit = Unit(self._obj.units)
  131. if not mean:
  132. unit *= Unit(self.bounds.units if "units" in self.bounds.attrs else "d")
  133. out.attrs['units'] = str(unit).replace("."," ")
  134. out.ilamb.measure = self.measure
  135.  
  136. return out
  137.  
  138. def cumsum(self):
  139.  
  140. if self.bounds is None:
  141. msg = "Must call ilamb.add_bounds() before you can call ilamb.cumsum()"
  142. raise ValueError(msg)
  143.  
  144. out = (self._obj*self.bounds).cumsum(dim='time')
  145. unit = Unit(self._obj.units)
  146. unit *= Unit(self.bounds.units if "units" in self.bounds.attrs else "d")
  147. out.attrs['units'] = str(unit).replace("."," ")
  148. out.ilamb.measure = self.measure
  149. return out
  150.  
  151. def add_bounds(self,dataset):
  152. if not ('time' in dataset and 'time' in self._obj.coords): return
  153. t0 = dataset['time']
  154. t = self._obj['time']
  155. if 'bounds' not in t.attrs: return
  156. if t0.size != t.size: return
  157. #if not np.allclose(t0,t): return # should check this but it breaks
  158. if t.bounds not in dataset: return
  159. self.bounds = np.diff(dataset[t.bounds].values,axis=1)
  160. self.bounds = xr.DataArray([bnd.total_seconds()/(24*3600) for bnd in self.bounds[:,0]],
  161. dims = ('time'),
  162. coords = {'time':t})
  163. self.bounds.attrs['units'] = 'd'
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement