def setup_gradient_fields(registry, grad_field, field_units, slice_info=None): geom = registry.ds.geometry if is_curvilinear(geom): mylog.warning( "In %s geometry, gradient fields may contain artifacts near cartesian axes." % geom) assert (isinstance(grad_field, tuple)) ftype, fname = grad_field if slice_info is None: sl_left = slice(None, -2, None) sl_right = slice(2, None, None) div_fac = 2.0 else: sl_left, sl_right, div_fac = slice_info slice_3d = (slice(1, -1), slice(1, -1), slice(1, -1)) def grad_func(axi, ax): slice_3dl = slice_3d[:axi] + (sl_left, ) + slice_3d[axi + 1:] slice_3dr = slice_3d[:axi] + (sl_right, ) + slice_3d[axi + 1:] def func(field, data): ds = div_fac * data[ftype, "d%s" % ax] if ax == "theta": ds *= data[ftype, "r"] if ax == "phi": ds *= data[ftype, "r"] * np.sin(data[ftype, "theta"]) f = data[grad_field][slice_3dr] / ds[slice_3d] f -= data[grad_field][slice_3dl] / ds[slice_3d] new_field = np.zeros_like(data[grad_field], dtype=np.float64) new_field = data.ds.arr(new_field, f.units) new_field[slice_3d] = f return new_field return func field_units = Unit(field_units, registry=registry.ds.unit_registry) grad_units = field_units / registry.ds.unit_system["length"] for axi, ax in enumerate(registry.ds.coordinates.axis_order): f = grad_func(axi, ax) registry.add_field((ftype, "%s_gradient_%s" % (fname, ax)), sampling_type="local", function=f, validators=[ValidateSpatial(1, [grad_field])], units=grad_units) create_magnitude_field(registry, "%s_gradient" % fname, grad_units, ftype=ftype, validators=[ValidateSpatial(1, [grad_field])])
def __init__(self, ds, field_list, slice_info=None): self._show_field_errors = [] self.ds = ds # Now we start setting things up. self.field_list = field_list self.slice_info = slice_info self.field_aliases = {} self.species_names = [] if ds is not None and is_curvilinear(ds.geometry): self.curvilinear = True else: self.curvilinear = False self.setup_fluid_aliases()
def setup_gradient_fields(registry, grad_field, field_units, slice_info=None): # Current implementation for gradient is not valid for curvilinear geometries if is_curvilinear(registry.ds.geometry): return assert (isinstance(grad_field, tuple)) ftype, fname = grad_field if slice_info is None: sl_left = slice(None, -2, None) sl_right = slice(2, None, None) div_fac = 2.0 else: sl_left, sl_right, div_fac = slice_info slice_3d = (slice(1, -1), slice(1, -1), slice(1, -1)) def grad_func(axi, ax): slice_3dl = slice_3d[:axi] + (sl_left, ) + slice_3d[axi + 1:] slice_3dr = slice_3d[:axi] + (sl_right, ) + slice_3d[axi + 1:] def func(field, data): ds = div_fac * data[ftype, "d%s" % ax] f = data[grad_field][slice_3dr] / ds[slice_3d] f -= data[grad_field][slice_3dl] / ds[slice_3d] new_field = np.zeros_like(data[grad_field], dtype=np.float64) new_field = data.ds.arr(new_field, f.units) new_field[slice_3d] = f return new_field return func field_units = Unit(field_units, registry=registry.ds.unit_registry) grad_units = field_units / registry.ds.unit_system["length"] for axi, ax in enumerate('xyz'): f = grad_func(axi, ax) registry.add_field((ftype, "%s_gradient_%s" % (fname, ax)), sampling_type="cell", function=f, validators=[ValidateSpatial(1, [grad_field])], units=grad_units) create_magnitude_field(registry, "%s_gradient" % fname, grad_units, ftype=ftype, validators=[ValidateSpatial(1, [grad_field])])
def create_vector_fields(registry, basename, field_units, ftype="gas", slice_info=None): from yt.units.unit_object import Unit # slice_info would be the left, the right, and the factor. # For example, with the old Enzo-ZEUS fields, this would be: # slice(None, -2, None) # slice(1, -1, None) # 1.0 # Otherwise, we default to a centered difference. if slice_info is None: sl_left = slice(None, -2, None) sl_right = slice(2, None, None) div_fac = 2.0 else: sl_left, sl_right, div_fac = slice_info xn, yn, zn = [(ftype, f"{basename}_{ax}") for ax in "xyz"] # Is this safe? if registry.ds.dimensionality < 3: zn = ("index", "zeros") if registry.ds.dimensionality < 2: yn = ("index", "zeros") create_relative_field( registry, basename, field_units, ftype=ftype, slice_info=slice_info, validators=[ValidateParameter(f"bulk_{basename}")], ) create_magnitude_field( registry, basename, field_units, ftype=ftype, slice_info=slice_info, validators=[ValidateParameter(f"bulk_{basename}")], ) if not is_curvilinear(registry.ds.geometry): # The following fields are invalid for curvilinear geometries def _spherical_radius_component(field, data): """The spherical radius component of the vector field Relative to the coordinate system defined by the *normal* vector, *center*, and *bulk_* field parameters. """ normal = data.get_field_parameter("normal") vectors = obtain_relative_velocity_vector(data, (xn, yn, zn), f"bulk_{basename}") theta = data["index", "spherical_theta"] phi = data["index", "spherical_phi"] rv = get_sph_r_component(vectors, theta, phi, normal) # Now, anywhere that radius is in fact zero, we want to zero out our # return values. rv[np.isnan(theta)] = 0.0 return rv registry.add_field( (ftype, f"{basename}_spherical_radius"), sampling_type="local", function=_spherical_radius_component, units=field_units, validators=[ ValidateParameter("normal"), ValidateParameter("center"), ValidateParameter(f"bulk_{basename}"), ], ) create_los_field(registry, basename, field_units, ftype=ftype, slice_info=slice_info) def _radial(field, data): return data[ftype, f"{basename}_spherical_radius"] def _radial_absolute(field, data): return np.abs(data[ftype, f"{basename}_spherical_radius"]) def _tangential(field, data): return np.sqrt(data[ftype, f"{basename}_spherical_theta"]**2.0 + data[ftype, f"{basename}_spherical_phi"]**2.0) registry.add_field( (ftype, f"radial_{basename}"), sampling_type="local", function=_radial, units=field_units, validators=[ ValidateParameter("normal"), ValidateParameter("center") ], ) registry.add_field( (ftype, f"radial_{basename}_absolute"), sampling_type="local", function=_radial_absolute, units=field_units, ) registry.add_field( (ftype, f"tangential_{basename}"), sampling_type="local", function=_tangential, units=field_units, ) def _spherical_theta_component(field, data): """The spherical theta component of the vector field Relative to the coordinate system defined by the *normal* vector, *center*, and *bulk_* field parameters. """ normal = data.get_field_parameter("normal") vectors = obtain_relative_velocity_vector(data, (xn, yn, zn), f"bulk_{basename}") theta = data["index", "spherical_theta"] phi = data["index", "spherical_phi"] return get_sph_theta_component(vectors, theta, phi, normal) registry.add_field( (ftype, f"{basename}_spherical_theta"), sampling_type="local", function=_spherical_theta_component, units=field_units, validators=[ ValidateParameter("normal"), ValidateParameter("center"), ValidateParameter(f"bulk_{basename}"), ], ) def _spherical_phi_component(field, data): """The spherical phi component of the vector field Relative to the coordinate system defined by the *normal* vector, *center*, and *bulk_* field parameters. """ normal = data.get_field_parameter("normal") vectors = obtain_relative_velocity_vector(data, (xn, yn, zn), f"bulk_{basename}") phi = data["index", "spherical_phi"] return get_sph_phi_component(vectors, phi, normal) registry.add_field( (ftype, f"{basename}_spherical_phi"), sampling_type="local", function=_spherical_phi_component, units=field_units, validators=[ ValidateParameter("normal"), ValidateParameter("center"), ValidateParameter(f"bulk_{basename}"), ], ) def _cp_vectors(ax): def _cp_val(field, data): vec = data.get_field_parameter(f"cp_{ax}_vec") tr = data[xn[0], f"relative_{xn[1]}"] * vec.d[0] tr += data[yn[0], f"relative_{yn[1]}"] * vec.d[1] tr += data[zn[0], f"relative_{zn[1]}"] * vec.d[2] return tr return _cp_val for ax in "xyz": registry.add_field( (ftype, f"cutting_plane_{basename}_{ax}"), sampling_type="local", function=_cp_vectors(ax), units=field_units, ) def _divergence(field, data): ds = div_fac * just_one(data["index", "dx"]) f = data[xn[0], f"relative_{xn[1]}"][sl_right, 1:-1, 1:-1] / ds f -= data[xn[0], f"relative_{xn[1]}"][sl_left, 1:-1, 1:-1] / ds ds = div_fac * just_one(data["index", "dy"]) f += data[yn[0], f"relative_{yn[1]}"][1:-1, sl_right, 1:-1] / ds f -= data[yn[0], f"relative_{yn[1]}"][1:-1, sl_left, 1:-1] / ds ds = div_fac * just_one(data["index", "dz"]) f += data[zn[0], f"relative_{zn[1]}"][1:-1, 1:-1, sl_right] / ds f -= data[zn[0], f"relative_{zn[1]}"][1:-1, 1:-1, sl_left] / ds new_field = data.ds.arr(np.zeros(data[xn].shape, dtype=np.float64), f.units) new_field[1:-1, 1:-1, 1:-1] = f return new_field def _divergence_abs(field, data): return np.abs(data[ftype, f"{basename}_divergence"]) field_units = Unit(field_units, registry=registry.ds.unit_registry) div_units = field_units / registry.ds.unit_system["length"] registry.add_field( (ftype, f"{basename}_divergence"), sampling_type="local", function=_divergence, units=div_units, validators=[ ValidateSpatial(1), ValidateParameter(f"bulk_{basename}") ], ) registry.add_field( (ftype, f"{basename}_divergence_absolute"), sampling_type="local", function=_divergence_abs, units=div_units, ) def _tangential_over_magnitude(field, data): tr = (data[ftype, f"tangential_{basename}"] / data[ftype, f"{basename}_magnitude"]) return np.abs(tr) registry.add_field( (ftype, f"tangential_over_{basename}_magnitude"), sampling_type="local", function=_tangential_over_magnitude, take_log=False, ) def _cylindrical_radius_component(field, data): """The cylindrical radius component of the vector field Relative to the coordinate system defined by the *normal* vector, *center*, and *bulk_* field parameters. """ normal = data.get_field_parameter("normal") vectors = obtain_relative_velocity_vector(data, (xn, yn, zn), f"bulk_{basename}") theta = data["index", "cylindrical_theta"] return get_cyl_r_component(vectors, theta, normal) registry.add_field( (ftype, f"{basename}_cylindrical_radius"), sampling_type="local", function=_cylindrical_radius_component, units=field_units, validators=[ValidateParameter("normal")], ) registry.alias( (ftype, f"cylindrical_radial_{basename}"), (ftype, f"{basename}_cylindrical_radius"), deprecate=("4.0.0", "4.1.0"), ) def _cylindrical_radial_absolute(field, data): """This field is deprecated and will be removed in a future version""" return np.abs(data[ftype, f"{basename}_cylindrical_radius"]) registry.add_deprecated_field( (ftype, f"cylindrical_radial_{basename}_absolute"), function=_cylindrical_radial_absolute, sampling_type="local", since="4.0.0", removal="4.1.0", units=field_units, validators=[ValidateParameter("normal")], ) def _cylindrical_theta_component(field, data): """The cylindrical theta component of the vector field Relative to the coordinate system defined by the *normal* vector, *center*, and *bulk_* field parameters. """ normal = data.get_field_parameter("normal") vectors = obtain_relative_velocity_vector(data, (xn, yn, zn), f"bulk_{basename}") theta = data["index", "cylindrical_theta"].copy() theta = np.tile(theta, (3, ) + (1, ) * len(theta.shape)) return get_cyl_theta_component(vectors, theta, normal) registry.add_field( (ftype, f"{basename}_cylindrical_theta"), sampling_type="local", function=_cylindrical_theta_component, units=field_units, validators=[ ValidateParameter("normal"), ValidateParameter("center"), ValidateParameter(f"bulk_{basename}"), ], ) def _cylindrical_tangential_absolute(field, data): """This field is deprecated and will be removed in a future release""" return np.abs(data[ftype, f"cylindrical_tangential_{basename}"]) registry.alias( (ftype, f"cylindrical_tangential_{basename}"), (ftype, f"{basename}_cylindrical_theta"), deprecate=("4.0.0", "4.1.0"), ) registry.add_deprecated_field( (ftype, f"cylindrical_tangential_{basename}_absolute"), function=_cylindrical_tangential_absolute, sampling_type="local", since="4.0.0", removal="4.1.0", units=field_units, ) def _cylindrical_z_component(field, data): """The cylindrical z component of the vector field Relative to the coordinate system defined by the *normal* vector, *center*, and *bulk_* field parameters. """ normal = data.get_field_parameter("normal") vectors = obtain_relative_velocity_vector(data, (xn, yn, zn), f"bulk_{basename}") return get_cyl_z_component(vectors, normal) registry.add_field( (ftype, f"{basename}_cylindrical_z"), sampling_type="local", function=_cylindrical_z_component, units=field_units, validators=[ ValidateParameter("normal"), ValidateParameter("center"), ValidateParameter(f"bulk_{basename}"), ], ) else: # Create Cartesian fields for curvilinear coordinates def _cartesian_x(field, data): if registry.ds.geometry == "polar": return data[(ftype, f"{basename}_r")] * np.cos( data[(ftype, "theta")]) elif registry.ds.geometry == "cylindrical": if data.ds.dimensionality == 2: return data[(ftype, f"{basename}_r")] elif data.ds.dimensionality == 3: return data[(ftype, f"{basename}_r")] * np.cos(data[ (ftype, "theta")]) - data[ (ftype, f"{basename}_theta")] * np.sin(data[ (ftype, "theta")]) elif registry.ds.geometry == "spherical": if data.ds.dimensionality == 2: return data[(ftype, f"{basename}_r")] * np.sin(data[ (ftype, "theta")]) + data[ (ftype, f"{basename}_theta")] * np.cos(data[ (ftype, "theta")]) elif data.ds.dimensionality == 3: return (data[(ftype, f"{basename}_r")] * np.sin(data[ (ftype, "theta")]) * np.cos(data[(ftype, "phi")]) + data[(ftype, f"{basename}_theta")] * np.cos(data[ (ftype, "theta")]) * np.cos([(ftype, "phi")]) - data[(ftype, f"{basename}_phi")] * np.sin(data[(ftype, "phi")])) # it's redundant to define a cartesian x field for 1D data if registry.ds.dimensionality > 1: registry.add_field( (ftype, f"{basename}_cartesian_x"), sampling_type="local", function=_cartesian_x, units=field_units, display_field=True, ) def _cartesian_y(field, data): if registry.ds.geometry == "polar": return data[(ftype, f"{basename}_r")] * np.sin( data[(ftype, "theta")]) elif registry.ds.geometry == "cylindrical": if data.ds.dimensionality == 2: return data[(ftype, f"{basename}_z")] elif data.ds.dimensionality == 3: return data[(ftype, f"{basename}_r")] * np.sin(data[ (ftype, "theta")]) + data[ (ftype, f"{basename}_theta")] * np.cos(data[ (ftype, "theta")]) elif registry.ds.geometry == "spherical": if data.ds.dimensionality == 2: return data[(ftype, f"{basename}_r")] * np.cos(data[( ftype, "theta")]) - data[f"{basename}_theta"] * np.sin( data[(ftype, "theta")]) elif data.ds.dimensionality == 3: return (data[(ftype, f"{basename}_r")] * np.sin(data[ (ftype, "theta")]) * np.sin(data[(ftype, "phi")]) + data[(ftype, f"{basename}_theta")] * np.cos(data[ (ftype, "theta")]) * np.sin([(ftype, "phi")]) + data[(ftype, f"{basename}_phi")] * np.cos(data[(ftype, "phi")])) if registry.ds.dimensionality >= 2: registry.add_field( (ftype, f"{basename}_cartesian_y"), sampling_type="local", function=_cartesian_y, units=field_units, display_field=True, ) def _cartesian_z(field, data): if registry.ds.geometry == "cylindrical": return data[(ftype, f"{basename}_z")] elif registry.ds.geometry == "spherical": return data[(ftype, f"{basename}_r")] * np.cos(data[ (ftype, "theta")]) - data[ (ftype, f"{basename}_theta")] * np.sin(data[ (ftype, "theta")]) if registry.ds.dimensionality == 3: registry.add_field( (ftype, f"{basename}_cartesian_z"), sampling_type="local", function=_cartesian_z, units=field_units, display_field=True, )
def setup_gradient_fields(registry, grad_field, field_units, slice_info=None): geom = registry.ds.geometry if is_curvilinear(geom): mylog.warning( "In %s geometry, gradient fields may contain " "artifacts near cartesian axes.", geom, ) assert isinstance(grad_field, tuple) ftype, fname = grad_field if slice_info is None: sl_left = slice(None, -2, None) sl_right = slice(2, None, None) div_fac = 2.0 else: sl_left, sl_right, div_fac = slice_info slice_3d = (slice(1, -1), slice(1, -1), slice(1, -1)) def grad_func(axi, ax): slice_3dl = slice_3d[:axi] + (sl_left, ) + slice_3d[axi + 1:] slice_3dr = slice_3d[:axi] + (sl_right, ) + slice_3d[axi + 1:] def func(field, data): block_order = getattr(data, "_block_order", "C") if block_order == "F": # Fortran-ordering: we need to swap axes here and # reswap below field_data = data[grad_field].swapaxes(0, 2) else: field_data = data[grad_field] dx = div_fac * data[ftype, f"d{ax}"] if ax == "theta": dx *= data[ftype, "r"] if ax == "phi": dx *= data[ftype, "r"] * np.sin(data[ftype, "theta"]) f = field_data[slice_3dr] / dx[slice_3d] f -= field_data[slice_3dl] / dx[slice_3d] new_field = np.zeros_like(data[grad_field], dtype=np.float64) new_field = data.ds.arr(new_field, field_data.units / dx.units) new_field[slice_3d] = f if block_order == "F": new_field = new_field.swapaxes(0, 2) return new_field return func field_units = Unit(field_units, registry=registry.ds.unit_registry) grad_units = field_units / registry.ds.unit_system["length"] for axi, ax in enumerate(registry.ds.coordinates.axis_order): f = grad_func(axi, ax) registry.add_field( (ftype, f"{fname}_gradient_{ax}"), sampling_type="local", function=f, validators=[ValidateSpatial(1, [grad_field])], units=grad_units, ) create_magnitude_field( registry, f"{fname}_gradient", grad_units, ftype=ftype, validators=[ValidateSpatial(1, [grad_field])], )
def setup_fluid_vector_fields(registry, ftype="gas", slice_info=None): # Current implementation for gradient is not valid for curvilinear geometries if is_curvilinear(registry.ds.geometry): return unit_system = registry.ds.unit_system # slice_info would be the left, the right, and the factor. # For example, with the old Enzo-ZEUS fields, this would be: # slice(None, -2, None) # slice(1, -1, None) # 1.0 # Otherwise, we default to a centered difference. if slice_info is None: sl_left = slice(None, -2, None) sl_right = slice(2, None, None) div_fac = 2.0 else: sl_left, sl_right, div_fac = slice_info sl_center = slice(1, -1, None) def _baroclinic_vorticity_x(field, data): rho2 = data[ftype, "density"].astype(np.float64)**2 return (data[ftype, "pressure_gradient_y"] * data[ftype, "density_gradient_z"] - data[ftype, "pressure_gradient_z"] * data[ftype, "density_gradient_z"]) / rho2 def _baroclinic_vorticity_y(field, data): rho2 = data[ftype, "density"].astype(np.float64)**2 return (data[ftype, "pressure_gradient_z"] * data[ftype, "density_gradient_x"] - data[ftype, "pressure_gradient_x"] * data[ftype, "density_gradient_z"]) / rho2 def _baroclinic_vorticity_z(field, data): rho2 = data[ftype, "density"].astype(np.float64)**2 return (data[ftype, "pressure_gradient_x"] * data[ftype, "density_gradient_y"] - data[ftype, "pressure_gradient_y"] * data[ftype, "density_gradient_x"]) / rho2 bv_validators = [ ValidateSpatial(1, [(ftype, "density"), (ftype, "pressure")]) ] for ax in 'xyz': n = "baroclinic_vorticity_%s" % ax registry.add_field((ftype, n), sampling_type="cell", function=eval("_%s" % n), validators=bv_validators, units=unit_system["frequency"]**2) create_magnitude_field(registry, "baroclinic_vorticity", unit_system["frequency"]**2, ftype=ftype, slice_info=slice_info, validators=bv_validators) def _vorticity_x(field, data): vz = data[ftype, "relative_velocity_z"] vy = data[ftype, "relative_velocity_y"] f = ((vz[sl_center, sl_right, sl_center] - vz[sl_center, sl_left, sl_center]) / (div_fac * just_one(data["index", "dy"]))) f -= ((vy[sl_center, sl_center, sl_right] - vy[sl_center, sl_center, sl_left]) / (div_fac * just_one(data["index", "dz"]))) new_field = data.ds.arr(np.zeros_like(vz, dtype=np.float64), f.units) new_field[sl_center, sl_center, sl_center] = f return new_field def _vorticity_y(field, data): vx = data[ftype, "relative_velocity_x"] vz = data[ftype, "relative_velocity_z"] f = ((vx[sl_center, sl_center, sl_right] - vx[sl_center, sl_center, sl_left]) / (div_fac * just_one(data["index", "dz"]))) f -= ((vz[sl_right, sl_center, sl_center] - vz[sl_left, sl_center, sl_center]) / (div_fac * just_one(data["index", "dx"]))) new_field = data.ds.arr(np.zeros_like(vz, dtype=np.float64), f.units) new_field[sl_center, sl_center, sl_center] = f return new_field def _vorticity_z(field, data): vx = data[ftype, "relative_velocity_x"] vy = data[ftype, "relative_velocity_y"] f = ((vy[sl_right, sl_center, sl_center] - vx[sl_left, sl_center, sl_center]) / (div_fac * just_one(data["index", "dx"]))) f -= ((vx[sl_center, sl_right, sl_center] - vx[sl_center, sl_left, sl_center]) / (div_fac * just_one(data["index", "dy"]))) new_field = data.ds.arr(np.zeros_like(vy, dtype=np.float64), f.units) new_field[sl_center, sl_center, sl_center] = f return new_field vort_validators = [ ValidateSpatial(1, [(ftype, "velocity_%s" % d) for d in 'xyz']), ValidateParameter('bulk_velocity') ] for ax in 'xyz': n = "vorticity_%s" % ax registry.add_field((ftype, n), sampling_type="cell", function=eval("_%s" % n), units=unit_system["frequency"], validators=vort_validators) create_magnitude_field(registry, "vorticity", unit_system["frequency"], ftype=ftype, slice_info=slice_info, validators=vort_validators) create_squared_field(registry, "vorticity", unit_system["frequency"]**2, ftype=ftype, slice_info=slice_info, validators=vort_validators) def _vorticity_stretching_x(field, data): return data[ftype, "velocity_divergence"] * data[ftype, "vorticity_x"] def _vorticity_stretching_y(field, data): return data[ftype, "velocity_divergence"] * data[ftype, "vorticity_y"] def _vorticity_stretching_z(field, data): return data[ftype, "velocity_divergence"] * data[ftype, "vorticity_z"] for ax in 'xyz': n = "vorticity_stretching_%s" % ax registry.add_field((ftype, n), sampling_type="cell", function=eval("_%s" % n), units=unit_system["frequency"]**2, validators=vort_validators) create_magnitude_field(registry, "vorticity_stretching", unit_system["frequency"]**2, ftype=ftype, slice_info=slice_info, validators=vort_validators) def _vorticity_growth_x(field, data): return -data[ftype, "vorticity_stretching_x"] - \ data[ftype, "baroclinic_vorticity_x"] def _vorticity_growth_y(field, data): return -data[ftype, "vorticity_stretching_y"] - \ data[ftype, "baroclinic_vorticity_y"] def _vorticity_growth_z(field, data): return -data[ftype, "vorticity_stretching_z"] - \ data[ftype, "baroclinic_vorticity_z"] for ax in 'xyz': n = "vorticity_growth_%s" % ax registry.add_field((ftype, n), sampling_type="cell", function=eval("_%s" % n), units=unit_system["frequency"]**2, validators=vort_validators) def _vorticity_growth_magnitude(field, data): result = np.sqrt(data[ftype, "vorticity_growth_x"]**2 + data[ftype, "vorticity_growth_y"]**2 + data[ftype, "vorticity_growth_z"]**2) dot = data.ds.arr(np.zeros(result.shape), "") for ax in "xyz": dot += (data[ftype, "vorticity_%s" % ax] * data[ftype, "vorticity_growth_%s" % ax]).to_ndarray() result = np.sign(dot) * result return result registry.add_field((ftype, "vorticity_growth_magnitude"), sampling_type="cell", function=_vorticity_growth_magnitude, units=unit_system["frequency"]**2, validators=vort_validators, take_log=False) def _vorticity_growth_magnitude_absolute(field, data): return np.sqrt(data[ftype, "vorticity_growth_x"]**2 + data[ftype, "vorticity_growth_y"]**2 + data[ftype, "vorticity_growth_z"]**2) registry.add_field((ftype, "vorticity_growth_magnitude_absolute"), sampling_type="cell", function=_vorticity_growth_magnitude_absolute, units=unit_system["frequency"]**2, validators=vort_validators) def _vorticity_growth_timescale(field, data): domegax_dt = data[ftype, "vorticity_x"] / data[ftype, "vorticity_growth_x"] domegay_dt = data[ftype, "vorticity_y"] / data[ftype, "vorticity_growth_y"] domegaz_dt = data[ftype, "vorticity_z"] / data[ftype, "vorticity_growth_z"] return np.sqrt(domegax_dt**2 + domegay_dt**2 + domegaz_dt**2) registry.add_field((ftype, "vorticity_growth_timescale"), sampling_type="cell", function=_vorticity_growth_timescale, units=unit_system["time"], validators=vort_validators) ######################################################################## # With radiation pressure ######################################################################## def _vorticity_radiation_pressure_x(field, data): rho = data[ftype, "density"].astype(np.float64) return (data[ftype, "radiation_acceleration_y"] * data[ftype, "density_gradient_z"] - data[ftype, "radiation_acceleration_z"] * data[ftype, "density_gradient_y"]) / rho def _vorticity_radiation_pressure_y(field, data): rho = data[ftype, "density"].astype(np.float64) return (data[ftype, "radiation_acceleration_z"] * data[ftype, "density_gradient_x"] - data[ftype, "radiation_acceleration_x"] * data[ftype, "density_gradient_z"]) / rho def _vorticity_radiation_pressure_z(field, data): rho = data[ftype, "density"].astype(np.float64) return (data[ftype, "radiation_acceleration_x"] * data[ftype, "density_gradient_y"] - data[ftype, "radiation_acceleration_y"] * data[ftype, "density_gradient_x"]) / rho vrp_validators = [ ValidateSpatial(1, [(ftype, "density"), (ftype, "radiation_acceleration_x"), (ftype, "radiation_acceleration_y"), (ftype, "radiation_acceleration_z")]) ] for ax in 'xyz': n = "vorticity_radiation_pressure_%s" % ax registry.add_field((ftype, n), sampling_type="cell", function=eval("_%s" % n), units=unit_system["frequency"]**2, validators=vrp_validators) create_magnitude_field(registry, "vorticity_radiation_pressure", unit_system["frequency"]**2, ftype=ftype, slice_info=slice_info, validators=vrp_validators) def _vorticity_radiation_pressure_growth_x(field, data): return -data[ftype, "vorticity_stretching_x"] - \ data[ftype, "baroclinic_vorticity_x"] \ -data[ftype, "vorticity_radiation_pressure_x"] def _vorticity_radiation_pressure_growth_y(field, data): return -data[ftype, "vorticity_stretching_y"] - \ data[ftype, "baroclinic_vorticity_y"] \ -data[ftype, "vorticity_radiation_pressure_y"] def _vorticity_radiation_pressure_growth_z(field, data): return -data[ftype, "vorticity_stretching_z"] - \ data[ftype, "baroclinic_vorticity_z"] \ -data[ftype, "vorticity_radiation_pressure_z"] for ax in 'xyz': n = "vorticity_radiation_pressure_growth_%s" % ax registry.add_field((ftype, n), sampling_type="cell", function=eval("_%s" % n), units=unit_system["frequency"]**2, validators=vrp_validators) def _vorticity_radiation_pressure_growth_magnitude(field, data): result = np.sqrt( data[ftype, "vorticity_radiation_pressure_growth_x"]**2 + data[ftype, "vorticity_radiation_pressure_growth_y"]**2 + data[ftype, "vorticity_radiation_pressure_growth_z"]**2) dot = data.ds.arr(np.zeros(result.shape), "") for ax in "xyz": dot += (data[ftype, "vorticity_%s" % ax] * data[ftype, "vorticity_growth_%s" % ax]).to_ndarray() result = np.sign(dot) * result return result registry.add_field( (ftype, "vorticity_radiation_pressure_growth_magnitude"), sampling_type="cell", function=_vorticity_radiation_pressure_growth_magnitude, units=unit_system["frequency"]**2, validators=vrp_validators, take_log=False) def _vorticity_radiation_pressure_growth_magnitude_absolute(field, data): return np.sqrt( data[ftype, "vorticity_radiation_pressure_growth_x"]**2 + data[ftype, "vorticity_radiation_pressure_growth_y"]**2 + data[ftype, "vorticity_radiation_pressure_growth_z"]**2) registry.add_field( (ftype, "vorticity_radiation_pressure_growth_magnitude_absolute"), sampling_type="cell", function=_vorticity_radiation_pressure_growth_magnitude_absolute, units="s**(-2)", validators=vrp_validators) def _vorticity_radiation_pressure_growth_timescale(field, data): domegax_dt = data[ftype, "vorticity_x"] / \ data[ftype, "vorticity_radiation_pressure_growth_x"] domegay_dt = data[ftype, "vorticity_y"] / \ data[ftype, "vorticity_radiation_pressure_growth_y"] domegaz_dt = data[ftype, "vorticity_z"] / \ data[ftype, "vorticity_radiation_pressure_growth_z"] return np.sqrt(domegax_dt**2 + domegay_dt**2 + domegaz_dt**2) registry.add_field( (ftype, "vorticity_radiation_pressure_growth_timescale"), sampling_type="cell", function=_vorticity_radiation_pressure_growth_timescale, units=unit_system["time"], validators=vrp_validators) def _shear(field, data): """ Shear is defined as [(dvx/dy + dvy/dx)^2 + (dvz/dy + dvy/dz)^2 + (dvx/dz + dvz/dx)^2 ]^(0.5) where dvx/dy = [vx(j-1) - vx(j+1)]/[2dy] and is in units of s^(-1) (it's just like vorticity except add the derivative pairs instead of subtracting them) """ if data.ds.dimensionality > 1: vx = data[ftype, "relative_velocity_x"] vy = data[ftype, "relative_velocity_y"] dvydx = ((vy[sl_right, sl_center, sl_center] - vy[sl_left, sl_center, sl_center]) / (div_fac * just_one(data["index", "dx"]))) dvxdy = ((vx[sl_center, sl_right, sl_center] - vx[sl_center, sl_left, sl_center]) / (div_fac * just_one(data["index", "dy"]))) f = (dvydx + dvxdy)**2.0 del dvydx, dvxdy if data.ds.dimensionality > 2: vz = data[ftype, "relative_velocity_z"] dvzdy = ((vz[sl_center, sl_right, sl_center] - vz[sl_center, sl_left, sl_center]) / (div_fac * just_one(data["index", "dy"]))) dvydz = ((vy[sl_center, sl_center, sl_right] - vy[sl_center, sl_center, sl_left]) / (div_fac * just_one(data["index", "dz"]))) f += (dvzdy + dvydz)**2.0 del dvzdy, dvydz dvxdz = ((vx[sl_center, sl_center, sl_right] - vx[sl_center, sl_center, sl_left]) / (div_fac * just_one(data["index", "dz"]))) dvzdx = ((vz[sl_right, sl_center, sl_center] - vz[sl_left, sl_center, sl_center]) / (div_fac * just_one(data["index", "dx"]))) f += (dvxdz + dvzdx)**2.0 del dvxdz, dvzdx np.sqrt(f, out=f) new_field = data.ds.arr(np.zeros_like(data[ftype, "velocity_x"]), f.units) new_field[sl_center, sl_center, sl_center] = f return new_field registry.add_field((ftype, "shear"), sampling_type="cell", function=_shear, validators=[ ValidateSpatial(1, [(ftype, "velocity_x"), (ftype, "velocity_y"), (ftype, "velocity_z")]), ValidateParameter('bulk_velocity') ], units=unit_system["frequency"]) def _shear_criterion(field, data): """ Divide by c_s to leave shear in units of length**-1, which can be compared against the inverse of the local cell size (1/dx) to determine if refinement should occur. """ return data[ftype, "shear"] / data[ftype, "sound_speed"] registry.add_field((ftype, "shear_criterion"), sampling_type="cell", function=_shear_criterion, units=unit_system["length"]**-1, validators=[ ValidateSpatial(1, [(ftype, "sound_speed"), (ftype, "velocity_x"), (ftype, "velocity_y"), (ftype, "velocity_z")]) ]) def _shear_mach(field, data): """ Dimensionless shear (shear_mach) is defined nearly the same as shear, except that it is scaled by the local dx/dy/dz and the local sound speed. So it results in a unitless quantity that is effectively measuring shear in mach number. In order to avoid discontinuities created by multiplying by dx/dy/dz at grid refinement boundaries, we also multiply by 2**GridLevel. Shear (Mach) = [(dvx + dvy)^2 + (dvz + dvy)^2 + (dvx + dvz)^2 ]^(0.5) / c_sound """ if data.ds.dimensionality > 1: vx = data[ftype, "relative_velocity_x"] vy = data[ftype, "relative_velocity_y"] dvydx = (vy[sl_right, sl_center, sl_center] - vy[sl_left, sl_center, sl_center]) / div_fac dvxdy = (vx[sl_center, sl_right, sl_center] - vx[sl_center, sl_left, sl_center]) / div_fac f = (dvydx + dvxdy)**2.0 del dvydx, dvxdy if data.ds.dimensionality > 2: vz = data[ftype, "relative_velocity_z"] dvzdy = (vz[sl_center, sl_right, sl_center] - vz[sl_center, sl_left, sl_center]) / div_fac dvydz = (vy[sl_center, sl_center, sl_right] - vy[sl_center, sl_center, sl_left]) / div_fac f += (dvzdy + dvydz)**2.0 del dvzdy, dvydz dvxdz = (vx[sl_center, sl_center, sl_right] - vx[sl_center, sl_center, sl_left]) / div_fac dvzdx = (vz[sl_right, sl_center, sl_center] - vz[sl_left, sl_center, sl_center]) / div_fac f += (dvxdz + dvzdx)**2.0 del dvxdz, dvzdx f *= ( 2.0**data["index", "grid_level"][sl_center, sl_center, sl_center] / data[ftype, "sound_speed"][sl_center, sl_center, sl_center])**2.0 np.sqrt(f, out=f) new_field = data.ds.arr(np.zeros_like(vx), f.units) new_field[sl_center, sl_center, sl_center] = f return new_field vs_fields = [(ftype, "sound_speed"), (ftype, "velocity_x"), (ftype, "velocity_y"), (ftype, "velocity_z")] registry.add_field((ftype, "shear_mach"), sampling_type="cell", function=_shear_mach, units="", validators=[ ValidateSpatial(1, vs_fields), ValidateParameter('bulk_velocity') ])