14. Post Processors
For most OpenSn workflows, post processing starts with field functions. A field function is a mesh-based representation of transport data that can be exported or interpolated from Python.
The important practical point is that field functions are created from the
current solver state when requested. They are snapshots, not continuously
updated solver-owned views. Some field functions can refresh that same
snapshot object explicitly through
pyopensn.fieldfunc.FieldFunctionGridBased.Update().
14.1. Overview
The OpenSn field function interfaces are:
pyopensn.solver.DiscreteOrdinatesProblem.GetAngularFieldFunctionList()pyopensn.fieldfunc.FieldFunctionGridBased.ExportMultipleToPVTU()
In practice, most workflows are:
solve the problem,
create the field functions needed from the current state,
export them or evaluate them with interpolation objects.
Note
If the solver state changes, a field function created earlier does not
update itself automatically after a later solve or timestep. Either create a
new field function from the current state, or call Update() on the
existing field function when CanUpdate() returns True.
Note
For LBS workflows, the important field-function creators are
GetScalarFluxFieldFunction(),
CreateFieldFunction(), and, for discrete ordinates problems,
GetAngularFieldFunctionList(). These methods return fresh field
functions from the current state; they do not rely on a persistent
field-function cache.
14.2. Updating Existing Field Functions
Field functions returned by LBS problem accessors are updateable snapshots. They hold their own field-vector data, but also know how to refresh that data from the owning problem while the owning problem is still alive.
Use pyopensn.fieldfunc.FieldFunctionGridBased.CanUpdate() before
refreshing a field function that may have outlived its problem:
scalar_ff = phys.GetScalarFluxFieldFunction()[0]
solver.Advance()
if scalar_ff.CanUpdate():
scalar_ff.Update()
Calling Update() refreshes the same field-function object. Interpolators
and export calls that already reference that object will see the refreshed data
the next time they execute.
This is mainly useful in timestep loops or repeated-solve workflows where reusing the same interpolation or export setup is clearer than reconstructing field functions each time.
14.3. Scalar Flux Field Functions
14.3.1. GetScalarFluxFieldFunction
Use pyopensn.solver.LBSProblem.GetScalarFluxFieldFunction() to create
scalar-flux or flux-moment field functions from the current scalar-flux state.
The common case is scalar flux only:
scalar_ffs = phys.GetScalarFluxFieldFunction()
This returns one field function per energy group, each representing the zeroth moment of the flux.
If higher moments are needed too:
ff_by_group_and_moment = phys.GetScalarFluxFieldFunction(
only_scalar_flux=False,
)
In that form:
result[g][m]is the field function for groupgand momentm
Note
Most visualization and response workflows begin with scalar flux. Higher moments are mainly for diagnostics and specialized analysis.
14.4. Derived Field Functions
14.4.1. CreateFieldFunction
Use pyopensn.solver.LBSProblem.CreateFieldFunction() to create a named
scalar field function derived from a 1D cross section or from the special case
"power".
Examples:
fission_rate_ff = phys.CreateFieldFunction("fission_rate", "sigma_f")
power_ff = phys.CreateFieldFunction("power_generation", "power")
For a built-in or custom 1D XS name, this creates the field:
sum_g xs[g] * phi_g
at each spatial point.
If a power-normalized view is needed:
power_ff = phys.CreateFieldFunction(
"power_generation_norm",
"power",
power_normalization_target=1.0,
)
Important points:
nameis the field-function name assigned to the returned objectxs_namecan be a built-in 1D XS name, a custom XS name, or"power"power_normalization_targetis optional and affects only the returned field function
Note
power_normalization_target applies a power-based scaling to the returned
field function only. It does not rescale the solver’s internal flux vectors,
and it does not change field functions created earlier. If you want a
normalized power field, use
CreateFieldFunction("name", "power", power_normalization_target=...).
The same argument can also be used for other derived fields such as
sigma_f * phi when a power-normalized post-processed view is desired.
14.5. Angular Flux Field Functions
14.5.1. GetAngularFieldFunctionList
Use
pyopensn.solver.DiscreteOrdinatesProblem.GetAngularFieldFunctionList()
to create field functions for selected angular-flux components.
Example:
ang_ffs = phys.GetAngularFieldFunctionList(groups=[0], angles=[0])
Important requirements:
this is available on
pyopensn.solver.DiscreteOrdinatesProblemangular-flux storage must be enabled with
save_angular_flux=Truethe returned field functions are snapshots created from the currently stored angular flux
if the stored angular flux changes later, call
Update()on the existing angular field functions or create new ones from the current state
Important
For transient problems, save_angular_flux=True is not optional. It is
required by the transient solver itself, and it is also required if you
want to create angular-flux field functions or query GetPsi() during the
transient run.
This is mainly useful for:
angular diagnostics,
leakage studies,
checking directional structure in difficult problems.
Note
Angular flux is much larger than scalar flux. Only enable angular-flux storage and create angular field functions when the workflow actually needs them.
Scalar-flux field functions use names based on group and moment, for example:
phi_g000_m00phi_g001_m00phi_g000_m01
If the problem uses a field-function prefix, that prefix is applied to these names as well.
14.6. Exporting Field Functions
The main Python export routine is:
Export scalar flux:
from pyopensn.fieldfunc import FieldFunctionGridBased
scalar_ffs = phys.GetScalarFluxFieldFunction()
FieldFunctionGridBased.ExportMultipleToPVTU(
scalar_ffs,
"scalar_flux",
)
Export a derived field:
from pyopensn.fieldfunc import FieldFunctionGridBased
power_ff = phys.CreateFieldFunction("power_generation", "power")
FieldFunctionGridBased.ExportMultipleToPVTU(
[power_ff],
"power",
)
Export several fields together:
from pyopensn.fieldfunc import FieldFunctionGridBased
scalar_ffs = phys.GetScalarFluxFieldFunction()
power_ff = phys.CreateFieldFunction("power_generation", "power")
FieldFunctionGridBased.ExportMultipleToPVTU(
scalar_ffs + [power_ff],
"transport_outputs",
)
Note
Export after the solve, not before. For transient problems, export after the completed timestep whose state you want to visualize.
14.7. Field-Function Interpolation
14.7.1. Point Interpolation
Use pyopensn.fieldfunc.FieldFunctionInterpolationPoint to evaluate a
field function at a point.
Example:
from pyopensn.fieldfunc import FieldFunctionInterpolationPoint
ffi = FieldFunctionInterpolationPoint()
ffi.AddFieldFunction(phys.GetScalarFluxFieldFunction()[0])
ffi.Initialize()
ffi.Execute()
value = ffi.GetPointValue()
This is useful for detector-like spot checks or comparison against an analytic value.
14.7.2. Line Interpolation
Use pyopensn.fieldfunc.FieldFunctionInterpolationLine to sample a
field function along a line.
Example:
from pyopensn.fieldfunc import FieldFunctionInterpolationLine
from pyopensn.math import Vector3
ffi = FieldFunctionInterpolationLine()
ffi.AddFieldFunction(phys.GetScalarFluxFieldFunction()[0])
ffi.SetInitialPoint(Vector3(0.0, 0.0, 0.0))
ffi.SetFinalPoint(Vector3(10.0, 0.0, 0.0))
ffi.SetNumberOfPoints(101)
ffi.Initialize()
ffi.Execute()
ffi.ExportToCSV("centerline")
This is useful for profiles, attenuation curves, and one-dimensional comparisons across runs.
14.7.3. Volume Interpolation
Use pyopensn.fieldfunc.FieldFunctionInterpolationVolume for region
averages, sums, maxima, and function-weighted variants over a logical volume.
Example:
from pyopensn.fieldfunc import FieldFunctionInterpolationVolume
ffi = FieldFunctionInterpolationVolume()
ffi.AddFieldFunction(phys.GetScalarFluxFieldFunction()[0])
ffi.SetLogicalVolume(my_lv)
ffi.SetOperationType("avg")
ffi.Initialize()
ffi.Execute()
avg_value = ffi.GetValue()
Available operation types are:
"sum""avg""max""sum_func""avg_func""max_func"
The *_func variants use a scalar material function supplied with
SetOperationFunction.
14.8. Other Useful Post-Processing Paths
For discrete ordinates problems, other useful output paths include:
GetPsi()for direct access to stored angular-flux arraysComputeLeakage()for boundary leakageWriteAngularFluxes()andReadAngularFluxes()for angular flux storage and restart-style workflows
These are not field-function exports, but they are often part of the same post-processing workflow.
14.9. Transient Workflows
For transient problems, the usual pattern is:
advance the timestep,
update or create the desired field functions,
export or interpolate them,
repeat as needed.
If the transient workflow needs angular-flux output as well, the problem must
have been created with options={"save_angular_flux": True} from the start.
For example:
scalar_ff = phys.GetScalarFluxFieldFunction()[0]
while not solver.Finished():
solver.Advance()
scalar_ff.Update()
...
This keeps one field-function object and refreshes it from each completed
timestep. Creating a new field function after each Advance() is also valid,
but is usually unnecessary when the existing object supports Update().
14.10. Practical Guidance
For most workflows:
use
GetScalarFluxFieldFunction()for scalar flux,use
CreateFieldFunction()for power or XS-weighted derived outputs,use
GetAngularFieldFunctionList()only when angular information is actually needed,use
ExportMultipleToPVTU()for visualization output,use point interpolation for spot checks,
use line interpolation for profiles,
use volume interpolation for averages and integrated responses.
call
Update()before reusing a field function after the solver state changes, or create a fresh field function from the current state.
If a script is becoming complicated, it is often worth separating the solve and the post-processing logic into different helper functions. That keeps the transport setup readable and makes the output workflow easier to reuse.