Source code for astronomix._fluid_equations._eigen_mhd

"""
Computations of the eigenvalues and eigenvectors for the MHD equations.

The eigenstructure was extracted from the HOW-MHD Fortran code, with altered
variable names for clarity and altered numerical safeguards.

NOTE: Problems for differentiation largely follow from square roots and divisions:
The derivative of sqrt(x) is 1/(2*sqrt(x)) and of 1/x is -1/x^2, where both expressions
are problematic for small x, especially when multiplying gradients in the backward pass,
-> exploding gradients.
"""

# general
from functools import partial

# typing
from typing import Union

# jax
import jax
import jax.numpy as jnp

# astronomix containers
from astronomix.option_classes.simulation_config import SimulationConfig
from astronomix.variable_registry.registered_variables import RegisteredVariables

# astronomix functions
from astronomix._stencil_operations._stencil_operations import _shift


[docs] def diff_safe_sqrt(x): """Square root with a small floor, so its derivative stays finite at x = 0. The derivative of sqrt(x) is 1 / (2 sqrt(x)), which blows up as x -> 0; clamping the argument to a tiny epsilon keeps the backward pass well-behaved. The floor is tighter under x64 than under x32 to match the available precision. """ if jax.config.jax_enable_x64: eps = 1e-30 else: eps = 1e-20 epsilon = eps x_safe = jnp.maximum(x, epsilon) return jnp.sqrt(x_safe)
@partial(jax.jit, static_argnames=["registered_variables"]) def _eigenvalue_building_blocks( conserved_state, gamma, rhomin, pgmin, registered_variables: RegisteredVariables, ): # unpack the conserved variables density = conserved_state[registered_variables.density_index] momentum_x = conserved_state[registered_variables.momentum_index.x] momentum_y = conserved_state[registered_variables.momentum_index.y] momentum_z = conserved_state[registered_variables.momentum_index.z] magnetic_x = conserved_state[registered_variables.magnetic_index.x] magnetic_y = conserved_state[registered_variables.magnetic_index.y] magnetic_z = conserved_state[registered_variables.magnetic_index.z] energy = conserved_state[registered_variables.energy_index] # compute primitives rho = density velocity_x = momentum_x / rho velocity_y = momentum_y / rho velocity_z = momentum_z / rho velocity_squared = ( velocity_x * velocity_x + velocity_y * velocity_y + velocity_z * velocity_z ) magnetic_field_squared = ( magnetic_x * magnetic_x + magnetic_y * magnetic_y + magnetic_z * magnetic_z ) gas_pressure = (gamma - 1.0) * ( energy - 0.5 * (rho * velocity_squared + magnetic_field_squared) ) # redefine the density and pressure, and energy based on floors rho = jnp.where( (rho < rhomin) | (gas_pressure < pgmin), jnp.maximum(rho, rhomin), rho ) gas_pressure = jnp.where( (rho < rhomin) | (gas_pressure < pgmin), jnp.maximum(gas_pressure, pgmin), gas_pressure, ) energy = jnp.where( (rho < rhomin) | (gas_pressure < pgmin), gas_pressure / (gamma - 1.0) + 0.5 * (rho * velocity_squared + magnetic_field_squared), energy, ) # compute derived quantities sound_speed_sq = jnp.maximum(0.0, gamma * jnp.abs(gas_pressure / rho)) magnetosonic_discriminant_root = diff_safe_sqrt( jnp.maximum( 0.0, (magnetic_field_squared / rho + sound_speed_sq) ** 2 - 4.0 * (magnetic_x * magnetic_x) / rho * sound_speed_sq, ) ) fast_magnetosonic_velocity = diff_safe_sqrt( jnp.maximum( 0.0, 0.5 * ( magnetic_field_squared / rho + sound_speed_sq + magnetosonic_discriminant_root ), ) ) alfven_velocity = diff_safe_sqrt(jnp.maximum(0.0, (magnetic_x * magnetic_x) / rho)) slow_magnetosonic_velocity = diff_safe_sqrt( jnp.maximum( 0.0, 0.5 * ( magnetic_field_squared / rho + sound_speed_sq - magnetosonic_discriminant_root ), ) ) return ( velocity_x, fast_magnetosonic_velocity, alfven_velocity, slow_magnetosonic_velocity, ) @partial(jax.jit, static_argnames=["registered_variables"]) def _eigenvector_building_blocks( conserved_state, gamma, rhomin, pgmin, registered_variables: RegisteredVariables, ): if jax.config.jax_enable_x64: eps = 1e-30 else: eps = 1e-20 # Unpack the conserved variables. rho = conserved_state[registered_variables.density_index] momentum_x = conserved_state[registered_variables.momentum_index.x] momentum_y = conserved_state[registered_variables.momentum_index.y] momentum_z = conserved_state[registered_variables.momentum_index.z] magnetic_x = conserved_state[registered_variables.magnetic_index.x] magnetic_y = conserved_state[registered_variables.magnetic_index.y] magnetic_z = conserved_state[registered_variables.magnetic_index.z] energy = conserved_state[registered_variables.energy_index] # Compute the primitive quantities. velocity_x = momentum_x / rho velocity_y = momentum_y / rho velocity_z = momentum_z / rho velocity_sq = ( velocity_x * velocity_x + velocity_y * velocity_y + velocity_z * velocity_z ) magnetic_sq = ( magnetic_x * magnetic_x + magnetic_y * magnetic_y + magnetic_z * magnetic_z ) gas_pressure = (gamma - 1.0) * (energy - 0.5 * (rho * velocity_sq + magnetic_sq)) # redefine the density and pressure, and energy based on floors rho = jnp.where( (rho < rhomin) | (gas_pressure < pgmin), jnp.maximum(rho, rhomin), rho ) gas_pressure = jnp.where( (rho < rhomin) | (gas_pressure < pgmin), jnp.maximum(gas_pressure, pgmin), gas_pressure, ) energy = jnp.where( (rho < rhomin) | (gas_pressure < pgmin), gas_pressure / (gamma - 1.0) + 0.5 * (rho * velocity_sq + magnetic_sq), energy, ) specific_enthalpy = (energy + gas_pressure) / rho # Periodic average from cell centres to interfaces. def avg_x(arr): return 0.5 * (arr + _shift(arr, shift=-1, axis=0)) # Average the momenta (rather than the velocities) to the interface and divide # by the interface density; this keeps the interface velocity consistent with # the averaged conserved quantities. rho_interface = avg_x(jnp.maximum(rho, rhomin)) rho_interface = jnp.maximum(rho_interface, rhomin) velocity_x_interface = avg_x(momentum_x) / rho_interface velocity_y_interface = avg_x(momentum_y) / rho_interface velocity_z_interface = avg_x(momentum_z) / rho_interface magnetic_x_interface = avg_x(magnetic_x) magnetic_y_interface = avg_x(magnetic_y) magnetic_z_interface = avg_x(magnetic_z) specific_enthalpy_interface = avg_x(specific_enthalpy) # interface derived quantities velocity_sq_interface = ( velocity_x_interface * velocity_x_interface + velocity_y_interface * velocity_y_interface + velocity_z_interface * velocity_z_interface ) magnetic_sq_interface = ( magnetic_x_interface * magnetic_x_interface + magnetic_y_interface * magnetic_y_interface + magnetic_z_interface * magnetic_z_interface ) b_sq_over_rho_interface = magnetic_sq_interface / rho_interface bx_sq_over_rho_interface = ( magnetic_x_interface * magnetic_x_interface ) / rho_interface # enthalpy based sound speed at interfaces sound_speed_sq_interface = (gamma - 1.0) * ( specific_enthalpy_interface - 0.5 * (velocity_sq_interface + b_sq_over_rho_interface) ) sound_speed_interface = diff_safe_sqrt(jnp.maximum(0.0, sound_speed_sq_interface)) # calculate the characteristic velocities at the interfaces magnetosonic_discriminant_interface = ( b_sq_over_rho_interface + sound_speed_sq_interface ) ** 2 - 4.0 * bx_sq_over_rho_interface * sound_speed_sq_interface magnetosonic_discriminant_root_interface = diff_safe_sqrt( jnp.maximum(0.0, magnetosonic_discriminant_interface) ) fast_magnetosonic_velocity_interface = diff_safe_sqrt( jnp.maximum( 0.0, 0.5 * ( b_sq_over_rho_interface + sound_speed_sq_interface + magnetosonic_discriminant_root_interface ), ) ) alfven_velocity_interface = diff_safe_sqrt( jnp.maximum(0.0, bx_sq_over_rho_interface) ) slow_magnetosonic_velocity_interface = diff_safe_sqrt( jnp.maximum( 0.0, 0.5 * ( b_sq_over_rho_interface + sound_speed_sq_interface - magnetosonic_discriminant_root_interface ), ) ) # retrieve tangential magnetic field components b_tangential_sq = ( magnetic_y_interface * magnetic_y_interface + magnetic_z_interface * magnetic_z_interface ) sgn_bx = jnp.where(magnetic_x_interface >= 0.0, 1.0, -1.0) b_tangential_sq_safe = jnp.maximum(b_tangential_sq, eps) # B_y / (sqrt(B_y^2 + B_z^2)) bt_normalized_y = jnp.where( b_tangential_sq >= eps, magnetic_y_interface / jnp.sqrt(b_tangential_sq_safe), 1.0 / jnp.sqrt(2.0), ) # B_z / (sqrt(B_y^2 + B_z^2)) bt_normalized_z = jnp.where( b_tangential_sq >= eps, magnetic_z_interface / jnp.sqrt(b_tangential_sq_safe), 1.0 / jnp.sqrt(2.0), ) # fast_mode_weighting = sqrt( c_s^2 − λ_slow^2 ) / sqrt( λ_fast^2 − λ_slow^2 ) # slow_mode_weighting = sqrt( λ_fast^2 − c_s^2 ) / sqrt( λ_fast^2 − λ_slow^2 ) # these are designed such that fast_mode_weighting^2 + slow_mode_weighting^2 = 1 denom = ( fast_magnetosonic_velocity_interface * fast_magnetosonic_velocity_interface - slow_magnetosonic_velocity_interface * slow_magnetosonic_velocity_interface ) denom_safe = jnp.maximum(denom, eps) fast_mode_weighting = jnp.where( denom >= eps, diff_safe_sqrt( jnp.maximum( 0.0, sound_speed_sq_interface - slow_magnetosonic_velocity_interface * slow_magnetosonic_velocity_interface, ) ) / diff_safe_sqrt(denom_safe), 1.0, ) slow_mode_weighting = jnp.where( denom >= eps, diff_safe_sqrt( jnp.maximum( 0.0, fast_magnetosonic_velocity_interface * fast_magnetosonic_velocity_interface - sound_speed_sq_interface, ) ) / diff_safe_sqrt(denom_safe), 1.0, ) sqrt_rho = diff_safe_sqrt(rho_interface) gam0 = 1.0 - gamma gam1 = 0.5 * (gamma - 1.0) gam2 = (gamma - 2.0) / (gamma - 1.0) sound_speed_sq_inverse = jnp.where( sound_speed_sq_interface > 0.0, 1.0 / sound_speed_sq_interface, 0.0 ) sgn_bt = jnp.where( magnetic_y_interface != 0.0, jnp.where(magnetic_y_interface >= 0.0, 1.0, -1.0), jnp.where(magnetic_z_interface >= 0.0, 1.0, -1.0), ) sound_speed_greater_alfven_speed = ( sound_speed_interface >= alfven_velocity_interface ) return ( rho_interface, sqrt_rho, velocity_x_interface, velocity_y_interface, velocity_z_interface, velocity_sq_interface, magnetic_x_interface, magnetic_y_interface, magnetic_z_interface, bt_normalized_y, bt_normalized_z, sgn_bx, sgn_bt, sound_speed_interface, sound_speed_sq_interface, sound_speed_sq_inverse, sound_speed_greater_alfven_speed, fast_magnetosonic_velocity_interface, alfven_velocity_interface, slow_magnetosonic_velocity_interface, fast_mode_weighting, slow_mode_weighting, gam0, gam1, gam2, ) @partial(jax.jit, static_argnames=["registered_variables"]) def _eigen_R_col( conserved_state, rhomin: Union[float, jnp.ndarray], pgmin: Union[float, jnp.ndarray], gamma: Union[float, jnp.ndarray], registered_variables: RegisteredVariables, col: int, ): ( rho_interface, sqrt_rho, velocity_x_interface, velocity_y_interface, velocity_z_interface, velocity_sq_interface, magnetic_x_interface, magnetic_y_interface, magnetic_z_interface, bt_normalized_y, bt_normalized_z, sgn_bx, sgn_bt, sound_speed_interface, sound_speed_sq_interface, sound_speed_sq_inverse, sound_speed_greater_alfven_speed, fast_magnetosonic_velocity_interface, alfven_velocity_interface, slow_magnetosonic_velocity_interface, fast_mode_weighting, slow_mode_weighting, gam0, gam1, gam2, ) = _eigenvector_building_blocks( conserved_state, gamma, rhomin, pgmin, registered_variables, ) # shorter names for registry indices density_index = registered_variables.density_index momentum_index_x = registered_variables.momentum_index.x momentum_index_y = registered_variables.momentum_index.y momentum_index_z = registered_variables.momentum_index.z magnetic_index_y = registered_variables.magnetic_index.y magnetic_index_z = registered_variables.magnetic_index.z energy_index = registered_variables.energy_index def col_0(): # Column 1 (fast -) R = jnp.zeros_like(conserved_state) R = R.at[density_index].set(fast_mode_weighting) R = R.at[momentum_index_x].set( fast_mode_weighting * (velocity_x_interface - fast_magnetosonic_velocity_interface) ) R = R.at[momentum_index_y].set( fast_mode_weighting * velocity_y_interface + slow_mode_weighting * slow_magnetosonic_velocity_interface * bt_normalized_y * sgn_bx ) R = R.at[momentum_index_z].set( fast_mode_weighting * velocity_z_interface + slow_mode_weighting * slow_magnetosonic_velocity_interface * bt_normalized_z * sgn_bx ) R = R.at[magnetic_index_y].set( sound_speed_interface * slow_mode_weighting * bt_normalized_y / sqrt_rho ) R = R.at[magnetic_index_z].set( sound_speed_interface * slow_mode_weighting * bt_normalized_z / sqrt_rho ) R = R.at[energy_index].set( fast_mode_weighting * ( fast_magnetosonic_velocity_interface**2 - fast_magnetosonic_velocity_interface * velocity_x_interface + 0.5 * velocity_sq_interface - gam2 * sound_speed_sq_interface ) + slow_mode_weighting * slow_magnetosonic_velocity_interface * ( bt_normalized_y * velocity_y_interface + bt_normalized_z * velocity_z_interface ) * sgn_bx ) R = jnp.where(~sound_speed_greater_alfven_speed, R * sgn_bt, R) return R def col_1(): # Column 2 (alfven -) R = jnp.zeros_like(conserved_state) R = R.at[density_index].set(0.0) R = R.at[momentum_index_x].set(0.0) R = R.at[momentum_index_y].set(-bt_normalized_z) R = R.at[momentum_index_z].set(bt_normalized_y) R = R.at[magnetic_index_y].set(-bt_normalized_z * sgn_bx / sqrt_rho) R = R.at[magnetic_index_z].set(bt_normalized_y * sgn_bx / sqrt_rho) R = R.at[energy_index].set( bt_normalized_y * velocity_z_interface - bt_normalized_z * velocity_y_interface ) return R def col_2(): R = jnp.zeros_like(conserved_state) R = R.at[density_index].set(slow_mode_weighting) R = R.at[momentum_index_x].set( slow_mode_weighting * (velocity_x_interface - slow_magnetosonic_velocity_interface) ) R = R.at[momentum_index_y].set( slow_mode_weighting * velocity_y_interface - fast_mode_weighting * fast_magnetosonic_velocity_interface * bt_normalized_y * sgn_bx ) R = R.at[momentum_index_z].set( slow_mode_weighting * velocity_z_interface - fast_mode_weighting * fast_magnetosonic_velocity_interface * bt_normalized_z * sgn_bx ) R = R.at[magnetic_index_y].set( -sound_speed_interface * fast_mode_weighting * bt_normalized_y / sqrt_rho ) R = R.at[magnetic_index_z].set( -sound_speed_interface * fast_mode_weighting * bt_normalized_z / sqrt_rho ) R = R.at[energy_index].set( slow_mode_weighting * ( slow_magnetosonic_velocity_interface**2 - slow_magnetosonic_velocity_interface * velocity_x_interface + 0.5 * velocity_sq_interface - gam2 * sound_speed_sq_interface ) - fast_mode_weighting * fast_magnetosonic_velocity_interface * ( bt_normalized_y * velocity_y_interface + bt_normalized_z * velocity_z_interface ) * sgn_bx ) R = jnp.where(sound_speed_greater_alfven_speed, R * sgn_bt, R) return R def col_3(): R = jnp.zeros_like(conserved_state) R = R.at[density_index].set(1.0) R = R.at[momentum_index_x].set(velocity_x_interface) R = R.at[momentum_index_y].set(velocity_y_interface) R = R.at[momentum_index_z].set(velocity_z_interface) R = R.at[magnetic_index_y].set(0.0) R = R.at[magnetic_index_z].set(0.0) R = R.at[energy_index].set(0.5 * velocity_sq_interface) return R def col_4(): # Column 5 (slow +) R = jnp.zeros_like(conserved_state) R = R.at[density_index].set(slow_mode_weighting) R = R.at[momentum_index_x].set( slow_mode_weighting * (velocity_x_interface + slow_magnetosonic_velocity_interface) ) R = R.at[momentum_index_y].set( slow_mode_weighting * velocity_y_interface + fast_mode_weighting * fast_magnetosonic_velocity_interface * bt_normalized_y * sgn_bx ) R = R.at[momentum_index_z].set( slow_mode_weighting * velocity_z_interface + fast_mode_weighting * fast_magnetosonic_velocity_interface * bt_normalized_z * sgn_bx ) R = R.at[magnetic_index_y].set( -sound_speed_interface * fast_mode_weighting * bt_normalized_y / sqrt_rho ) R = R.at[magnetic_index_z].set( -sound_speed_interface * fast_mode_weighting * bt_normalized_z / sqrt_rho ) R = R.at[energy_index].set( slow_mode_weighting * ( slow_magnetosonic_velocity_interface**2 + slow_magnetosonic_velocity_interface * velocity_x_interface + 0.5 * velocity_sq_interface - gam2 * sound_speed_sq_interface ) + fast_mode_weighting * fast_magnetosonic_velocity_interface * ( bt_normalized_y * velocity_y_interface + bt_normalized_z * velocity_z_interface ) * sgn_bx ) R = jnp.where(sound_speed_greater_alfven_speed, R * sgn_bt, R) return R def col_5(): # Column 6 (alfven +) R = jnp.zeros_like(conserved_state) R = R.at[density_index].set(0.0) R = R.at[momentum_index_x].set(0.0) R = R.at[momentum_index_y].set(-bt_normalized_z) R = R.at[momentum_index_z].set(bt_normalized_y) R = R.at[magnetic_index_y].set(bt_normalized_z * sgn_bx / sqrt_rho) R = R.at[magnetic_index_z].set(-bt_normalized_y * sgn_bx / sqrt_rho) R = R.at[energy_index].set( bt_normalized_y * velocity_z_interface - bt_normalized_z * velocity_y_interface ) return R def col_6(): # Column 7 (fast +) R = jnp.zeros_like(conserved_state) R = R.at[density_index].set(fast_mode_weighting) R = R.at[momentum_index_x].set( fast_mode_weighting * (velocity_x_interface + fast_magnetosonic_velocity_interface) ) R = R.at[momentum_index_y].set( fast_mode_weighting * velocity_y_interface - slow_mode_weighting * slow_magnetosonic_velocity_interface * bt_normalized_y * sgn_bx ) R = R.at[momentum_index_z].set( fast_mode_weighting * velocity_z_interface - slow_mode_weighting * slow_magnetosonic_velocity_interface * bt_normalized_z * sgn_bx ) R = R.at[magnetic_index_y].set( sound_speed_interface * slow_mode_weighting * bt_normalized_y / sqrt_rho ) R = R.at[magnetic_index_z].set( sound_speed_interface * slow_mode_weighting * bt_normalized_z / sqrt_rho ) R = R.at[energy_index].set( fast_mode_weighting * ( fast_magnetosonic_velocity_interface**2 + fast_magnetosonic_velocity_interface * velocity_x_interface + 0.5 * velocity_sq_interface - gam2 * sound_speed_sq_interface ) - slow_mode_weighting * slow_magnetosonic_velocity_interface * ( bt_normalized_y * velocity_y_interface + bt_normalized_z * velocity_z_interface ) * sgn_bx ) R = jnp.where(~sound_speed_greater_alfven_speed, R * sgn_bt, R) return R R = jax.lax.switch(col, [col_0, col_1, col_2, col_3, col_4, col_5, col_6]) return R @partial(jax.jit, static_argnames=["registered_variables"]) def _eigen_L_row( conserved_state, rhomin: Union[float, jnp.ndarray], pgmin: Union[float, jnp.ndarray], gamma: Union[float, jnp.ndarray], registered_variables: RegisteredVariables, row: int, ): ( rho_interface, sqrt_rho, velocity_x_interface, velocity_y_interface, velocity_z_interface, velocity_sq_interface, magnetic_x_interface, magnetic_y_interface, magnetic_z_interface, bt_normalized_y, bt_normalized_z, sgn_bx, sgn_bt, sound_speed_interface, sound_speed_sq_interface, sound_speed_sq_inverse, sound_speed_greater_alfven_speed, fast_magnetosonic_velocity_interface, alfven_velocity_interface, slow_magnetosonic_velocity_interface, fast_mode_weighting, slow_mode_weighting, gam0, gam1, gam2, ) = _eigenvector_building_blocks( conserved_state, gamma, rhomin, pgmin, registered_variables, ) # shorter names for registry indices density_index = registered_variables.density_index momentum_index_x = registered_variables.momentum_index.x momentum_index_y = registered_variables.momentum_index.y momentum_index_z = registered_variables.momentum_index.z magnetic_index_y = registered_variables.magnetic_index.y magnetic_index_z = registered_variables.magnetic_index.z energy_index = registered_variables.energy_index def row_0(): L = jnp.zeros_like(conserved_state) L = L.at[density_index].set( fast_mode_weighting * ( gam1 * velocity_sq_interface + fast_magnetosonic_velocity_interface * velocity_x_interface ) - slow_mode_weighting * slow_magnetosonic_velocity_interface * ( bt_normalized_y * velocity_y_interface + bt_normalized_z * velocity_z_interface ) * sgn_bx ) L = L.at[momentum_index_x].set( fast_mode_weighting * (gam0 * velocity_x_interface - fast_magnetosonic_velocity_interface) ) L = L.at[momentum_index_y].set( gam0 * fast_mode_weighting * velocity_y_interface + slow_mode_weighting * slow_magnetosonic_velocity_interface * bt_normalized_y * sgn_bx ) L = L.at[momentum_index_z].set( gam0 * fast_mode_weighting * velocity_z_interface + slow_mode_weighting * slow_magnetosonic_velocity_interface * bt_normalized_z * sgn_bx ) L = L.at[magnetic_index_y].set( gam0 * fast_mode_weighting * magnetic_y_interface + sound_speed_interface * slow_mode_weighting * bt_normalized_y * sqrt_rho ) L = L.at[magnetic_index_z].set( gam0 * fast_mode_weighting * magnetic_z_interface + sound_speed_interface * slow_mode_weighting * bt_normalized_z * sqrt_rho ) L = L.at[energy_index].set(-gam0 * fast_mode_weighting) L = 0.5 * L * sound_speed_sq_inverse L = jnp.where(~sound_speed_greater_alfven_speed, L * sgn_bt, L) return L def row_1(): L = jnp.zeros_like(conserved_state) L = L.at[density_index].set( bt_normalized_z * velocity_y_interface - bt_normalized_y * velocity_z_interface ) L = L.at[momentum_index_x].set(0.0) L = L.at[momentum_index_y].set(-bt_normalized_z) L = L.at[momentum_index_z].set(bt_normalized_y) L = L.at[magnetic_index_y].set(-bt_normalized_z * sgn_bx * sqrt_rho) L = L.at[magnetic_index_z].set(bt_normalized_y * sgn_bx * sqrt_rho) L = L.at[energy_index].set(0.0) L = 0.5 * L return L def row_2(): L = jnp.zeros_like(conserved_state) L = L.at[density_index].set( slow_mode_weighting * ( gam1 * velocity_sq_interface + slow_magnetosonic_velocity_interface * velocity_x_interface ) + fast_mode_weighting * fast_magnetosonic_velocity_interface * ( bt_normalized_y * velocity_y_interface + bt_normalized_z * velocity_z_interface ) * sgn_bx ) L = L.at[momentum_index_x].set( gam0 * slow_mode_weighting * velocity_x_interface - slow_mode_weighting * slow_magnetosonic_velocity_interface ) L = L.at[momentum_index_y].set( gam0 * slow_mode_weighting * velocity_y_interface - fast_mode_weighting * fast_magnetosonic_velocity_interface * bt_normalized_y * sgn_bx ) L = L.at[momentum_index_z].set( gam0 * slow_mode_weighting * velocity_z_interface - fast_mode_weighting * fast_magnetosonic_velocity_interface * bt_normalized_z * sgn_bx ) L = L.at[magnetic_index_y].set( gam0 * slow_mode_weighting * magnetic_y_interface - sound_speed_interface * fast_mode_weighting * bt_normalized_y * sqrt_rho ) L = L.at[magnetic_index_z].set( gam0 * slow_mode_weighting * magnetic_z_interface - sound_speed_interface * fast_mode_weighting * bt_normalized_z * sqrt_rho ) L = L.at[energy_index].set(-gam0 * slow_mode_weighting) L = 0.5 * L * sound_speed_sq_inverse L = jnp.where(sound_speed_greater_alfven_speed, L * sgn_bt, L) return L def row_3(): L = jnp.zeros_like(conserved_state) L = L.at[density_index].set( -sound_speed_sq_interface / gam0 - 0.5 * velocity_sq_interface ) L = L.at[momentum_index_x].set(velocity_x_interface) L = L.at[momentum_index_y].set(velocity_y_interface) L = L.at[momentum_index_z].set(velocity_z_interface) L = L.at[magnetic_index_y].set(magnetic_y_interface) L = L.at[magnetic_index_z].set(magnetic_z_interface) L = L.at[energy_index].set(-1.0) L = -gam0 * L * sound_speed_sq_inverse return L def row_4(): L = jnp.zeros_like(conserved_state) L = L.at[density_index].set( slow_mode_weighting * ( gam1 * velocity_sq_interface - slow_magnetosonic_velocity_interface * velocity_x_interface ) - fast_mode_weighting * fast_magnetosonic_velocity_interface * ( bt_normalized_y * velocity_y_interface + bt_normalized_z * velocity_z_interface ) * sgn_bx ) L = L.at[momentum_index_x].set( slow_mode_weighting * (gam0 * velocity_x_interface + slow_magnetosonic_velocity_interface) ) L = L.at[momentum_index_y].set( gam0 * slow_mode_weighting * velocity_y_interface + fast_mode_weighting * fast_magnetosonic_velocity_interface * bt_normalized_y * sgn_bx ) L = L.at[momentum_index_z].set( gam0 * slow_mode_weighting * velocity_z_interface + fast_mode_weighting * fast_magnetosonic_velocity_interface * bt_normalized_z * sgn_bx ) L = L.at[magnetic_index_y].set( gam0 * slow_mode_weighting * magnetic_y_interface - sound_speed_interface * fast_mode_weighting * bt_normalized_y * sqrt_rho ) L = L.at[magnetic_index_z].set( gam0 * slow_mode_weighting * magnetic_z_interface - sound_speed_interface * fast_mode_weighting * bt_normalized_z * sqrt_rho ) L = L.at[energy_index].set(-gam0 * slow_mode_weighting) L = 0.5 * L * sound_speed_sq_inverse L = jnp.where(sound_speed_greater_alfven_speed, L * sgn_bt, L) return L def row_5(): L = jnp.zeros_like(conserved_state) L = L.at[density_index].set( bt_normalized_z * velocity_y_interface - bt_normalized_y * velocity_z_interface ) L = L.at[momentum_index_x].set(0.0) L = L.at[momentum_index_y].set(-bt_normalized_z) L = L.at[momentum_index_z].set(bt_normalized_y) L = L.at[magnetic_index_y].set(bt_normalized_z * sgn_bx * sqrt_rho) L = L.at[magnetic_index_z].set(-bt_normalized_y * sgn_bx * sqrt_rho) L = L.at[energy_index].set(0.0) L = 0.5 * L return L def row_6(): L = jnp.zeros_like(conserved_state) L = L.at[density_index].set( fast_mode_weighting * ( gam1 * velocity_sq_interface - fast_magnetosonic_velocity_interface * velocity_x_interface ) + slow_mode_weighting * slow_magnetosonic_velocity_interface * ( bt_normalized_y * velocity_y_interface + bt_normalized_z * velocity_z_interface ) * sgn_bx ) L = L.at[momentum_index_x].set( fast_mode_weighting * (gam0 * velocity_x_interface + fast_magnetosonic_velocity_interface) ) L = L.at[momentum_index_y].set( gam0 * fast_mode_weighting * velocity_y_interface - slow_mode_weighting * slow_magnetosonic_velocity_interface * bt_normalized_y * sgn_bx ) L = L.at[momentum_index_z].set( gam0 * fast_mode_weighting * velocity_z_interface - slow_mode_weighting * slow_magnetosonic_velocity_interface * bt_normalized_z * sgn_bx ) L = L.at[magnetic_index_y].set( gam0 * fast_mode_weighting * magnetic_y_interface + sound_speed_interface * slow_mode_weighting * bt_normalized_y * sqrt_rho ) L = L.at[magnetic_index_z].set( gam0 * fast_mode_weighting * magnetic_z_interface + sound_speed_interface * slow_mode_weighting * bt_normalized_z * sqrt_rho ) L = L.at[energy_index].set(-gam0 * fast_mode_weighting) L = 0.5 * L * sound_speed_sq_inverse L = jnp.where(~sound_speed_greater_alfven_speed, L * sgn_bt, L) return L L = jax.lax.switch(row, [row_0, row_1, row_2, row_3, row_4, row_5, row_6]) return L @partial(jax.jit, static_argnames=["registered_variables"]) def _eigen_all_lambdas( conserved_state, rhomin: Union[float, jnp.ndarray], pgmin: Union[float, jnp.ndarray], gamma: Union[float, jnp.ndarray], registered_variables: RegisteredVariables, ): ( velocity_x, fast_magnetosonic_velocity, alfven_velocity, slow_magnetosonic_velocity, ) = _eigenvalue_building_blocks( conserved_state, gamma, rhomin, pgmin, registered_variables, ) return jnp.stack( [ velocity_x - fast_magnetosonic_velocity, velocity_x - alfven_velocity, velocity_x - slow_magnetosonic_velocity, velocity_x, velocity_x + slow_magnetosonic_velocity, velocity_x + alfven_velocity, velocity_x + fast_magnetosonic_velocity, ], axis=0, ) def _eigen_lambdas( conserved_state, rhomin: Union[float, jnp.ndarray], pgmin: Union[float, jnp.ndarray], gamma: Union[float, jnp.ndarray], registered_variables: RegisteredVariables, mode: int, ): ( velocity_x, fast_magnetosonic_velocity, alfven_velocity, slow_magnetosonic_velocity, ) = _eigenvalue_building_blocks( conserved_state, gamma, rhomin, pgmin, registered_variables, ) def mode_0(): return velocity_x - fast_magnetosonic_velocity def mode_1(): return velocity_x - alfven_velocity def mode_2(): return velocity_x - slow_magnetosonic_velocity def mode_3(): return velocity_x def mode_4(): return velocity_x + slow_magnetosonic_velocity def mode_5(): return velocity_x + alfven_velocity def mode_6(): return velocity_x + fast_magnetosonic_velocity return jax.lax.switch( mode, [mode_0, mode_1, mode_2, mode_3, mode_4, mode_5, mode_6] )