diff --git a/framework/doc/content/source/mfem/postprocessors/MFEMVectorBoundaryFluxIntegralPostprocessor.md b/framework/doc/content/source/mfem/postprocessors/MFEMVectorBoundaryFluxIntegralPostprocessor.md new file mode 100644 index 000000000000..7a97f753f84d --- /dev/null +++ b/framework/doc/content/source/mfem/postprocessors/MFEMVectorBoundaryFluxIntegralPostprocessor.md @@ -0,0 +1,31 @@ +# MFEMVectorBoundaryFluxIntegralPostprocessor + +!if! function=hasCapability('mfem') + +## Overview + +Postprocessor for calculating the integral of the flux of a vector $H^{1,d}$, +$L^{2,d}$, $H(\mathrm{curl})$, or $H(\mathrm{div})$ conforming source variable through a given boundary surface. + +!equation +(k \vec v \cdot \hat n)_{\partial\Omega} + +where $\vec v \in H^{1,d}$, $L^{2,d}$, $H(\mathrm{curl})$, or $H(\mathrm{div})$, +$k$ is an optional scalar coefficient, $\partial\Omega$ is the user-specified mesh boundary, +and $\hat n$ is the outward facing unit normal vector on the boundary. +If $k$ is provided as a material coefficient, it must be defined on the boundary itself, rather than on a submesh adjacent to the boundary. + +## Example Input File Syntax + +!listing mfem/submeshes/av_magnetostatic.i block=Postprocessors/CoilCurrent + +!syntax parameters /Postprocessors/MFEMVectorBoundaryFluxIntegralPostprocessor + +!syntax inputs /Postprocessors/MFEMVectorBoundaryFluxIntegralPostprocessor + +!syntax children /Postprocessors/MFEMVectorBoundaryFluxIntegralPostprocessor + +!if-end! + +!else +!include mfem/mfem_warning.md \ No newline at end of file diff --git a/framework/doc/content/source/mfem/postprocessors/MFEMVectorFEInnerProductIntegralPostprocessor.md b/framework/doc/content/source/mfem/postprocessors/MFEMVectorFEInnerProductIntegralPostprocessor.md index 352585e0e031..505055e952e3 100644 --- a/framework/doc/content/source/mfem/postprocessors/MFEMVectorFEInnerProductIntegralPostprocessor.md +++ b/framework/doc/content/source/mfem/postprocessors/MFEMVectorFEInnerProductIntegralPostprocessor.md @@ -15,7 +15,7 @@ and $\Omega$ is the user-specified mesh subdomain. ## Example Input File Syntax -!listing mfem/submeshes/av_magnetostatic.i block=Postprocessors +!listing mfem/submeshes/av_magnetostatic.i block=Postprocessors/CoilPower !syntax parameters /Postprocessors/MFEMVectorFEInnerProductIntegralPostprocessor diff --git a/framework/include/mfem/postprocessors/MFEMVectorBoundaryFluxIntegralPostprocessor.h b/framework/include/mfem/postprocessors/MFEMVectorBoundaryFluxIntegralPostprocessor.h new file mode 100644 index 000000000000..1becca197fc9 --- /dev/null +++ b/framework/include/mfem/postprocessors/MFEMVectorBoundaryFluxIntegralPostprocessor.h @@ -0,0 +1,50 @@ +//* This file is part of the MOOSE framework +//* https://mooseframework.inl.gov +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#ifdef MOOSE_MFEM_ENABLED + +#pragma once + +#include "MFEMPostprocessor.h" +#include "MFEMBoundaryRestrictable.h" + +/** + * Compute the integral of the flux of an MFEM vector variable across a boundary, + * scaled by an optional scalar coefficient. + */ +class MFEMVectorBoundaryFluxIntegralPostprocessor : public MFEMPostprocessor, + public MFEMBoundaryRestrictable +{ +public: + static InputParameters validParams(); + + MFEMVectorBoundaryFluxIntegralPostprocessor(const InputParameters & parameters); + + /** + * Evaluate integral. + */ + virtual void execute() override; + + /** + * Return the last evaluated integral value. + */ + virtual PostprocessorValue getValue() const override final; + +private: + mfem::real_t _integral; + mfem::ParGridFunction & _var; + mfem::Coefficient & _scalar_coef; + mfem::VectorGridFunctionCoefficient _var_coef; + mfem::RT_FECollection _rt_fec; + mfem::ParFiniteElementSpace _rt_vector_fespace; + mfem::ParGridFunction _rt_var; + mfem::ParLinearForm _boundary_integrator; +}; + +#endif diff --git a/framework/src/mfem/postprocessors/MFEMVectorBoundaryFluxIntegralPostprocessor.C b/framework/src/mfem/postprocessors/MFEMVectorBoundaryFluxIntegralPostprocessor.C new file mode 100644 index 000000000000..9f609ead9c45 --- /dev/null +++ b/framework/src/mfem/postprocessors/MFEMVectorBoundaryFluxIntegralPostprocessor.C @@ -0,0 +1,62 @@ +//* This file is part of the MOOSE framework +//* https://mooseframework.inl.gov +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#ifdef MOOSE_MFEM_ENABLED + +#include "MFEMVectorBoundaryFluxIntegralPostprocessor.h" +#include "MFEMProblem.h" + +registerMooseObject("MooseApp", MFEMVectorBoundaryFluxIntegralPostprocessor); + +InputParameters +MFEMVectorBoundaryFluxIntegralPostprocessor::validParams() +{ + InputParameters params = MFEMPostprocessor::validParams(); + params += MFEMBoundaryRestrictable::validParams(); + params.addClassDescription( + "Calculates the integral of the flux of a vector variable across a boundary."); + params.addParam( + "coefficient", "1.", "Name of optional scalar coefficient to scale integrand by."); + params.addRequiredParam("variable", "Name of the vector variable."); + return params; +} + +MFEMVectorBoundaryFluxIntegralPostprocessor::MFEMVectorBoundaryFluxIntegralPostprocessor( + const InputParameters & parameters) + : MFEMPostprocessor(parameters), + MFEMBoundaryRestrictable( + parameters, getMFEMProblem().getMFEMVariableMesh(getParam("variable"))), + _var( + getMFEMProblem().getProblemData().gridfunctions.GetRef(getParam("variable"))), + _scalar_coef(getScalarCoefficient("coefficient")), + _var_coef(&_var), + _rt_fec(_var.ParFESpace()->GetMaxElementOrder(), getMesh().Dimension()), + _rt_vector_fespace(const_cast(&getMesh()), &_rt_fec), + _rt_var(&_rt_vector_fespace), + _boundary_integrator(&_rt_vector_fespace) +{ + _boundary_integrator.AddBoundaryIntegrator( + new mfem::VectorFEBoundaryFluxLFIntegrator(_scalar_coef), getBoundaryMarkers()); +} + +void +MFEMVectorBoundaryFluxIntegralPostprocessor::execute() +{ + _rt_var.ProjectBdrCoefficientNormal(_var_coef, getBoundaryMarkers()); + _boundary_integrator.Assemble(); + _integral = _boundary_integrator(_rt_var); +} + +PostprocessorValue +MFEMVectorBoundaryFluxIntegralPostprocessor::getValue() const +{ + return _integral; +} + +#endif diff --git a/test/tests/mfem/submeshes/av_magnetostatic.i b/test/tests/mfem/submeshes/av_magnetostatic.i index e0f7caaef8be..f68241f89f93 100644 --- a/test/tests/mfem/submeshes/av_magnetostatic.i +++ b/test/tests/mfem/submeshes/av_magnetostatic.i @@ -10,7 +10,20 @@ type = MFEMProblem [] +[SubMeshes] + inactive = 'fluxcut' + [fluxcut] + type = MFEMCutTransitionSubMesh + cut_boundary = 'MeasurementPlane' + block = 'TorusCore TorusSheath' + transition_subdomain = transition_dom + transition_subdomain_boundary = transition_bdr + closed_subdomain = coil_dom + [] +[] + [FESpaces] + inactive = 'FluxFESpace' [HCurlFESpace] type = MFEMVectorFESpace fec_type = ND @@ -21,6 +34,12 @@ fec_type = RT fec_order = CONSTANT [] + [FluxFESpace] + type = MFEMVectorFESpace + fec_type = ND + fec_order = FIRST + submesh = fluxcut + [] [] [Variables] @@ -31,6 +50,7 @@ [] [AuxVariables] + inactive = 'flux_e_field' [b_field] type = MFEMVariable fespace = HDivFESpace @@ -39,6 +59,10 @@ type = MFEMVariable fespace = HCurlFESpace [] + [flux_e_field] + type = MFEMVariable + fespace = FluxFESpace + [] [] [AuxKernels] @@ -70,6 +94,7 @@ [] [FunctorMaterials] + inactive = 'ConductorBoundary' [Vacuum] type = MFEMGenericFunctorMaterial prop_names = reluctivity @@ -81,6 +106,12 @@ prop_values = 1.0 block = 'TorusCore TorusSheath' [] + [ConductorBoundary] + type = MFEMGenericFunctorMaterial + prop_names = conductivity_boundary + prop_values = 1.0 + boundary = 'MeasurementPlane' + [] [] [Kernels] @@ -131,15 +162,23 @@ [] [Transfers] + inactive = 'submesh_transfer_to_fluxsurface' [from_coil] type = MultiAppMFEMCopyTransfer source_variable = e_field variable = e_field from_multi_app = coil [] + [submesh_transfer_to_fluxsurface] + type = MFEMSubMeshTransfer + from_variable = e_field + to_variable = flux_e_field + execute_on = TIMESTEP_END + [] [] [Postprocessors] + inactive = 'CoilCurrent' [CoilPower] type = MFEMVectorFEInnerProductIntegralPostprocessor coefficient = conductivity @@ -147,6 +186,12 @@ primal_variable = e_field block = 'TorusCore TorusSheath' [] + [CoilCurrent] + type = MFEMVectorBoundaryFluxIntegralPostprocessor + coefficient = conductivity_boundary + variable = flux_e_field + boundary = 'MeasurementPlane' + [] [] [Outputs] diff --git a/test/tests/mfem/submeshes/gold/OutputData/AVMagnetostaticClosedCoilBoundaryFluxCSV.csv b/test/tests/mfem/submeshes/gold/OutputData/AVMagnetostaticClosedCoilBoundaryFluxCSV.csv new file mode 100644 index 000000000000..b32a9b5c8b23 --- /dev/null +++ b/test/tests/mfem/submeshes/gold/OutputData/AVMagnetostaticClosedCoilBoundaryFluxCSV.csv @@ -0,0 +1,3 @@ +time,CoilCurrent +0,0 +1,-0.099634666692599 diff --git a/test/tests/mfem/submeshes/tests b/test/tests/mfem/submeshes/tests index 587cf2120b9c..225fe484cd4c 100644 --- a/test/tests/mfem/submeshes/tests +++ b/test/tests/mfem/submeshes/tests @@ -45,7 +45,7 @@ compute_devices = 'cpu cuda' max_parallel = 1 recover = false - [] + [] [MFEMGeometryCutTransitionSubMesh] design = 'syntax/MFEM/ClosedCoilMagnetostatic.md' issues = '#31404' @@ -58,6 +58,25 @@ recover = false valgrind = heavy [] + [MFEMBoundaryFluxCutTransitionSubMesh] + design = 'MFEMVectorBoundaryFluxIntegralPostprocessor.md' + issues = '#32851' + type = CSVDiff + input = av_magnetostatic.i + cli_args = 'SubMeshes/inactive="" ' + 'FESpaces/inactive="" ' + 'AuxVariables/inactive="" ' + 'FunctorMaterials/inactive="" ' + 'Transfers/inactive="" ' + 'Postprocessors/inactive="CoilPower" ' + 'Outputs/ReportedPostprocessors/file_base=OutputData/AVMagnetostaticClosedCoilBoundaryFluxCSV' + csvdiff = 'OutputData/AVMagnetostaticClosedCoilBoundaryFluxCSV.csv' + requirement = 'MOOSE shall have the ability to determine the flux through an internal or external MFEM boundary surface' + capabilities = 'mfem' + compute_devices = 'cpu cuda' + recover = false + valgrind = heavy + [] [MFEMVacuumCutTransitionSubMesh] design = 'syntax/MFEM/ClosedCoilMagnetostatic.md' issues = '#31404'