Jet in astronomix#

Imports#

# ==== GPU selection ====
from autocvd import autocvd
autocvd(num_gpus = 1)
# ruff: noqa: E402
# =======================

# general
import jax.numpy as jnp

# astronomix constants
from astronomix import (
    PERIODIC_BOUNDARY,
)

# astronomix containers
from astronomix import (
    SimulationConfig,
    SimulationParams,
    BoundarySettings,
    BoundarySettings1D,
    SnapshotSettings,
)

# astronomix functions
from astronomix import (
    get_registered_variables,
    construct_primitive_state,
    time_integration,
    finalize_config,
    setup_magnetic_fields_from_vector_potential,
)

# plotting
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from IPython.display import HTML

Setup simulation#

gamma = 5/3

box_size = 24.0
num_cells = 64
grid_spacing = box_size / num_cells
x_center = box_size / 2.0
y_center = box_size / 2.0
z_center = box_size / 2.0

config = SimulationConfig(
    grid_spacing = grid_spacing,
    mhd = True,
    progress_bar = True,
    dimensionality = 3,
    box_size = box_size,
    num_cells = num_cells,
    boundary_settings = BoundarySettings(
        BoundarySettings1D(
            left_boundary = PERIODIC_BOUNDARY,
            right_boundary = PERIODIC_BOUNDARY
        ),
        BoundarySettings1D(
            left_boundary = PERIODIC_BOUNDARY,
            right_boundary = PERIODIC_BOUNDARY
        ),
        BoundarySettings1D(
            left_boundary = PERIODIC_BOUNDARY,
            right_boundary = PERIODIC_BOUNDARY
        )
    ),
    return_snapshots = True,
    num_snapshots = 100,
    snapshot_settings = SnapshotSettings(
      return_states=True,
      return_final_state=True,
      return_magnetic_divergence=True
    ),
)

registered_variables = get_registered_variables(config)

C_CFL = 1.5

rho_0 = 1.0
p_0 = 1.0

# define the vector potential function
def jet_vector_potential(X, Y, Z):
    r = jnp.sqrt((X - x_center)**2 + (Y - y_center)**2 + (Z - z_center)**2)
    A0 = 20.0

    A_x = -jnp.exp(-r ** 2) * (Y - y_center)
    A_y = jnp.exp(-r ** 2) * (X - x_center)
    A_z = 0.5 * A0 * jnp.exp(-r ** 2)

    return A_x, A_y, A_z

# init b fields from vector potential
B_x, B_y, B_z, bxb, byb, bzb = setup_magnetic_fields_from_vector_potential(
    config=config,
    vector_potential_func=jet_vector_potential
)

# Set up primitive hydro arrays
rho = jnp.ones((config.num_cells, config.num_cells, config.num_cells)) * rho_0
u_x = jnp.zeros((config.num_cells, config.num_cells, config.num_cells))
u_y = jnp.zeros((config.num_cells, config.num_cells, config.num_cells))
u_z = jnp.zeros((config.num_cells, config.num_cells, config.num_cells))
p = jnp.ones((config.num_cells, config.num_cells, config.num_cells)) * p_0

# simulation params
params = SimulationParams(
    C_cfl = C_CFL,
    t_end = 5.0,
    gamma = gamma,
)

# construct primitive state
initial_state = construct_primitive_state(
    config = config,
    registered_variables=registered_variables,
    density = rho,
    velocity_x = u_x,
    velocity_y = u_y,
    velocity_z = u_z,
    gas_pressure = p,
    magnetic_field_x = B_x,
    magnetic_field_y = B_y,
    magnetic_field_z = B_z,
    interface_magnetic_field_x = bxb,
    interface_magnetic_field_y = byb,
    interface_magnetic_field_z = bzb,
)

config = finalize_config(config, initial_state.shape)
OPTIMAL_BACKEND: using the PALLAS backend (GPU compute capability >= 8.0).
For 3D simulations with periodic boundaries, setting boundary handling to PERIODIC_ROLL and num_ghost_cells to 0 for better performance.
Setting time integrator to RK4_SSP for finite difference solver mode.

Run the simulation#

result = time_integration(initial_state, config, params, registered_variables)
 |████████████████████████████████████████████████████████████████████| 100.0%  

Animate the result#

# what we want to plot
y_index = num_cells // 2
density_slices = result.states[:, registered_variables.density_index, :, y_index, :]

fig_anim, ax_anim = plt.subplots(1, 1, figsize=(6, 6))

# init image
current_slice = density_slices[0, :, :].T
img = ax_anim.imshow(current_slice, cmap="YlOrRd", origin='lower')
ax_anim.set_title("Density Slice Animation")
ax_anim.set_xlabel("X-axis")
ax_anim.set_ylabel("Z-axis")

# update image
def update(frame):
    current_slice = density_slices[frame, :, :].T
    img.set_array(current_slice)
    img.set_clim(current_slice.min(), current_slice.max())
    ax_anim.set_title(f"Density Slice Animation")
    return [img]

# create anim
ani = animation.FuncAnimation(
    fig_anim,
    update,
    frames=density_slices.shape[0],
    blit=True,
    interval=50 # mills between frames
)

fig_anim.tight_layout()
plt.close(fig_anim)

HTML(ani.to_jshtml())