def test_unicode_history(self): unicode_str = unichr(40960) + u'wxyz' + unichr(1972) cube = iris.tests.stock.simple_1d() cube.add_history(unicode_str) self.assertString(str(cube), ('cdm', 'str_repr', 'unicode_history.__str__.txt')) self.assertString(unicode(cube), ('cdm', 'str_repr', 'unicode_history.__unicode__.txt'))
def curl(i_cube, j_cube, k_cube=None, ignore=None, update_history=True): r''' Calculate the 3d curl of the given vector of cubes. Args: * i_cube The i cube of the vector to operate on * j_cube The j cube of the vector to operate on Kwargs: * k_cube The k cube of the vector to operate on Return (i_cmpt_curl_cube, j_cmpt_curl_cube, k_cmpt_curl_cube) The calculation of curl is dependent on the type of :func:`iris.coord_systems.CoordSystem` in the cube: Cartesian curl The Cartesian curl is defined as: .. math:: \nabla\times \vec u = (\frac{\delta w}{\delta y} - \frac{\delta v}{\delta z}) \vec a_i - (\frac{\delta w}{\delta x} - \frac{\delta u}{\delta z})\vec a_j + (\frac{\delta v}{\delta x} - \frac{\delta u}{\delta y})\vec a_k Spherical curl When spherical calculus is used, i_cube is the phi vector component (e.g. eastward), j_cube is the theta component (e.g. northward) and k_cube is the radial component. The spherical curl is defined as: .. math:: \nabla\times \vec A = \frac{1}{r cos \theta}(\frac{\delta}{\delta \theta}(\vec A_\phi cos \theta) - \frac{\delta \vec A_\theta}{\delta \phi}) \vec r + \frac{1}{r}(\frac{1}{cos \theta} \frac{\delta \vec A_r}{\delta \phi} - \frac{\delta}{\delta r} (r \vec A_\phi))\vec \theta + \frac{1}{r}(\frac{\delta}{\delta r}(r \vec A_\theta) - \frac{\delta \vec A_r}{\delta \theta}) \vec \phi where phi is longitude, theta is latitude. ''' if ignore is not None: ignore = None warnings.warn('The ignore keyword to iris.analysis.calculus.curl is deprecated, ignoring is now done automatically.') # Get the vector quantity names (i.e. ['easterly', 'northerly', 'vertical']) vector_quantity_names, phenomenon_name = spatial_vectors_with_phenom_name(i_cube, j_cube, k_cube) cubes = filter(None, [i_cube, j_cube, k_cube]) # get the names of all coords binned into useful comparison groups coord_comparison = iris.analysis.coord_comparison(*cubes) bad_coords = coord_comparison['ungroupable_and_dimensioned'] if bad_coords: raise ValueError("Coordinates found in one cube that describe a data dimension which weren't in the other " "cube (%s), try removing this coordinate." % ', '.join([group.name() for group in bad_coords])) bad_coords = coord_comparison['resamplable'] if bad_coords: raise ValueError('Some coordinates are different (%s), consider resampling.' % ', '.join([group.name() for group in bad_coords])) ignore_string = '' if coord_comparison['ignorable']: ignore_string = ' (ignoring %s)' % ', '.join([group.name() for group in bad_coords]) # Get the dim_coord, or None if none exist, for the xyz dimensions x_coord = i_cube.coord(axis='X') y_coord = i_cube.coord(axis='Y') z_coord = i_cube.coord(axis='Z') y_dim = i_cube.coord_dims(y_coord)[0] horiz_cs = i_cube.coord_system('CoordSystem') # Planar (non spherical) coords? ellipsoidal = isinstance(horiz_cs, (iris.coord_systems.GeogCS, iris.coord_systems.RotatedGeogCS)) if not ellipsoidal: # TODO Implement some mechanism for conforming to a common grid dj_dx = _curl_differentiate(j_cube, x_coord) prototype_diff = dj_dx # i curl component (dk_dy - dj_dz) dk_dy = _curl_differentiate(k_cube, y_coord) dk_dy = _curl_regrid(dk_dy, prototype_diff) dj_dz = _curl_differentiate(j_cube, z_coord) dj_dz = _curl_regrid(dj_dz, prototype_diff) # TODO Implement resampling in the vertical (which regridding does not support). if dj_dz is not None and dj_dz.data.shape != prototype_diff.data.shape: dj_dz = _curl_change_z(dj_dz, z_coord, prototype_diff) i_cmpt = _curl_subtract(dk_dy, dj_dz) dj_dz = dk_dy = None # j curl component (di_dz - dk_dx) di_dz = _curl_differentiate(i_cube, z_coord) di_dz = _curl_regrid(di_dz, prototype_diff) # TODO Implement resampling in the vertical (which regridding does not support). if di_dz is not None and di_dz.data.shape != prototype_diff.data.shape: di_dz = _curl_change_z(di_dz, z_coord, prototype_diff) dk_dx = _curl_differentiate(k_cube, x_coord) dk_dx = _curl_regrid(dk_dx, prototype_diff) j_cmpt = _curl_subtract(di_dz, dk_dx) di_dz = dk_dx = None # k curl component ( dj_dx - di_dy) di_dy = _curl_differentiate(i_cube, y_coord) di_dy = _curl_regrid(di_dy, prototype_diff) # Since prototype_diff == dj_dx we don't need to recalculate dj_dx # dj_dx = _curl_differentiate(j_cube, x_coord) # dj_dx = _curl_regrid(dj_dx, prototype_diff) k_cmpt = _curl_subtract(dj_dx, di_dy) di_dy = dj_dx = None result = [i_cmpt, j_cmpt, k_cmpt] # Spherical coords (GeogCS or RotatedGeogCS). else: # A_\phi = i ; A_\theta = j ; A_\r = k # theta = lat ; phi = long ; # r_cmpt = 1/ ( r * cos(lat) ) * ( d/dtheta ( i_cube * sin( lat ) ) - d_j_cube_dphi ) # phi_cmpt = 1/r * ( d/dr (r * j_cube) - d_k_cube_dtheta) # theta_cmpt = 1/r * ( 1/cos(lat) * d_k_cube_dphi - d/dr (r * i_cube) if y_coord.name() != 'latitude' or x_coord.name() != 'longitude': raise ValueError('Expecting latitude as the y coord and longitude as the x coord for spherical curl.') # Get the radius of the earth - and check for sphericity ellipsoid = horiz_cs if isinstance(horiz_cs, iris.coord_systems.RotatedGeogCS): ellipsoid = horiz_cs.ellipsoid if ellipsoid: # TODO: Add a test for this r = ellipsoid.semi_major_axis r_unit = iris.unit.Unit("m") spherical = (ellipsoid.inverse_flattening == 0.0) else: r = iris.analysis.cartography.DEFAULT_SPHERICAL_EARTH_RADIUS r_unit = iris.analysis.cartography.DEFAULT_SPHERICAL_EARTH_RADIUS_UNIT spherical = True if not spherical: raise ValueError("Cannot take the curl over a non-spherical ellipsoid.") lon_coord = x_coord.copy() lat_coord = y_coord.copy() lon_coord.convert_units('radians') lat_coord.convert_units('radians') lat_cos_coord = _coord_cos(lat_coord) # TODO Implement some mechanism for conforming to a common grid temp = iris.analysis.maths.multiply(i_cube, lat_cos_coord, y_dim) dicos_dtheta = _curl_differentiate(temp, lat_coord) prototype_diff = dicos_dtheta # r curl component: 1/ ( r * cos(lat) ) * ( dicos_dtheta - d_j_cube_dphi ) # Since prototype_diff == dicos_dtheta we don't need to recalculate dicos_dtheta d_j_cube_dphi = _curl_differentiate(j_cube, lon_coord) d_j_cube_dphi = _curl_regrid(d_j_cube_dphi, prototype_diff) new_lat_coord = d_j_cube_dphi.coord(name='latitude') new_lat_cos_coord = _coord_cos(new_lat_coord) lat_dim = d_j_cube_dphi.coord_dims(new_lat_coord)[0] r_cmpt = iris.analysis.maths.divide(_curl_subtract(dicos_dtheta, d_j_cube_dphi), r * new_lat_cos_coord, dim=lat_dim) r_cmpt.units = r_cmpt.units / r_unit d_j_cube_dphi = dicos_dtheta = None # phi curl component: 1/r * ( drj_dr - d_k_cube_dtheta) drj_dr = _curl_differentiate(r * j_cube, z_coord) if drj_dr is not None: drj_dr.units = drj_dr.units * r_unit drj_dr = _curl_regrid(drj_dr, prototype_diff) d_k_cube_dtheta = _curl_differentiate(k_cube, lat_coord) d_k_cube_dtheta = _curl_regrid(d_k_cube_dtheta, prototype_diff) if drj_dr is None and d_k_cube_dtheta is None: phi_cmpt = None else: phi_cmpt = 1/r * _curl_subtract(drj_dr, d_k_cube_dtheta) phi_cmpt.units = phi_cmpt.units / r_unit drj_dr = d_k_cube_dtheta = None # theta curl component: 1/r * ( 1/cos(lat) * d_k_cube_dphi - dri_dr ) d_k_cube_dphi = _curl_differentiate(k_cube, lon_coord) d_k_cube_dphi = _curl_regrid(d_k_cube_dphi, prototype_diff) if d_k_cube_dphi is not None: d_k_cube_dphi = iris.analysis.maths.divide(d_k_cube_dphi, lat_cos_coord) dri_dr = _curl_differentiate(r * i_cube, z_coord) if dri_dr is not None: dri_dr.units = dri_dr.units * r_unit dri_dr = _curl_regrid(dri_dr, prototype_diff) if d_k_cube_dphi is None and dri_dr is None: theta_cmpt = None else: theta_cmpt = 1/r * _curl_subtract(d_k_cube_dphi, dri_dr) theta_cmpt.units = theta_cmpt.units / r_unit d_k_cube_dphi = dri_dr = None result = [phi_cmpt, theta_cmpt, r_cmpt] for direction, cube in zip(vector_quantity_names, result): if cube is not None: cube.rename('%s curl of %s' % (direction, phenomenon_name)) if update_history: # Add history in place if k_cube is None: cube.add_history('%s cmpt of the curl of %s and %s%s' % \ (direction, i_cube.name(), j_cube.name(), ignore_string)) else: cube.add_history('%s cmpt of the curl of %s, %s and %s%s' % \ (direction, i_cube.name(), j_cube.name(), k_cube.name(), ignore_string)) return result
def curl(i_cube, j_cube, k_cube=None, ignore=None, update_history=True): r''' Calculate the 3d curl of the given vector of cubes. Args: * i_cube The i cube of the vector to operate on * j_cube The j cube of the vector to operate on Kwargs: * k_cube The k cube of the vector to operate on Return (i_cmpt_curl_cube, j_cmpt_curl_cube, k_cmpt_curl_cube) The calculation of curl is dependent on the type of :func:`iris.coord_systems.CoordSystem` in the cube: Cartesian curl The Cartesian curl is defined as: .. math:: \nabla\times \vec u = (\frac{\delta w}{\delta y} - \frac{\delta v}{\delta z}) \vec a_i - (\frac{\delta w}{\delta x} - \frac{\delta u}{\delta z})\vec a_j + (\frac{\delta v}{\delta x} - \frac{\delta u}{\delta y})\vec a_k Spherical curl When spherical calculus is used, i_cube is the phi vector component (e.g. eastward), j_cube is the theta component (e.g. northward) and k_cube is the radial component. The spherical curl is defined as: .. math:: \nabla\times \vec A = \frac{1}{r cos \theta}(\frac{\delta}{\delta \theta}(\vec A_\phi cos \theta) - \frac{\delta \vec A_\theta}{\delta \phi}) \vec r + \frac{1}{r}(\frac{1}{cos \theta} \frac{\delta \vec A_r}{\delta \phi} - \frac{\delta}{\delta r} (r \vec A_\phi))\vec \theta + \frac{1}{r}(\frac{\delta}{\delta r}(r \vec A_\theta) - \frac{\delta \vec A_r}{\delta \theta}) \vec \phi where phi is longitude, theta is latitude. ''' if ignore is not None: ignore = None warnings.warn( 'The ignore keyword to iris.analysis.calculus.curl is deprecated, ignoring is now done automatically.' ) # Get the vector quantity names (i.e. ['easterly', 'northerly', 'vertical']) vector_quantity_names, phenomenon_name = spatial_vectors_with_phenom_name( i_cube, j_cube, k_cube) cubes = filter(None, [i_cube, j_cube, k_cube]) # get the names of all coords binned into useful comparison groups coord_comparison = iris.analysis.coord_comparison(*cubes) bad_coords = coord_comparison['ungroupable_and_dimensioned'] if bad_coords: raise ValueError( "Coordinates found in one cube that describe a data dimension which weren't in the other " "cube (%s), try removing this coordinate." % ', '.join([group.name() for group in bad_coords])) bad_coords = coord_comparison['resamplable'] if bad_coords: raise ValueError( 'Some coordinates are different (%s), consider resampling.' % ', '.join([group.name() for group in bad_coords])) ignore_string = '' if coord_comparison['ignorable']: ignore_string = ' (ignoring %s)' % ', '.join( [group.name() for group in bad_coords]) # Get the dim_coord, or None if none exist, for the xyz dimensions x_coord = i_cube.coord(axis='X') y_coord = i_cube.coord(axis='Y') z_coord = i_cube.coord(axis='Z') y_dim = i_cube.coord_dims(y_coord)[0] horiz_cs = i_cube.coord_system('CoordSystem') # Planar (non spherical) coords? ellipsoidal = isinstance( horiz_cs, (iris.coord_systems.GeogCS, iris.coord_systems.RotatedGeogCS)) if not ellipsoidal: # TODO Implement some mechanism for conforming to a common grid dj_dx = _curl_differentiate(j_cube, x_coord) prototype_diff = dj_dx # i curl component (dk_dy - dj_dz) dk_dy = _curl_differentiate(k_cube, y_coord) dk_dy = _curl_regrid(dk_dy, prototype_diff) dj_dz = _curl_differentiate(j_cube, z_coord) dj_dz = _curl_regrid(dj_dz, prototype_diff) # TODO Implement resampling in the vertical (which regridding does not support). if dj_dz is not None and dj_dz.data.shape != prototype_diff.data.shape: dj_dz = _curl_change_z(dj_dz, z_coord, prototype_diff) i_cmpt = _curl_subtract(dk_dy, dj_dz) dj_dz = dk_dy = None # j curl component (di_dz - dk_dx) di_dz = _curl_differentiate(i_cube, z_coord) di_dz = _curl_regrid(di_dz, prototype_diff) # TODO Implement resampling in the vertical (which regridding does not support). if di_dz is not None and di_dz.data.shape != prototype_diff.data.shape: di_dz = _curl_change_z(di_dz, z_coord, prototype_diff) dk_dx = _curl_differentiate(k_cube, x_coord) dk_dx = _curl_regrid(dk_dx, prototype_diff) j_cmpt = _curl_subtract(di_dz, dk_dx) di_dz = dk_dx = None # k curl component ( dj_dx - di_dy) di_dy = _curl_differentiate(i_cube, y_coord) di_dy = _curl_regrid(di_dy, prototype_diff) # Since prototype_diff == dj_dx we don't need to recalculate dj_dx # dj_dx = _curl_differentiate(j_cube, x_coord) # dj_dx = _curl_regrid(dj_dx, prototype_diff) k_cmpt = _curl_subtract(dj_dx, di_dy) di_dy = dj_dx = None result = [i_cmpt, j_cmpt, k_cmpt] # Spherical coords (GeogCS or RotatedGeogCS). else: # A_\phi = i ; A_\theta = j ; A_\r = k # theta = lat ; phi = long ; # r_cmpt = 1/ ( r * cos(lat) ) * ( d/dtheta ( i_cube * sin( lat ) ) - d_j_cube_dphi ) # phi_cmpt = 1/r * ( d/dr (r * j_cube) - d_k_cube_dtheta) # theta_cmpt = 1/r * ( 1/cos(lat) * d_k_cube_dphi - d/dr (r * i_cube) if y_coord.name() != 'latitude' or x_coord.name() != 'longitude': raise ValueError( 'Expecting latitude as the y coord and longitude as the x coord for spherical curl.' ) # Get the radius of the earth - and check for sphericity ellipsoid = horiz_cs if isinstance(horiz_cs, iris.coord_systems.RotatedGeogCS): ellipsoid = horiz_cs.ellipsoid if ellipsoid: # TODO: Add a test for this r = ellipsoid.semi_major_axis r_unit = iris.unit.Unit("m") spherical = (ellipsoid.inverse_flattening == 0.0) else: r = iris.analysis.cartography.DEFAULT_SPHERICAL_EARTH_RADIUS r_unit = iris.analysis.cartography.DEFAULT_SPHERICAL_EARTH_RADIUS_UNIT spherical = True if not spherical: raise ValueError( "Cannot take the curl over a non-spherical ellipsoid.") lon_coord = x_coord.copy() lat_coord = y_coord.copy() lon_coord.convert_units('radians') lat_coord.convert_units('radians') lat_cos_coord = _coord_cos(lat_coord) # TODO Implement some mechanism for conforming to a common grid temp = iris.analysis.maths.multiply(i_cube, lat_cos_coord, y_dim) dicos_dtheta = _curl_differentiate(temp, lat_coord) prototype_diff = dicos_dtheta # r curl component: 1/ ( r * cos(lat) ) * ( dicos_dtheta - d_j_cube_dphi ) # Since prototype_diff == dicos_dtheta we don't need to recalculate dicos_dtheta d_j_cube_dphi = _curl_differentiate(j_cube, lon_coord) d_j_cube_dphi = _curl_regrid(d_j_cube_dphi, prototype_diff) new_lat_coord = d_j_cube_dphi.coord(name='latitude') new_lat_cos_coord = _coord_cos(new_lat_coord) lat_dim = d_j_cube_dphi.coord_dims(new_lat_coord)[0] r_cmpt = iris.analysis.maths.divide(_curl_subtract( dicos_dtheta, d_j_cube_dphi), r * new_lat_cos_coord, dim=lat_dim) r_cmpt.units = r_cmpt.units / r_unit d_j_cube_dphi = dicos_dtheta = None # phi curl component: 1/r * ( drj_dr - d_k_cube_dtheta) drj_dr = _curl_differentiate(r * j_cube, z_coord) if drj_dr is not None: drj_dr.units = drj_dr.units * r_unit drj_dr = _curl_regrid(drj_dr, prototype_diff) d_k_cube_dtheta = _curl_differentiate(k_cube, lat_coord) d_k_cube_dtheta = _curl_regrid(d_k_cube_dtheta, prototype_diff) if drj_dr is None and d_k_cube_dtheta is None: phi_cmpt = None else: phi_cmpt = 1 / r * _curl_subtract(drj_dr, d_k_cube_dtheta) phi_cmpt.units = phi_cmpt.units / r_unit drj_dr = d_k_cube_dtheta = None # theta curl component: 1/r * ( 1/cos(lat) * d_k_cube_dphi - dri_dr ) d_k_cube_dphi = _curl_differentiate(k_cube, lon_coord) d_k_cube_dphi = _curl_regrid(d_k_cube_dphi, prototype_diff) if d_k_cube_dphi is not None: d_k_cube_dphi = iris.analysis.maths.divide(d_k_cube_dphi, lat_cos_coord) dri_dr = _curl_differentiate(r * i_cube, z_coord) if dri_dr is not None: dri_dr.units = dri_dr.units * r_unit dri_dr = _curl_regrid(dri_dr, prototype_diff) if d_k_cube_dphi is None and dri_dr is None: theta_cmpt = None else: theta_cmpt = 1 / r * _curl_subtract(d_k_cube_dphi, dri_dr) theta_cmpt.units = theta_cmpt.units / r_unit d_k_cube_dphi = dri_dr = None result = [phi_cmpt, theta_cmpt, r_cmpt] for direction, cube in zip(vector_quantity_names, result): if cube is not None: cube.rename('%s curl of %s' % (direction, phenomenon_name)) if update_history: # Add history in place if k_cube is None: cube.add_history('%s cmpt of the curl of %s and %s%s' % \ (direction, i_cube.name(), j_cube.name(), ignore_string)) else: cube.add_history('%s cmpt of the curl of %s, %s and %s%s' % \ (direction, i_cube.name(), j_cube.name(), k_cube.name(), ignore_string)) return result