Source code for jax_sn.operators.operator_data

'''
Common data structures for all operators, containing differentiable and non-differentiable data.
'''

from abc import abstractmethod

import equinox as eqx
import jax.numpy as jnp

from jax_sn.domain.geometric_domain import GeometricDomain
from jax_sn.solution_domain import SolutionDomain, SpatialData
from jax_sn.quadrature_set import QuadratureSetBase
from jax_sn.operators.sweep_data import SweepData


[docs] class AngularData(eqx.Module): r'''Operator-defining angular data: the quadrature set and its precomputed harmonics.''' quadrature_set : QuadratureSetBase '''Angular quadrature set (angles + weights).''' wk_ylm : jnp.ndarray r'''Weighted harmonics :math:`w_k Y_{l,m}(\Omega_k)` -- the moment operator's matrix. [n_angles, n_moments]''' ylm : jnp.ndarray r'''Unweighted harmonics :math:`Y_{l,m}(\Omega_k)` -- the discrete operator's matrix. [n_angles, n_moments]'''
[docs] @classmethod def from_quadrature_set(cls, quadrature_set : QuadratureSetBase, l_max : int) -> "AngularData": '''Precompute the harmonics up to moment order ``l_max`` for ``quadrature_set``.''' return cls(quadrature_set, quadrature_set.wk_ylm_real(l_max), quadrature_set.ylm_real(l_max))
[docs] class DiscretisationData(eqx.Module): r''' Operator-defining differentiable data. ''' spatial : SpatialData '''Spatial + material data: geometry, FEM matrices, geometric factors, cross sections.''' angular : AngularData '''Angular data: quadrature + harmonics.'''
[docs] class NonDifferentiableData(eqx.Module): r''' Non-differentiable data (setup data) that is not part of the operator's definition and as such does not have to take part in the VJP/JVP of the operator. ''' sweep_data : SweepData '''The sweep execution plan (per-angle topological ordering).'''
[docs] class RequiresNonDifferentiableData(eqx.Module): ''' Mixin for strategies that require non-differentiable data (transport / single-group/ multi-group). '''
[docs] @abstractmethod def accepts(self, non_differentiable_data : NonDifferentiableData) -> bool: '''Whether it can accept this non-differentiable data.''' ...
[docs] def check_compatible(self, non_differentiable_data : NonDifferentiableData): '''The asserting twin of :meth:`accepts`, for data assembled by hand.''' assert self.accepts(non_differentiable_data), \ f"{type(self).__name__} cannot use the given {type(non_differentiable_data).__name__}. " \ f"Build the tier it needs with " \ f"{type(self).__name__}.build_default_non_differentiable_data(geometric_domain, angles)."
[docs] @abstractmethod def build_default_non_differentiable_data( self, geometric_domain : GeometricDomain, angles : jnp.ndarray) -> NonDifferentiableData: ''' Require each strategy to provide a way to construct this non-differentiable data from the geometric domain and angles. ''' ...
[docs] class OperatorData(eqx.Module): r''' Data for neutron transport operators, split into differentiable and non-differentiable data. ''' discretisation_data : DiscretisationData '''Operator-defining, differentiable data.''' non_differentiable_data : NonDifferentiableData '''Discrete, non-differentiable execution data''' @property def spatial_data(self) -> SpatialData: return self.discretisation_data.spatial @property def angular_data(self) -> AngularData: return self.discretisation_data.angular @property def sweep_data(self) -> SweepData: return self.non_differentiable_data.sweep_data @property def domain(self): return self.discretisation_data.spatial.domain @property def solution_domain(self) -> SolutionDomain: return SolutionDomain(self.discretisation_data.spatial) @property def elements_basis_shape(self): return self.discretisation_data.spatial.elements_basis_shape @property def n_moments(self): return self.angular_data.wk_ylm.shape[1] @property def n_angles(self): return self.angular_data.wk_ylm.shape[0] @property def n_basis(self): return self.elements_basis_shape.shape[-1] @property def n_energy_groups(self): return self.domain.cross_sections_indexed.n_energy_groups