The question this method answers¶
Given a self-gravitating sphere in hydrostatic equilibrium with a prescribed equation of state, what dimensionless density and enclosed-mass profile does that equilibrium imply, and where is its edge? Jaxstro solves the Lane-Emden equation in two branches: the polytropic branch (a finite polytrope of index , whose surface is the first zero of the solution) and the isothermal Bonnor-Ebert branch (no intrinsic edge -- the truncation radius is a genuine physical input set by the confining external pressure). Both branches are differentiable, which is what lets them feed gradient-based inference and hydrostatic initial conditions downstream.
Before computation: what should be true?¶
The model must be a spherically symmetric, self-gravitating equilibrium whose
pressure-density relation is either a polytrope (with
) or isothermal . Choose xi_max and n_points
knowing they are static: they size the output grid, not the physics. For a
polytrope, xi_max should not exceed the surface if you intend to
differentiate (see below). These ordinary-differential solves are singular at the
origin, so the integration starts just off it at XI_0 = 1e-6.
Scientific state representations and fixed-shape PyTrees are connected in PyTrees as scientific state, and the model-to-program framing is in From mathematical relations to differentiable programs.
Define the mathematical objects¶
Hydrostatic equilibrium plus Poisson’s equation for a self-gravitating sphere, nondimensionalized with a density scale (the central density) and a length scale, collapses to a single second-order ordinary differential equation in the dimensionless radius . The dependent variable is (isothermal, with ) or (polytropic, with ). The dimensionless enclosed mass is the radial integral of in scaled variables. The polytropic index is the one traced, differentiable parameter; , the first zero of , is the polytrope’s surface and exists (is finite) only for .
Derive the method¶
The isothermal (Bonnor-Ebert) branch writes the scaled Poisson equation with an exponential source and boundary conditions at the center:
The polytropic branch replaces the exponential source with the power law and normalizes the center to unity:
Integrating in scaled variables, and using each equation to eliminate the second derivative, gives the dimensionless enclosed mass in closed form on each branch, with a common cubic behavior near the origin:
Both right-hand sides carry a term that is singular at , so the
solve cannot start from the bare boundary condition. Substituting a Taylor
expansion into each equation fixes the low-order coefficients and seeds the state
at XI_0:
The polytrope surface is the first root of . It is located as a
differentiable event root -- the implicit function theorem applied to the solver’s
diffrax.Event -- rather than as a grid argmin, which would have no usable
gradient in .
What the algorithm actually does¶
solve_isothermal and solve_polytrope build the corresponding first-order
diffrax.ODETerm, seed the state at XI_0 from the series above, and integrate
with adaptive Tsit5 under a PIDController(rtol=1e-8, atol=1e-10), saving on a
linspace(XI_0, xi_max, n_points). Each returns a LaneEmdenSolution with fields
xi, y ( or ), dy, m, and dm, all length n_points. The
enclosed-mass derivative dm is supplied analytically ( or
), not by differencing. polytrope_xi1 runs the same term with a
diffrax.Event whose condition is , closed by an optimistix.Newton
root finder, and returns the scalar edge. Past the first zero the polytropic
source is floored at zero (a non-integer power of a negative number is
NaN); that floored continuation is not physical.
What JAX differentiates¶
jit, vmap, and grad are supported. The gradient in the polytropic index
flows through the adaptive solve and through the polytrope_xi1 event root via the
implicit function theorem, so is well defined. On the
isothermal branch the truncation radius xi_max is a real physical input, and the
edge mass is differentiable in it. xi_max and n_points are static and must not
be differentiated.
Using it in Jaxstro¶
import jax
jax.config.update("jax_enable_x64", True)
import jax.numpy as jnp
from jaxstro.numerics.lane_emden import polytrope_xi1, solve_polytrope
# n = 1 has the closed-form solution theta = sin(xi)/xi with surface xi_1 = pi.
xi1 = polytrope_xi1(1.0)
assert abs(float(xi1) - jnp.pi) < 1e-4
solution = solve_polytrope(1.0, xi_max=float(jnp.pi), n_points=200)
assert solution.xi.shape == (200,)
assert solution.m.shape == (200,)
# The event root is differentiable in the polytropic index.
grad_xi1 = jax.grad(lambda n: polytrope_xi1(n))(1.5)
assert jnp.isfinite(grad_xi1)n is a traced scalar and may be differentiated; xi_max and n_points size the
output grid and are static. Enable the intended precision before creating arrays,
since the adaptive tolerances are tight.
How to audit the result¶
Compare against the three closed-form polytropes: gives with ; gives with ; gives , which never reaches zero (infinite extent). Check the origin behavior against the series and , and confirm the solver’s leading error shrinks at the expected rate under grid or tolerance refinement. Verify the enclosed mass approaches near the origin and is monotone where the density is positive. Cross-check from the event root against a central finite difference. The executable audit map is in Validation methods.
Where the claim stops¶
Jaxstro solves the dimensionless equilibrium; it does not choose a physically
adequate xi_max, restore dimensions, or model stability, rotation, magnetic
support, or time evolution. Agreement at the closed-form indices validates the
solver, not any particular astrophysical application of the resulting profile. The
regime and any xi_max beyond are outside the differentiable
contract and must be handled by the caller.