def _cube_area_sum(cube): """Calculate total area-sum - Iris can't do this in one operation.""" area_sums = cube * i_cartog.area_weights(cube, normalize=False) area_sum = area_sums.collapsed( area_sums.coords(dim_coords=True), iris.analysis.SUM ) return area_sum.data.flatten()[0]
def calculate_area(features, mask): from tobac.utils import mask_features_surface, mask_features from iris import Constraint from iris.analysis.cartography import area_weights features['area'] = np.nan mask_coords = [coord.name() for coord in mask.coords()] if ('projection_x_coordinate' in features.columns) and ( 'projection_y_coordinate' in features.columns) and ( 'projection_x_coordinate' in mask_coords) and ('projection_y_coordinate' in mask_coords): method_area = 'xy' elif ('latitude' in features.columns) and ('longitude' in features.columns) and ( 'latitude' in mask_coords) and ('longitude' in mask_coords): if mask.coord('latitude').ndim == 1: method_area = 'latlon' else: raise ValueError('2D latitude/longitude coordinates not supported') else: raise ValueError( 'either latitude/longitude or projection_x_coordinate/projection_y_coordinate have to be present to calculate distances' ) for time_i, features_i in features.groupby('time'): loggin.debug(str(time_i)) constraint_time = Constraint(time=time_i) mask_i = mask.extract(constraint_time) if method_area == 'xy': if not (mask.coord('projection_x_coordinate').has_bounds() and mask.coord('projection_y_coordinate').has_bounds()): mask.coord('projection_x_coordinate').guess_bounds() mask.coord('projection_y_coordinate').guess_bounds() area = np.outer( np.diff(mask.coord('projection_x_coordinate').bounds, axis=1), np.diff(mask.coord('projection_y_coordinate').bounds, axis=1)) elif method_area == 'latlon': if not (mask.coord('latitude').has_bounds() and mask.coord('longitude').has_bounds()): mask.coord('latitude').guess_bounds() mask.coord('longitude').guess_bounds() area = area_weights(mask, normalize=False) else: raise ValueError('method undefined') for i in features_i.index: if len(mask_i.shape) == 3: mask_i_surface = mask_features_surface( mask_i, features_i.loc[i, 'feature'], z_coord='model_level_number') elif len(mask_i.shape) == 2: mask_i_surface = mask_features(mask_i, features_i.loc[i, 'feature']) area_feature = np.sum(area * (mask_i_surface.data > 0)) features.at[i, 'area'] = area_feature return features
def area_average(x,latitude='latitude',longitude='longitude'): if x.coord(latitude).bounds is None: x.coord(latitude).guess_bounds() if x.coord(longitude).bounds is None: x.coord(longitude).guess_bounds() grid_areas = icart.area_weights(x) x_avg = x.collapsed([longitude, latitude], iris.analysis.MEAN, weights=grid_areas) return x_avg
def weights_array(cube, scheme): """Weights for a data set on a grid. Returned weights are a `numpy.ndarray` broadcastable to the shape of the input cube. **Arguments:** *cube* An `~iris.cube.Cube` instance to generate weights for. *scheme* Weighting scheme to use. The following values are accepted: * *'coslat'* : Square-root of cosine of latitude. * *'area'* : Square-root of grid cell area normalized by total grid area. **Returns:** *weights* An array contanining the weights (not a `~iris.cube.Cube`). **Examples:** Area weights for a `~iris.cube.Cube` on 2D grid: weights = weights_array(cube, scheme='area') Square-root of cosine of latitude weights for a `~iris.cube.Cube` with a latitude dimension: weights = weights_array(cube, scheme='coslat') """ # Always use lower-case for the scheme, allowing the user to use # upper-case in their calling code without an error. scheme = scheme.lower() if scheme in ('area',): # Handle area weighting. try: with warnings.catch_warnings(): warnings.simplefilter('ignore', UserWarning) weights = np.sqrt(area_weights(cube, normalize=True)) except (ValueError, CoordinateMultiDimError): raise ValueError('cannot generate area weights') elif scheme in ('coslat',): # Handle square-root of cosine of latitude weighting. try: weights = np.sqrt(cosine_latitude_weights(cube)) except (ValueError, CoordinateMultiDimError): raise ValueError('cannot generate latitude weights') else: raise ValueError("invalid weighting scheme: '{!s}'".format(scheme)) return weights
def weights_array(cube, scheme): """Weights for a data set on a grid. Returned weights are a `numpy.ndarray` broadcastable to the shape of the input cube. **Arguments:** *cube* An `~iris.cube.Cube` instance to generate weights for. *scheme* Weighting scheme to use. The following values are accepted: * *'coslat'* : Square-root of cosine of latitude. * *'area'* : Square-root of grid cell area normalized by total grid area. **Returns:** *weights* An array contanining the weights (not a `~iris.cube.Cube`). **Examples:** Area weights for a `~iris.cube.Cube` on 2D grid: weights = weights_array(cube, scheme='area') Square-root of cosine of latitude weights for a `~iris.cube.Cube` with a latitude dimension: weights = weights_array(cube, scheme='coslat') """ # Always use lower-case for the scheme, allowing the user to use # upper-case in their calling code without an error. scheme = scheme.lower() if scheme in ('area', ): # Handle area weighting. try: with warnings.catch_warnings(): warnings.simplefilter('ignore', UserWarning) weights = np.sqrt(area_weights(cube, normalize=True)) except (ValueError, CoordinateMultiDimError): raise ValueError('cannot generate area weights') elif scheme in ('coslat', ): # Handle square-root of cosine of latitude weighting. try: weights = np.sqrt(cosine_latitude_weights(cube)) except (ValueError, CoordinateMultiDimError): raise ValueError('cannot generate latitude weights') else: raise ValueError("invalid weighting scheme: '{!s}'".format(scheme)) return weights
def get_gridbox_vol(incube): """ Calculate grid-box volumes for input cube """ import iris.analysis.cartography as icarto if incube.ndim < 3: raise TC_error('GET_VOL: Cube must have at least 3 dimensions') # Array for grid box areas. Same for all levels so can # work on 2-D cube if incube.ndim == 4: tcube = incube[0, 0, :, :] else: tcube = incube[0, :, :] if not tcube.coord('latitude').has_bounds(): tcube.coord('latitude').guess_bounds() if not tcube.coord('longitude').has_bounds(): tcube.coord('longitude').guess_bounds() grid_areas = icarto.area_weights(tcube) tcube = 'a' nlat = incube.coord('latitude').shape[0] nlong = incube.coord('longitude').shape[0] nlevs = incube.coord('level_height').shape[0] # Obtain grid top-boundary heights, so that subtracting from lower # will give height of each grid-box (horizontally uniform) grid_top = np.zeros(nlevs + 1, dtype=np.float32) grid_top[1:] = incube.coord('level_height').bounds[:, 1] # top bound # Volume -area x height, always 3-D array (lat,long,hgt) vol_box = np.zeros([nlevs, nlat, nlong], dtype=np.float) for l in np.arange(nlevs): vol_box[l, :, :] = grid_areas[:, :] * (grid_top[l + 1] - grid_top[l]) # Setup coords and generate a cube from computed values dm_long = incube.coord('longitude') dm_lat = incube.coord('latitude') dm_lev = incube.coord('model_level_number') aux_hgt = incube.coord('level_height') oucube = iris.cube.Cube( vol_box, var_name='grid_cell_volume', units='m^3', dim_coords_and_dims=[(dm_lev, 0), (dm_lat, 1), (dm_long, 2)], attributes={'Source': 'Derived from grid_area and level_height'}) oucube.add_aux_coord(incube.coord('level_height'), 0) vol_box = np.zeros(1, dtype=np.int) return oucube
def area_average(x, latitude='latitude', longitude='longitude'): if x.coord(latitude).bounds is None: x.coord(latitude).guess_bounds() if x.coord(longitude).bounds is None: x.coord(longitude).guess_bounds() grid_areas = icart.area_weights(x) x_avg = x.collapsed([longitude, latitude], iris.analysis.MEAN, weights=grid_areas) return x_avg
def area_avg(cube, vert=None): ''' Routine to calculate global area-weighted mean fields ''' if cube: cube.coord('longitude').guess_bounds() cube.coord('latitude').guess_bounds() weights = iac.area_weights(cube) mean = cube.collapsed(['time', 'longitude', 'latitude'], iris.analysis.MEAN, weights=weights) # Do vertical average if requested, using named vertical coordinate if vert: mean = mean.collapsed(vert, iris.analysis.MEAN) else: mean = None return mean
def _cube_area_sum(cube): """Calculate total area-sum - Iris can't do this in one operation.""" area_sums = cube * i_cartog.area_weights(cube, normalize=False) area_sum = area_sums.collapsed(area_sums.coords(dim_coords=True), iris.analysis.SUM) return area_sum.data.flatten()[0]
def weight_lat_ave(cube): """Routine to calculate weighted latitudinal average.""" grid_areas = iac.area_weights(cube) return cube.collapsed('latitude', iris.analysis.MEAN, weights=grid_areas)
print("Picking months...") month_cubes = [] for month in range(1,13): print("Picking {}...".format(month_name[month])) #We pick the desired cubes: month_cubes.append(pick_times(test_cube,[],[month],[],[])) print("...done picking months.") #This loop calculates the mean temperature over all space, #it "collapses" the cube. print("Calculating spatial means...") collapsed_cubes = [] for i in range(len(month_cubes)): print("Calculating spatial mean for {}...".format(month_name[i+1])) #We get the area weights of the cells composing the region: grid_areas = area_weights(month_cubes[i]) #We "collapse" our 2D+Time cube into a 0D+Time by averaging using MEAN aggregator: collapsed_cubes.append(month_cubes[i].collapsed(['longitude', 'latitude'], MEAN, weights=grid_areas)) print("...done calculating spatial means.") #Finally, we cast our analysis into a plot import matplotlib.pyplot as plt import iris.quickplot as iplt print("Plotting...".format(month_name[i+1])) for i in range(len(collapsed_cubes)): #Plot... iplt.plot(collapsed_cubes[i],linewidth='10',label=month_name[i+1]) plt.legend(loc=4)
def calculate_area(features, mask, method_area=None): from tobac.utils import mask_features_surface, mask_features from iris import Constraint from iris.analysis.cartography import area_weights features["area"] = np.nan mask_coords = [coord.name() for coord in mask.coords()] if method_area is None: if ("projection_x_coordinate" in mask_coords) and ( "projection_y_coordinate" in mask_coords ): method_area = "xy" elif ("latitude" in mask_coords) and ("longitude" in mask_coords): method_area = "latlon" else: raise ValueError( "either latitude/longitude or projection_x_coordinate/projection_y_coordinate have to be present to calculate distances" ) logging.debug("calculating area using method " + method_area) if method_area == "xy": if not ( mask.coord("projection_x_coordinate").has_bounds() and mask.coord("projection_y_coordinate").has_bounds() ): mask.coord("projection_x_coordinate").guess_bounds() mask.coord("projection_y_coordinate").guess_bounds() area = np.outer( np.diff(mask.coord("projection_x_coordinate").bounds, axis=1), np.diff(mask.coord("projection_y_coordinate").bounds, axis=1), ) elif method_area == "latlon": if (mask.coord("latitude").ndim == 1) and (mask.coord("latitude").ndim == 1): if not ( mask.coord("latitude").has_bounds() and mask.coord("longitude").has_bounds() ): mask.coord("latitude").guess_bounds() mask.coord("longitude").guess_bounds() area = area_weights(mask, normalize=False) elif mask.coord("latitude").ndim == 2 and mask.coord("longitude").ndim == 2: raise ValueError("2D latitude/longitude coordinates not supported yet") # area=calculate_areas_2Dlatlon(mask.coord('latitude'),mask.coord('longitude')) else: raise ValueError("latitude/longitude coordinate shape not supported") else: raise ValueError("method undefined") for time_i, features_i in features.groupby("time"): logging.debug("timestep:" + str(time_i)) constraint_time = Constraint(time=time_i) mask_i = mask.extract(constraint_time) for i in features_i.index: if len(mask_i.shape) == 3: mask_i_surface = mask_features_surface( mask_i, features_i.loc[i, "feature"], z_coord="model_level_number" ) elif len(mask_i.shape) == 2: mask_i_surface = mask_features(mask_i, features_i.loc[i, "feature"]) area_feature = np.sum(area * (mask_i_surface.data > 0)) features.at[i, "area"] = area_feature return features