- directionDirection in which to send data
C++ Type:MooseEnum
Controllable:No
Description:Direction in which to send data
NekVolumetricSource
Description
NekVolumetricSource
is a FieldTransfer that sends a volumetric source between NekRS and MOOSE. For instance, in the context of conservation of energy a volumetric source is the power density (units of energy/volume). First, this object creates an AuxVariable using the name of the object; this variable will hold the volumetric source which NekRS reads (for direction = to_nek
) or will be written by NekRS (for direction = from_nek
).
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 volumetric source 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 volumetric source in the originating MOOSE application. For direction = from_nek
, this postprocessor will be filled by the total integrated volumetric source computed internally in NekRS.
Example Input File Syntax
As an example, the example below couples NekRS to MOOSE via Conjugate Heat Transfer (CHT). A coupled MOOSE application sends a heat source variable and a postprocessor representing the total integral heat source (i.e. power) into a NekRS-wrapped case. The NekVolumetricSource
object creates automatically an auxiliary variable named heat_source
and a postprocessor by the name of source_integral
(the default postprocessor name would have been heat_source_integral
but you can also specify a custom name if desired as shown here). The NekVolumetricSource
object then handles writing this volumetric source 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."}>>> = 'cylinder'
[FieldTransfers<<<{"href": "../../syntax/Problem/FieldTransfers/index.html"}>>>]
[heat_source]
type = NekVolumetricSource<<<{"description": "Reads/writes volumetric source data between NekRS and MOOSE."}>>>
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
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'"}>>> = source_integral
[]
[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/conduction/nonidentical_volume/cylinder/nek.i)Then, all that is required to use a volumetric heat source transferred by MOOSE is to apply it with a custom source kernel in the .oudf
file. Below is an example of a custom heat source kernel, arbitrarily named mooseHeatSource
- this code is executed on the Graphics Processing Unit (GPU), and is written in OKL, a decorated C++ kernel language. This code loops over all the NekRS elements, loops over all the Gauss-Lobatto-Legendre (GLL) points on each element, and then sets the volumetric heat source equal to a heat source passed into the mooseHeatSource
function.
@kernel void mooseHeatSource(const dlong Nelements, const dlong offset, @restrict const dfloat * source, @restrict dfloat * QVOL)
{
for(dlong e=0;e<Nelements;++e;@outer(0)){
for(int n=0;n<p_Np;++n;@inner(0)){
const int id = e*p_Np + n;
QVOL[id] = source[offset + id];
}
}
}
(test/tests/conduction/nonidentical_volume/cylinder/cylinder.oudf)The actual passing of the nrs->usrwrk
scratch space (that NekRSProblem
writes into) into this custom kernel occurs in the .udf
file. In the .udf
file, you need to define a custom function, named artbirarily here to userq
, with a signature that matches the user-defined source function expected by NekRS. This function should then pass the scratch space into the Open Concurrent Compute Abstraction (OCCA) kernel we saw in the .oudf
file. Note that the "offset" here should match the slice in the scratch space array which holds the volumetric heat source.
void userq(nrs_t * nrs, double time, occa::memory o_S, occa::memory o_FS)
{
auto mesh = nrs->cds->mesh[0];
// pass the necessary parameters into the kernel we define in the .oudf file
mooseHeatSourceKernel(mesh->Nelements, 0, nrs->o_usrwrk, o_FS);
}
(test/tests/conduction/nonidentical_volume/cylinder/cylinder.udf)Finally, be sure to "load" the custom heat source kernel and create the pointer needed by NekRS to call that function. These two extra steps are quite small - the entire .udf
file required to apply a MOOSE heat source is shown below (a large part of this file is applying initial conditions, which is unrelated to the custom heat source).
#include "udf.hpp"
// the custom kernel we are adding
static occa::kernel mooseHeatSourceKernel;
// build the kernel we are adding
void UDF_LoadKernels(occa::properties & kernelInfo)
{
mooseHeatSourceKernel = oudfBuildKernel(kernelInfo, "mooseHeatSource");
}
void userq(nrs_t * nrs, double time, occa::memory o_S, occa::memory o_FS)
{
auto mesh = nrs->cds->mesh[0];
// pass the necessary parameters into the kernel we define in the .oudf file
mooseHeatSourceKernel(mesh->Nelements, 0, nrs->o_usrwrk, o_FS);
}
void UDF_Setup(nrs_t *nrs)
{
// set initial conditions
auto mesh = nrs->cds->mesh[0];
int n_gll_points = mesh->Np * mesh->Nelements;
for (int n = 0; n < n_gll_points; ++n)
{
nrs->U[n + 0 * nrs->fieldOffset] = 0.0;
nrs->U[n + 1 * nrs->fieldOffset] = 0.0;
nrs->U[n + 2 * nrs->fieldOffset] = 0.0;
nrs->P[n] = 0.0;
nrs->cds->S[n + 0 * nrs->cds->fieldOffset[0]] = 500.0;
}
// set a pointer to the custom source function so that nekRS can call it
udf.sEqnSource = &userq;
}
void UDF_ExecuteStep(nrs_t *nrs, double time, int tstep)
{
}
(test/tests/conduction/nonidentical_volume/cylinder/cylinder.udf)If needed, please consult the NekRS documentation for more information on applying custom sources.
Input Parameters
- initial_source_integral0Initial value to use for the 'postprocessor_to_conserve'; this initial value will be overridden once the coupled app executes its transfer of the volumetric source 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 source term via Cardinal. Remember that this parameter is only used to normalize the source term 'source_variable', so you will need to populate an initial shape.
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 volumetric source 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 source term via Cardinal. Remember that this parameter is only used to normalize the source term 'source_variable', so you will need to populate an initial shape.
- normalization_abs_tol1e-08Absolute tolerance for checking if conservation is maintained during transfer
Default:1e-08
C++ Type:double
Unit:(no unit assumed)
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)
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
- (test/tests/nek_errors/no_occa_source_kernel/nek.i)
- (test/tests/conduction/boundary_and_volume/prism/nek.i)
- (test/tests/postprocessors/nek_point_value/usrwrk_units.i)
- (test/tests/conduction/nonidentical_volume/cylinder/nek.i)
- (test/tests/deformation/vol-areas/nek.i)
- (test/tests/conduction/identical_volume/cube/nek.i)
- (test/tests/nek_errors/no_temp_solve/source.i)
- (test/tests/conduction/nonidentical_volume/nondimensional/nek.i)
- (test/tests/deformation/simple-cube/nek.i)
- (test/tests/nek_errors/no_temp_var/source.i)
- (tutorials/msfr/nek.i)
- (test/tests/conduction/nonidentical_volume/cylinder/nek_exact.i)
- (test/tests/conduction/boundary_and_volume/prism/nek_exact.i)
- (test/tests/nek_errors/usrwrk_transfers/source.i)