- directionDirection in which to send data
C++ Type:MooseEnum
Controllable:No
Description:Direction in which to send data
NekBoundaryFlux
Description
NekBoundaryFlux is a FieldTransfer that sends a boundary flux between NekRS and MOOSE. First, this object creates an AuxVariable using the name of the object; this variable will hold the flux which NekRS reads (for direction = to_nek) or will be written by NekRS (for direction = from_nek).
Flux Normalization
The internal NekRS mesh is based on the spectral element method (with Guass-Lobatto quadrature), whereas a coupled MOOSE applcation which wants to write/use a boundary flux will likely be using a different quadrature rule. In order to ensure perfect conservation of the flux as it is exchanged between NekRS and MOOSE, a Receiver postprocessor is automatically added with name taken as <name of the object>_integral. For direction = to_nek, this postprocessor should be populated by a parent/sub application with the total integrated flux in the originating MOOSE application. For direction = from_nek, this postprocessor will be filled by the total integrated flux computed internally in NekRS.
Flux Normalization
By default, NekBoundaryFlux will lump all "receiving" sidesets in NekRS together for the purpose of flux normalization. For example, suppose we are coupling NekRS to MOOSE heat conduction through two boundaries. Suppose MOOSE heat conduction predicts the following heat flux integrals on each of these boundaries:
MOOSE boundary 1: 100.0 W
MOOSE boundary 2: 200.0 W
When this data gets mapped to NekRS's spectral element mesh, we need to renormalize to ensure that we conserve power. This renormalization is necessary because the NekRS mesh can be entirely different from the MOOSE mesh, and integrating a nodal field on the origin mesh from MOOSE (say, a 1-st order Lagrange interpolation) will give a different integral value than integrating the interpolated nodal values on the receiving NekRS mesh (say, a 7-th order Lagrange interpolation of GLL points). By default, NekBoundaryFlux lumps all receiving sidesets in the NekRS model together for the sake of normalization. For example, suppose that once received on the NekRS mesh and integrated, that the received flux has values of:
NekRS boundary 1: 102.0 W
NekRS boundary 2: 199.0 W
By default, this class will renormalize the NekRS flux in order to match the total MOOSE flux (of 300.0 W) to give:
NekRS boundary 1:
NekRS boundary 2:
The total power entering the flux is preserved, but not the distribution among the sidesets. This is usually a very small error and is an acceptable approximation for many analyses. However, we do also support an option where the heat flux on each sideset is preserved with the conserve_flux_by_sideset option. When setting this parameter to true, the heat flux on each NekRS sideset will instead be evaluated as:
NekRS boundary 1:
NekRS boundary 2:
This option requires using a vector postprocessor (we typically recommend the VectorOfPostprocessors object) to send indiviudal boundary flux values into your NekRS-wrapped case. This more advanced option cannot be used if:
Your sidesets in the coupled MOOSE App are not individually specified. For instance, if your heat conduction solver has one sideset that maps to two NekRS sidesets, there's no natural way to figure out what the heat flux is in those sub-sidesets to send a correct value to NekRS.
Any nodes are shared among the NekRS sidesets, such as a node on a corner between two sidesets. When we renormalize the NekRS flux, nodes that are present on more than one sideset will get renormalized multiple times, so there is no guarantee that we can enforce the total flux.
What are the Fluxes?
There are a few different heat fluxes involved when coupling NekRS via Conjugate Heat Transfer (CHT) to MOOSE. When you run a CHT calculation, for each time step you will see something like the following printed to the screen:
Sending heat flux to NekRS boundary 1
Normalizing total NekRS flux of 78.8581 to the conserved MOOSE value of 78.6508
Here, the "total NekRS flux of ..." is the integral of nrs->usrwrk over the high-order NekRS spectral element mesh. Conversely, the "conserved MOOSE value of ..." is the total heat flux that MOOSE is setting in NekRS (read from the <name_of_object>_integral postprocessor that this object creates. These two numbers will be different unless all of the following are true:
The NekRS and MOOSE meshes are identical
The NekRS polynomial order matches the MOOSE polynomial order, and for the same polynomial order the nodes are the same. Because NekRS is a spectral element method and MOOSE uses the finite element method, this criteria can only occur if NekRS and MOOSE are either 1st or 2nd order (because the node placement for 1st or 2nd order elements is the same for spectral and finite elements).
The quadrature rule used to integrate in MOOSE is the GLL quadrature
Any differences between the "total NekRS flux" and the "conserved MOOSE value" will simply arise due to differences in the integration of a field over their respective meshes, and is not anything of concern - in fact, Cardinal is specifically designed to renormalized so that conservation is maintained. However, you may see large differences if the geometry is curved, since the NekRS high-order mesh will capture the curvature and result in very different area integrals. We always recommend doing a sanity check on the flux sent from MOOSE to NekRS via the MOOSE output files.
You can also monitor the heat flux in NekRS by computing using a NekHeatFluxIntegral postprocessor. In general, the quantity computed by this postprocessor will not match the heat flux set by MOOSE because both the finite and spectral element methods solve weak forms where Neumann boundary conditions are only weakly imposed. That is, we set the heat flux but only enforce it by driving the entire nonlinear residual to a small-enough number, so heat flux boundary conditions are never perfectly observed like Dirichlet boundary conditions are.
Example Input File Syntax
As an example, the example below couples NekRS to MOOSE via CHT. A coupled MOOSE application sends a heat flux variable and a postprocessor representing the total flux integral into a NekRS-wrapped case. The NekBoundaryFlux object creates automatically an auxiliary variable named avg_flux and a postprocessor by the name of flux_integral (the default postprocessor name would have been avg_flux_integral but you can also specify a custom name if desired as shown here). The NekBoundaryFlux object then handles writing this heat flux into usrwrk slot 0.
[Problem<<<{"href": "../../syntax/Problem/index.html"}>>>]
type = NekRSProblem
casename<<<{"description": "Case name for the NekRS input files; this is <case> in <case>.par, <case>.udf, <case>.oudf, and <case>.re2."}>>> = 'onepebble2'
n_usrwrk_slots<<<{"description": "Number of slots to allocate in nrs->usrwrk to hold fields either related to coupling (which will be populated by Cardinal), or other custom usages, such as a distance-to-wall calculation"}>>> = 1
[FieldTransfers<<<{"href": "../../syntax/Problem/FieldTransfers/index.html"}>>>]
[avg_flux]
type = NekBoundaryFlux<<<{"description": "Reads/writes boundary flux data between NekRS and MOOSE."}>>>
direction<<<{"description": "Direction in which to send data"}>>> = to_nek
postprocessor_to_conserve<<<{"description": "Name of the postprocessor/vectorpostprocessor containing the integral(s) used to ensure conservation; defaults to the name of the object plus '_integral'"}>>> = flux_integral
usrwrk_slot<<<{"description": "When 'direction = to_nek', the slot(s) in the usrwrk array to write the incoming data; provide one entry for each quantity being passed"}>>> = 0
[]
[temp]
type = NekFieldVariable<<<{"description": "Reads/writes volumetric field data between NekRS and MOOSE."}>>>
field<<<{"description": "NekRS field variable to read/write; defaults to the name of the object"}>>> = temperature
direction<<<{"description": "Direction in which to send data"}>>> = from_nek
[]
[]
[](test/tests/cht/pebble/nek.i)Then, all that is required to actually _apply_ this heat flux to the NekRS model is to use it in the scalarNeumannConditions boundary condition. Below, bc->usrwrk is the same as nrs->o_usrwrk, or the scratch space on the device; this function applies the heat flux computed by MOOSE to the flux boundaries.
void scalarNeumannConditions(bcData *bc)
{
// note: when running with Cardinal, Cardinal will allocate the usrwrk
// array. If running with NekRS standalone (e.g. nrsmpi), you need to
// replace the usrwrk with some other value or allocate it youself from
// the udf and populate it with values.
bc->flux = bc->usrwrk[bc->idM];
}
(test/tests/cht/pebble/onepebble2.oudf)Input Parameters
- conserve_flux_by_sidesetFalseWhether to conserve the flux by individual sideset (as opposed to lumping all sidesets together). Setting this option to true requires syntax changes in the input file to use vector postprocessors, and places restrictions on how the sidesets are set up.
Default:False
C++ Type:bool
Controllable:No
Description:Whether to conserve the flux by individual sideset (as opposed to lumping all sidesets together). Setting this option to true requires syntax changes in the input file to use vector postprocessors, and places restrictions on how the sidesets are set up.
- initial_flux_integral0Initial value to use for the 'postprocessor_to_conserve'; this initial value will be overridden once the coupled app executes its transfer of the boundary flux term integral into the 'postprocessor_to_conserve'. You may want to use this parameter if NekRS runs first, or if you are running NekRS in isolation but still want to apply a boundary flux term via Cardinal. Remember that this parameter is only used to normalize the flux, so you will need to populate an initial shape if you want to see this parameter take effect.
Default:0
C++ Type:double
Unit:(no unit assumed)
Controllable:No
Description:Initial value to use for the 'postprocessor_to_conserve'; this initial value will be overridden once the coupled app executes its transfer of the boundary flux term integral into the 'postprocessor_to_conserve'. You may want to use this parameter if NekRS runs first, or if you are running NekRS in isolation but still want to apply a boundary flux term via Cardinal. Remember that this parameter is only used to normalize the flux, so you will need to populate an initial shape if you want to see this parameter take effect.
- normalization_abs_tol1e-08Absolute tolerance for checking if conservation is maintained during transfer
Default:1e-08
C++ Type:double
Unit:(no unit assumed)
Range:normalization_abs_tol > 0
Controllable:No
Description:Absolute tolerance for checking if conservation is maintained during transfer
- normalization_rel_tol1e-05Relative tolerance for checking if conservation is maintained during transfer
Default:1e-05
C++ Type:double
Unit:(no unit assumed)
Range:normalization_rel_tol > 0
Controllable:No
Description:Relative tolerance for checking if conservation is maintained during transfer
- postprocessor_to_conserveName of the postprocessor/vectorpostprocessor containing the integral(s) used to ensure conservation; defaults to the name of the object plus '_integral'
C++ Type:std::string
Controllable:No
Description:Name of the postprocessor/vectorpostprocessor containing the integral(s) used to ensure conservation; defaults to the name of the object plus '_integral'
- usrwrk_slotWhen 'direction = to_nek', the slot(s) in the usrwrk array to write the incoming data; provide one entry for each quantity being passed
C++ Type:std::vector<unsigned int>
Controllable:No
Description:When 'direction = to_nek', the slot(s) in the usrwrk array to write the incoming data; provide one entry for each quantity being passed
Optional Parameters
- control_tagsAdds user-defined labels for accessing object parameters via control logic.
C++ Type:std::vector<std::string>
Controllable:No
Description:Adds user-defined labels for accessing object parameters via control logic.
- enableTrueSet the enabled status of the MooseObject.
Default:True
C++ Type:bool
Controllable:No
Description:Set the enabled status of the MooseObject.
Advanced Parameters
Input Files
- (tutorials/fhr_reflector/cht/nek.i)
- (tutorials/pincell_multiphysics/nek.i)
- (test/tests/conduction/reverse_cht/nek.i)
- (test/tests/cht/nondimensional/nek.i)
- (test/tests/conduction/zero_flux/mismatch_nek.i)
- (test/tests/nek_errors/usrwrk_transfers/scalar.i)
- (tutorials/pebble_67/nek.i)
- (test/tests/nek_errors/no_temp_var/nek.i)
- (test/tests/conduction/zero_flux/nek.i)
- (test/tests/cht/pebble/shift/nek.i)
- (test/tests/cht/nondimensional/nek_exact.i)
- (tutorials/sfr_7pin/nek.i)
- (tutorials/nek_stochastic/nek.i)
- (test/tests/multiple_nek_apps/two_channels/nek.i)
- (test/tests/conduction/boundary_and_volume/prism/nek.i)
- (test/tests/transfers/nek_flux/nek.i)
- (test/tests/nek_errors/usrwrk_transfers/wrong_problem.i)
- (test/tests/conduction/nonidentical_interface/cylinders/nek.i)
- (test/tests/auxkernels/heat_transfer_coefficient/nek.i)
- (test/tests/cht/sfr_pincell/nek_vpp.i)
- (test/tests/cht/pincell_p_v/nek.i)
- (tutorials/sfr_7pin/nek_vpp.i)
- (test/tests/conduction/zero_flux/nek_vpp.i)
- (tutorials/pebble_cht/nek.i)
- (tutorials/sfr_7pin/nek_fluxflux.i)
- (test/tests/conduction/zero_flux/nek_disjoint.i)
- (test/tests/nek_errors/usrwrk_transfers/nek.i)
- (tutorials/gas_compact_cht/nek.i)
- (test/tests/cht/sfr_pincell/nek.i)
- (test/tests/nek_errors/no_temp_solve/nek.i)
- (test/tests/nek_errors/usrwrk_transfers/flux.i)
- (tutorials/fhr_reflector/conduction/nek.i)
- (tutorials/gas_compact_multiphysics/nek.i)
- (test/tests/conduction/identical_interface/cube/nek.i)
- (test/tests/cht/pebble/nek.i)
- (test/tests/conduction/identical_interface/pyramid/nek.i)
- (test/tests/conduction/nonidentical_interface/cylinders/nek_mini.i)
- (test/tests/cht/sfr_pincell/nek_isolated.i)
- (test/tests/nek_errors/invalid_settings/nek_bc.i)
- (test/tests/postprocessors/nek_point_value/usrwrk_units.i)
- (test/tests/transfers/nek_source/flux.i)
- (test/tests/conduction/nonidentical_interface/cylinders/nek_exact.i)
- (test/tests/conduction/boundary_and_volume/prism/nek_exact.i)