11. Discrete-Ordinates Problems
The discrete-ordinates problem object is the center of OpenSn inputs. It gathers the mesh, materials, groupsets, sources, boundary conditions, problem options, and transport state into one object that can then be driven by a solver.
The two main problem classes exposed in Python are:
11.1. Overview
Use pyopensn.solver.DiscreteOrdinatesProblem for Cartesian
discrete-ordinates problems. This is the standard problem class for most
steady-state, transient, and k-eigenvalue workflows.
The usual Cartesian construction pattern is:
phys = DiscreteOrdinatesProblem(
mesh=mesh,
num_groups=num_groups,
groupsets=groupsets,
xs_map=xs_map,
boundary_conditions=boundary_conditions,
point_sources=point_sources,
volumetric_sources=volumetric_sources,
options={...},
sweep_type="AAH",
use_gpus=False,
time_dependent=False,
)
The problem object owns:
the mesh and material mapping,
the scalar and angular transport state,
the groupset definitions,
the current forward or adjoint mode,
the current steady-state or time-dependent mode,
the field-function interface associated with the problem.
In practice, that means the problem is where the transport model is assembled:
the spatial domain and geometry ids come from the mesh,
the material assignment comes from
xs_map,the energy groupings and inner iteration setup come from
groupsets,driving terms come from sources and boundary conditions,
global transport behavior comes from problem-level options.
Note
A solver does not define the transport model. The problem does. The solver only decides how that already-defined model is advanced or converged.
11.2. Constructor Summary
The main constructor inputs are:
mesh: apyopensn.mesh.MeshContinuumnum_groups: total number of energy groupsgroupsets: one or more groupset dictionariesxs_map: block-id to cross-section mappingboundary_conditions: optional boundary specificationspoint_sources: optional point sourcesvolumetric_sources: optional volumetric sourcesoptions: problem-level settingssweep_type:"AAH"or"CBC"use_gpus: request GPU sweep support where availabletime_dependent: whether the problem starts in time-dependent mode
A complete problem definition often looks like:
phys = DiscreteOrdinatesProblem(
mesh=mesh,
num_groups=2,
groupsets=groupsets,
xs_map=[
{"block_ids": [0], "xs": fuel_xs},
{"block_ids": [1], "xs": moderator_xs},
],
boundary_conditions=boundary_conditions,
volumetric_sources=volumetric_sources,
options={
"verbose_inner_iterations": False,
"verbose_outer_iterations": True,
},
sweep_type="AAH",
time_dependent=False,
)
Each of these pieces is discussed elsewhere in the guide, but it is useful to see them in one place.
11.3. Constructor Inputs
11.3.1. mesh
This is the transport mesh.
It must already exist and already carry the block ids and boundary labels that the rest of the problem will use.
11.3.2. num_groups
This is the total number of energy groups in the problem.
It must be consistent with:
the loaded cross sections,
the groupset definitions,
any group-wise source data.
11.3.3. groupsets
This is the list of groupset dictionaries that define angular quadratures, inner iteration, angle aggregation, and optional DSA configuration.
Groupsets are detailed separately in Groupsets, but from the problem perspective they define how the energy range is partitioned for transport iterations.
11.3.4. xs_map
This maps mesh block ids to cross-section objects.
Every block id used in the transport region should be assigned an XS object.
11.3.5. boundary_conditions
This is the list of transport boundary-condition dictionaries. The names used here must match the boundary ids on the mesh.
11.3.6. point_sources and volumetric_sources
These are optional source objects added at construction time.
11.3.7. options
This dictionary holds problem-level behavior that is not naturally part of the mesh, source, or groupset definitions.
11.3.8. sweep_type
This selects the sweep type:
"AAH": the aggregated sweeper with cycle breaking"CBC": the cell-by-cell sweeper
11.3.9. use_gpus
This requests GPU acceleration where supported.
11.3.10. time_dependent
This puts the problem into time-dependent mode at construction time.
11.5. Problem Options
The Python API exposes problem options through the constructor and
SetOptions().
The most important options for everyday use are:
adjointsave_angular_fluxverbose_inner_iterationsverbose_outer_iterationsmax_ags_iterationsags_toleranceags_convergence_checkverbose_ags_iterationsfield_function_prefix_optionfield_function_prefix
There are also restart, precursor, and message-size options for more specialized workflows.
Note
Problem options are where users should look for global transport behavior. If a setting changes how the entire problem behaves rather than how one groupset behaves, it usually belongs here.
11.5.1. Setting options after construction
Problem options can also be changed after construction:
phys.SetOptions(
verbose_inner_iterations=False,
max_ags_iterations=200,
ags_tolerance=1.0e-8,
)
This is useful for parameter studies or staged workflows where the problem definition is reused.
11.6. sweep_type
The Python API exposes:
"AAH""CBC"
Example:
sweep_type="AAH"
If omitted, the default is "AAH".
Operationally, the two sweep types are different:
"AAH"is the more general aggregated sweeper and should be treated as the default production choice."CBC"is a cell-by-cell sweeper that preserves exact cell-to-cell dependencies.
The practical differences are:
AAHhas explicit delayed-angular-flux machinery for cycle handling.AAHcan break both local and inter-partition sweep cycles by removing feedback-arc-set edges and lagging the corresponding angular-flux dependencies.CBCdoes not support local sweep cycles.time-dependent discrete-ordinates problems are restricted to
AAH.
Note
In the AAH implementation, the lagged data is tied specifically to
cycle-breaking dependencies. It is not a general statement that all angular
fluxes are always lagged.
Practical recommendation:
keep
sweep_type="AAH"unless there is a specific reason to chooseCBCand the sweep graph is known to be compatible with it.
11.7. Problem Modes
11.7.1. time_dependent
If time_dependent=True, the problem starts in time-dependent mode.
Important requirement:
time-dependent operation requires
options={"save_angular_flux": True}
Example:
phys = DiscreteOrdinatesProblem(
...,
time_dependent=True,
options={"save_angular_flux": True},
)
This requirement exists because transient updates need access to angular-flux state from one timestep to the next.
This option changes the problem mode, not just a solver setting. That is why it belongs on the problem object and must be consistent with the solver used later.
11.7.2. use_gpus
use_gpus requests GPU acceleration for supported sweep paths.
Current restrictions:
only
"AAH"is supported for GPU use,curvilinear problems do not support GPU acceleration,
time-dependent problems do not support GPU acceleration,
adjoint problems do not support GPU acceleration.
Most users should treat this as a deployment choice after the base problem is already running correctly on the CPU.
11.7.3. Adjoint Mode
Adjoint mode is controlled at the problem level because it changes the meaning of the transport problem itself, not just the iterative algorithm.
It can be set at construction time through options or later with
SetAdjoint(True).
Because this is a fundamental change in problem interpretation, users should be deliberate about when they switch it on.
11.8. Field-Function Interface
The problem object is also where users access transport outputs.
The main field-function accessors are:
GetScalarFluxFieldFunction()CreateFieldFunction()GetAngularFieldFunctionList()
Example:
scalar_ffs = phys.GetScalarFluxFieldFunction()
power_ff = phys.CreateFieldFunction("power_generation", "power")
Note
In LBS workflows, users normally work with the field-function objects
returned by these accessors directly. They are created from the current
transport state when requested. If the transport state changes later, call
Update() on an existing updateable field function or create a fresh field
function from the current state.
11.9. Angular-Flux Access
The discrete-ordinates problem also exposes angular-flux data directly through
GetPsi().
GetPsi() returns a list of NumPy arrays that view the underlying angular-
flux storage.
This is a specialized interface and is mainly useful when:
a workflow needs direct access to angular-flux data,
a custom analysis step is easier to write in Python,
the problem was configured with
save_angular_flux=Truefor the needed workflow.
Note
Angular flux is much larger than scalar flux. Many workflows never need it. Only enable and use it when the calculation actually requires it.
11.10. Balance and Leakage
The Cartesian discrete-ordinates problem exposes two important diagnostics:
ComputeBalance()ComputeLeakage()
11.10.1. ComputeBalance()
This computes the particle balance for the problem and returns a dictionary of balance terms.
It is useful for:
checking whether the run is physically consistent,
confirming source, absorption, leakage, and production trends,
validating test and regression problems.
11.10.2. ComputeLeakage()
This computes boundary leakage and returns a dictionary mapping boundary names to group-wise NumPy arrays.
Example:
leakage = phys.ComputeLeakage(["xmin", "xmax"])
Important requirement:
leakage computation requires
save_angular_flux=True
This requirement exists because leakage is derived from the outgoing angular flux.
11.11. Writing and Reading Transport State
The problem object also exposes file-based state helpers such as:
WriteFluxMoments()ReadFluxMoments()CreateAndWriteSourceMoments()ReadSourceMoments()ReadFluxMomentsAndMakeSourceMoments()WriteAngularFluxes()ReadAngularFluxes()
These are useful for:
restart-like workflows,
response studies,
source-driven workflows that reuse previously computed fields.
11.12. Updating the Problem In Place
Several parts of the problem can be updated after construction.
The most important methods are:
SetOptions()SetPointSources()SetVolumetricSources()SetBoundaryOptions()SetAdjoint()SetTimeDependentMode()
11.12.1. Updating a Problem After Construction
One of the strengths of the problem API is that major parts of the model can be replaced without rebuilding the entire problem object.
Examples:
phys.SetVolumetricSources(
clear_volumetric_sources=True,
volumetric_sources=[new_source],
)
phys.SetBoundaryOptions(
clear_boundary_conditions=True,
boundary_conditions=[new_boundary],
)
phys.SetAdjoint(True)
This makes the problem object usable in:
source studies,
boundary-condition studies,
forward/adjoint comparisons,
transient driver loops that change forcing terms.
11.13. DiscreteOrdinatesCurvilinearProblem
pyopensn.solver.DiscreteOrdinatesCurvilinearProblem is the
curvilinear companion to the Cartesian problem class.
It uses the same general construction pattern, but currently requires:
a suitable curvilinear mesh,
coord_system=2for cylindrical coordinates,a compatible quadrature and solver setup.
Important current limitations:
the curvilinear solver only supports cylindrical geometries,
GPU acceleration is not supported,
users should treat it as a more specialized path than the standard Cartesian problem.
Example:
phys = DiscreteOrdinatesCurvilinearProblem(
mesh=mesh,
coord_system=2,
num_groups=num_groups,
groupsets=groupsets,
xs_map=xs_map,
sweep_type="AAH",
)
11.14. Typical Construction Patterns
A few patterns show up repeatedly:
source-driven steady-state problem plus
SteadyStateSourceSolvertime-dependent problem plus
TransientSolvermultiplication problem plus
PowerIterationKEigenSolvermultiplication problem plus
NonLinearKEigenSolver
11.15. Practical Guidance
As a rule:
use
DiscreteOrdinatesProblemunless the curvilinear class is specifically needed,keep
sweep_type="AAH"unless there is a clear reason to chooseCBC,treat
save_angular_fluxas a capability switch with a memory cost,build the simplest correct problem first, then add extra options,
remember that the problem object remains central even after the solver is constructed because it owns the transport state and output interface.
Note
Many solver issues are actually problem-definition issues. If something looks
unstable or physically wrong, it is often worth reviewing the problem object
first: mesh labels, xs_map, groupsets, source definitions, and problem
options.