diff --git a/framework/include/variables/MooseVariableFE.h b/framework/include/variables/MooseVariableFE.h index a66a1ed5cdde..f8d232d3b150 100644 --- a/framework/include/variables/MooseVariableFE.h +++ b/framework/include/variables/MooseVariableFE.h @@ -698,16 +698,22 @@ class MooseVariableFE : public MooseVariableField using NodeArg = Moose::NodeArg; using ElemPointArg = Moose::ElemPointArg; + /** + * A common method that both evaluate(FaceArg) and evaluateDot(FaceArg) can call. A value + * evaluation vs dot evaluation is delineated via the passed-in \p cache_data, e.g. if the + * passed-in cache data is the sln data member then this will return a value evaluation and if the + * cache data is the dot data member then this will return a dot evaluation + */ + ValueType + faceEvaluate(const FaceArg &, const StateArg &, const std::vector & cache_data) const; + ValueType evaluate(const ElemQpArg & elem_qp, const StateArg & state) const override final; ValueType evaluate(const ElemSideQpArg & elem_side_qp, const StateArg & state) const override final; ValueType evaluate(const ElemArg &, const StateArg &) const override final; ValueType evaluate(const ElemPointArg &, const StateArg &) const override final; ValueType evaluate(const NodeArg & node_arg, const StateArg & state) const override final; - ValueType evaluate(const FaceArg &, const StateArg &) const override final - { - mooseError("Face info functor overload not yet implemented for finite element variables"); - } + ValueType evaluate(const FaceArg &, const StateArg &) const override final; GradientType evaluateGradient(const ElemQpArg & elem_qp, const StateArg & state) const override; GradientType evaluateGradient(const ElemSideQpArg & elem_side_qp, diff --git a/framework/src/variables/MooseVariableFE.C b/framework/src/variables/MooseVariableFE.C index 8180550a156a..47f53b7409e3 100644 --- a/framework/src/variables/MooseVariableFE.C +++ b/framework/src/variables/MooseVariableFE.C @@ -1031,6 +1031,52 @@ MooseVariableFE::evaluate(const ElemArg & elem_arg, const StateArg & return _current_elem_qp_functor_sln[0]; } +template +typename MooseVariableFE::ValueType +MooseVariableFE::faceEvaluate(const FaceArg & face_arg, + const StateArg & state, + const std::vector & cache_data) const +{ + const QMonomial qrule(face_arg.fi->elem().dim() - 1, CONSTANT); + auto side_evaluate = + [this, &qrule, &state, &cache_data](const Elem * const elem, const unsigned int side) + { + // We can use whatever we want for the point argument since it won't be used + const ElemSideQpArg elem_side_qp_arg{elem, side, /*qp=*/0, &qrule, Point(0, 0, 0)}; + evaluateOnElementSide(elem_side_qp_arg, state, /*cache_eligible=*/false); + return cache_data[0]; + }; + + const auto continuity = this->getContinuity(); + const bool on_elem = !face_arg.face_side || (face_arg.face_side == face_arg.fi->elemPtr()); + const bool on_neighbor = + !face_arg.face_side || (face_arg.face_side == face_arg.fi->neighborPtr()); + if (on_neighbor) + mooseAssert( + face_arg.fi->neighborPtr(), + "If we are signaling we should evaluate on the neighbor, we better have a neighbor"); + + // Only do multiple evaluations if we are not continuous and we are on an internal face + if ((continuity != C_ZERO && continuity != C_ONE) && on_elem && on_neighbor) + return (side_evaluate(face_arg.fi->elemPtr(), face_arg.fi->elemSideID()) + + side_evaluate(face_arg.fi->neighborPtr(), face_arg.fi->neighborSideID())) / + 2; + else if (on_elem) + return side_evaluate(face_arg.fi->elemPtr(), face_arg.fi->elemSideID()); + else if (on_neighbor) + return side_evaluate(face_arg.fi->neighborPtr(), face_arg.fi->neighborSideID()); + else + mooseError( + "Attempted to evaluate a moose finite element variable on a face where it is not defined"); +} + +template +typename MooseVariableFE::ValueType +MooseVariableFE::evaluate(const FaceArg & face_arg, const StateArg & state) const +{ + return faceEvaluate(face_arg, state, _current_elem_side_qp_functor_sln); +} + template typename MooseVariableFE::ValueType MooseVariableFE::evaluate(const ElemPointArg & elem_point_arg, @@ -1214,12 +1260,7 @@ MooseVariableFE::evaluateDot(const FaceArg & face_arg, const StateAr mooseAssert(_time_integrator && _time_integrator->dt(), "A time derivative is being requested but we do not have a time integrator so we'll " "have no idea how to compute it"); - const QMonomial qrule(face_arg.fi->elem().dim() - 1, CONSTANT); - // We can use whatever we want for the point argument since it won't be used - const ElemSideQpArg elem_side_qp_arg{ - face_arg.fi->elemPtr(), face_arg.fi->elemSideID(), /*qp=*/0, &qrule, Point(0, 0, 0)}; - evaluateOnElementSide(elem_side_qp_arg, state, /*cache_eligible=*/false); - return _current_elem_side_qp_functor_dot[0]; + return faceEvaluate(face_arg, state, _current_elem_side_qp_functor_dot); } template <> diff --git a/test/tests/functors/fe-var-for-fv-neumann/gold/test_out.e b/test/tests/functors/fe-var-for-fv-neumann/gold/test_out.e new file mode 100644 index 000000000000..588790717cff Binary files /dev/null and b/test/tests/functors/fe-var-for-fv-neumann/gold/test_out.e differ diff --git a/test/tests/functors/fe-var-for-fv-neumann/test.i b/test/tests/functors/fe-var-for-fv-neumann/test.i new file mode 100644 index 000000000000..f82bf2bc18d7 --- /dev/null +++ b/test/tests/functors/fe-var-for-fv-neumann/test.i @@ -0,0 +1,68 @@ +[Mesh] + [gen] + type = GeneratedMeshGenerator + dim = 1 + nx = 20 + [] +[] + +[Variables] + [fe][] + [fv] + type = MooseVariableFVReal + [] +[] + +[Kernels] + [diff] + type = Diffusion + variable = fe + [] +[] + +[FVKernels] + [diff] + type = FVDiffusion + variable = fv + coeff = 1 + [] +[] + +[BCs] + [left] + type = DirichletBC + variable = fe + value = 0 + boundary = left + [] + [right] + type = DirichletBC + variable = fe + value = 1 + boundary = right + [] + [] + +[FVBCs] + [left] + type = FVDirichletBC + variable = fv + value = 0 + boundary = left + [] + [right] + type = FVFunctorNeumannBC + variable = fv + functor = fe + boundary = right + [] +[] + +[Executioner] + type = Steady + solve_type = NEWTON +[] + +[Outputs] + exodus = true +[] diff --git a/test/tests/functors/fe-var-for-fv-neumann/tests b/test/tests/functors/fe-var-for-fv-neumann/tests new file mode 100644 index 000000000000..1280dc04720a --- /dev/null +++ b/test/tests/functors/fe-var-for-fv-neumann/tests @@ -0,0 +1,18 @@ +[Tests] + design = 'Functors/index.md' + issues = '#19420' + [exo] + type = Exodiff + input = test.i + exodiff = test_out.e + requirement = 'The system shall be able to accurately evaluate a finite element variable through the functor system in a finite volume Dirichlet boundary condition.' + [] + [jac] + type = PetscJacobianTester + ratio_tol = 1e-7 + difference_tol = 1e-5 + input = test.i + run_sim = True + requirement = 'The system shall compute the correct Jacobian when evaluating a finite element variable through the functor system in a finite volume Dirichlet boundary condition.' + [] +[]