Source code for jax_sn.variance_reduction.fw_cadis

import equinox as eqx
import jax.numpy as jnp
import lineax as lx

from ..operators.multi_group_operator import MultiGroupOperator, ComposedMultiGroupChainStrategy
from ..solvers.right_hand_side import RightHandSide, TotalSource, solve_multigroup_operator_rhs
from .continuous_adjoint import solve_continuous_adjoint


[docs] @eqx.filter_jit def fw_cadis_scalar_flux(operator: MultiGroupOperator, rhs: RightHandSide, solver: lx.AbstractLinearSolver) -> jnp.ndarray: r''' Runs the FW-CADIS algorithm to compute an importance map for variance reduction. First the forward problem is solved to obtain the scalar flux: .. math:: \phi = A^{-1} q where :math:`A` is the multi-group operator and :math:`q` is the right-hand side. Then, the importance map is computed as: .. math:: \phi^\dagger = (A^\dagger)^{-1} \left( \frac{1}{\phi} \right) where :math:`A^\dagger` is the continuous adjoint (see :func:`solve_continuous_adjoint`). For stability purposes, the scalar flux is first averaged over each element (due to the conservation property of DG, this cannot be zero, while the value at a particular basis function node can be extremely small and blow up). Parameters ---------- operator : MultiGroupOperator The multi-group operator for which to compute the importance map. rhs : RightHandSide The right-hand side of the equation to solve, typically representing a response function. solver : lx.AbstractLinearSolver The (iterative) linear solver used for both the forward and adjoint solves. Returns ------- jnp.ndarray The adjoint scalar flux importance map, shape ``[n_groups, n_moments, n_elements, n_basis]``. ''' assert isinstance(operator, MultiGroupOperator), f"operator must be an instance of MultiGroupOperator, but got {type(operator)}." chain_strategy = ComposedMultiGroupChainStrategy.from_composed(operator.strategy) forward_result = solve_multigroup_operator_rhs(operator, rhs, solver, chain_strategy) integration_coeff = operator.data.spatial_data.fem_matrices.integration_coeff forward_average_result = jnp.einsum("gei,i->ge", forward_result.value[:, 0, ...], integration_coeff) # scalar flux [n_groups, n_elements] one_over_phi = 1.0 / forward_average_result n_basis = operator.data.elements_basis_shape.shape[-1] qdagger_isotropic = jnp.stack([one_over_phi] * n_basis, axis=-1) # [n_groups, n_elements, n_basis] qdagger_rhs = TotalSource.from_operator_and_isotropic_source(operator, qdagger_isotropic) adjoint_result = solve_continuous_adjoint(operator, qdagger_rhs, solver) return adjoint_result.value