Line data Source code
1 : //* This file is part of the MOOSE framework
2 : //* https://mooseframework.inl.gov
3 : //*
4 : //* All rights reserved, see COPYRIGHT for full restrictions
5 : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 : //*
7 : //* Licensed under LGPL 2.1, please see LICENSE for details
8 : //* https://www.gnu.org/licenses/lgpl-2.1.html
9 :
10 : #include "MooseVariableFV.h"
11 : #include "TimeIntegrator.h"
12 : #include "NonlinearSystemBase.h"
13 : #include "DisplacedSystem.h"
14 : #include "SystemBase.h"
15 : #include "SubProblem.h"
16 : #include "Assembly.h"
17 : #include "MathFVUtils.h"
18 : #include "FVUtils.h"
19 : #include "FVFluxBC.h"
20 : #include "FVDirichletBCBase.h"
21 : #include "GreenGaussGradient.h"
22 :
23 : #include "libmesh/numeric_vector.h"
24 :
25 : #include <climits>
26 : #include <typeinfo>
27 :
28 : using namespace Moose;
29 :
30 : registerMooseObject("MooseApp", MooseVariableFVReal);
31 :
32 : template <typename OutputType>
33 : InputParameters
34 25064 : MooseVariableFV<OutputType>::validParams()
35 : {
36 25064 : InputParameters params = MooseVariableField<OutputType>::validParams();
37 25064 : params.set<bool>("fv") = true;
38 25064 : params.set<MooseEnum>("family") = "MONOMIAL";
39 25064 : params.set<MooseEnum>("order") = "CONSTANT";
40 75192 : params.template addParam<bool>(
41 : "two_term_boundary_expansion",
42 50128 : true,
43 : "Whether to use a two-term Taylor expansion to calculate boundary face values. "
44 : "If the two-term expansion is used, then the boundary face value depends on the "
45 : "adjoining cell center gradient, which itself depends on the boundary face value. "
46 : "Consequently an implicit solve is used to simultaneously solve for the adjoining cell "
47 : "center gradient and boundary face value(s).");
48 25064 : MooseEnum face_interp_method("average skewness-corrected", "average");
49 25064 : params.template addParam<MooseEnum>("face_interp_method",
50 : face_interp_method,
51 : "Switch that can select between face interpolation methods.");
52 75192 : params.template addParam<bool>(
53 50128 : "cache_cell_gradients", true, "Whether to cache cell gradients or re-compute them.");
54 :
55 : // Depending on the face interpolation we might have to do more than one layer ghosting.
56 25064 : params.addRelationshipManager(
57 : "ElementSideNeighborLayers",
58 : Moose::RelationshipManagerType::GEOMETRIC | Moose::RelationshipManagerType::ALGEBRAIC |
59 : Moose::RelationshipManagerType::COUPLING,
60 0 : [](const InputParameters & obj_params, InputParameters & rm_params)
61 : {
62 12731 : unsigned short layers = 1;
63 12731 : if (obj_params.get<MooseEnum>("face_interp_method") == "skewness-corrected")
64 420 : layers = 2;
65 :
66 12731 : rm_params.set<unsigned short>("layers") = layers;
67 : });
68 50128 : return params;
69 25064 : }
70 :
71 : template <typename OutputType>
72 6374 : MooseVariableFV<OutputType>::MooseVariableFV(const InputParameters & parameters)
73 : : MooseVariableField<OutputType>(parameters),
74 6374 : _solution(this->_sys.currentSolution()),
75 6374 : _phi(this->_assembly.template fePhi<OutputShape>(FEType(CONSTANT, MONOMIAL))),
76 6374 : _grad_phi(this->_assembly.template feGradPhi<OutputShape>(FEType(CONSTANT, MONOMIAL))),
77 6374 : _phi_face(this->_assembly.template fePhiFace<OutputShape>(FEType(CONSTANT, MONOMIAL))),
78 6374 : _grad_phi_face(this->_assembly.template feGradPhiFace<OutputShape>(FEType(CONSTANT, MONOMIAL))),
79 6374 : _phi_face_neighbor(
80 6374 : this->_assembly.template fePhiFaceNeighbor<OutputShape>(FEType(CONSTANT, MONOMIAL))),
81 6374 : _grad_phi_face_neighbor(
82 6374 : this->_assembly.template feGradPhiFaceNeighbor<OutputShape>(FEType(CONSTANT, MONOMIAL))),
83 6374 : _phi_neighbor(this->_assembly.template fePhiNeighbor<OutputShape>(FEType(CONSTANT, MONOMIAL))),
84 6374 : _grad_phi_neighbor(
85 6374 : this->_assembly.template feGradPhiNeighbor<OutputShape>(FEType(CONSTANT, MONOMIAL))),
86 6374 : _prev_elem(nullptr),
87 12748 : _two_term_boundary_expansion(this->isParamValid("two_term_boundary_expansion")
88 11074 : ? this->template getParam<bool>("two_term_boundary_expansion")
89 : : true),
90 12748 : _cache_cell_gradients(this->isParamValid("cache_cell_gradients")
91 11074 : ? this->template getParam<bool>("cache_cell_gradients")
92 19122 : : true)
93 : {
94 6374 : _element_data = std::make_unique<MooseVariableDataFV<OutputType>>(
95 6374 : *this, _sys, _tid, Moose::ElementType::Element, this->_assembly.elem());
96 6374 : _neighbor_data = std::make_unique<MooseVariableDataFV<OutputType>>(
97 6374 : *this, _sys, _tid, Moose::ElementType::Neighbor, this->_assembly.neighbor());
98 :
99 6374 : if (this->isParamValid("face_interp_method"))
100 : {
101 4700 : const auto & interp_method = this->template getParam<MooseEnum>("face_interp_method");
102 4700 : if (interp_method == "average")
103 4560 : _face_interp_method = Moose::FV::InterpMethod::Average;
104 140 : else if (interp_method == "skewness-corrected")
105 140 : _face_interp_method = Moose::FV::InterpMethod::SkewCorrectedAverage;
106 : }
107 : else
108 1674 : _face_interp_method = Moose::FV::InterpMethod::Average;
109 15774 : }
110 :
111 : template <typename OutputType>
112 : void
113 20966107 : MooseVariableFV<OutputType>::clearDofIndices()
114 : {
115 20966107 : _element_data->clearDofIndices();
116 20966107 : }
117 :
118 : template <typename OutputType>
119 : typename MooseVariableFV<OutputType>::OutputData
120 2326 : MooseVariableFV<OutputType>::getElementalValue(const Elem * elem, unsigned int idx) const
121 : {
122 2326 : return _element_data->getElementalValue(elem, Moose::Current, idx);
123 : }
124 :
125 : template <typename OutputType>
126 : typename MooseVariableFV<OutputType>::OutputData
127 0 : MooseVariableFV<OutputType>::getElementalValueOld(const Elem * elem, unsigned int idx) const
128 : {
129 0 : return _element_data->getElementalValue(elem, Moose::Old, idx);
130 : }
131 :
132 : template <typename OutputType>
133 : typename MooseVariableFV<OutputType>::OutputData
134 0 : MooseVariableFV<OutputType>::getElementalValueOlder(const Elem * elem, unsigned int idx) const
135 : {
136 0 : return _element_data->getElementalValue(elem, Moose::Older, idx);
137 : }
138 :
139 : template <typename OutputType>
140 : void
141 430736 : MooseVariableFV<OutputType>::insert(NumericVector<Number> & residual)
142 : {
143 430736 : _element_data->insert(residual);
144 430736 : }
145 :
146 : template <typename OutputType>
147 : void
148 0 : MooseVariableFV<OutputType>::insertLower(NumericVector<Number> &)
149 : {
150 0 : lowerDError();
151 : }
152 :
153 : template <typename OutputType>
154 : void
155 36136 : MooseVariableFV<OutputType>::add(NumericVector<Number> & residual)
156 : {
157 36136 : _element_data->add(residual);
158 36136 : }
159 :
160 : template <typename OutputType>
161 : const typename MooseVariableFV<OutputType>::DoFValue &
162 65 : MooseVariableFV<OutputType>::dofValues() const
163 : {
164 65 : return _element_data->dofValues();
165 : }
166 :
167 : template <typename OutputType>
168 : const typename MooseVariableFV<OutputType>::DoFValue &
169 0 : MooseVariableFV<OutputType>::dofValuesOld() const
170 : {
171 0 : return _element_data->dofValuesOld();
172 : }
173 :
174 : template <typename OutputType>
175 : const typename MooseVariableFV<OutputType>::DoFValue &
176 0 : MooseVariableFV<OutputType>::dofValuesOlder() const
177 : {
178 0 : return _element_data->dofValuesOlder();
179 : }
180 :
181 : template <typename OutputType>
182 : const typename MooseVariableFV<OutputType>::DoFValue &
183 0 : MooseVariableFV<OutputType>::dofValuesPreviousNL() const
184 : {
185 0 : return _element_data->dofValuesPreviousNL();
186 : }
187 :
188 : template <typename OutputType>
189 : const typename MooseVariableFV<OutputType>::DoFValue &
190 0 : MooseVariableFV<OutputType>::dofValuesNeighbor() const
191 : {
192 0 : return _neighbor_data->dofValues();
193 : }
194 :
195 : template <typename OutputType>
196 : const typename MooseVariableFV<OutputType>::DoFValue &
197 0 : MooseVariableFV<OutputType>::dofValuesOldNeighbor() const
198 : {
199 0 : return _neighbor_data->dofValuesOld();
200 : }
201 :
202 : template <typename OutputType>
203 : const typename MooseVariableFV<OutputType>::DoFValue &
204 0 : MooseVariableFV<OutputType>::dofValuesOlderNeighbor() const
205 : {
206 0 : return _neighbor_data->dofValuesOlder();
207 : }
208 :
209 : template <typename OutputType>
210 : const typename MooseVariableFV<OutputType>::DoFValue &
211 0 : MooseVariableFV<OutputType>::dofValuesPreviousNLNeighbor() const
212 : {
213 0 : return _neighbor_data->dofValuesPreviousNL();
214 : }
215 :
216 : template <typename OutputType>
217 : const typename MooseVariableFV<OutputType>::DoFValue &
218 0 : MooseVariableFV<OutputType>::dofValuesDot() const
219 : {
220 0 : return _element_data->dofValuesDot();
221 : }
222 :
223 : template <typename OutputType>
224 : const typename MooseVariableFV<OutputType>::DoFValue &
225 0 : MooseVariableFV<OutputType>::dofValuesDotDot() const
226 : {
227 0 : return _element_data->dofValuesDotDot();
228 : }
229 :
230 : template <typename OutputType>
231 : const typename MooseVariableFV<OutputType>::DoFValue &
232 0 : MooseVariableFV<OutputType>::dofValuesDotOld() const
233 : {
234 0 : return _element_data->dofValuesDotOld();
235 : }
236 :
237 : template <typename OutputType>
238 : const typename MooseVariableFV<OutputType>::DoFValue &
239 0 : MooseVariableFV<OutputType>::dofValuesDotDotOld() const
240 : {
241 0 : return _element_data->dofValuesDotDotOld();
242 : }
243 :
244 : template <typename OutputType>
245 : const typename MooseVariableFV<OutputType>::DoFValue &
246 0 : MooseVariableFV<OutputType>::dofValuesDotNeighbor() const
247 : {
248 0 : return _neighbor_data->dofValuesDot();
249 : }
250 :
251 : template <typename OutputType>
252 : const typename MooseVariableFV<OutputType>::DoFValue &
253 0 : MooseVariableFV<OutputType>::dofValuesDotDotNeighbor() const
254 : {
255 0 : return _neighbor_data->dofValuesDotDot();
256 : }
257 :
258 : template <typename OutputType>
259 : const typename MooseVariableFV<OutputType>::DoFValue &
260 0 : MooseVariableFV<OutputType>::dofValuesDotOldNeighbor() const
261 : {
262 0 : return _neighbor_data->dofValuesDotOld();
263 : }
264 :
265 : template <typename OutputType>
266 : const typename MooseVariableFV<OutputType>::DoFValue &
267 0 : MooseVariableFV<OutputType>::dofValuesDotDotOldNeighbor() const
268 : {
269 0 : return _neighbor_data->dofValuesDotDotOld();
270 : }
271 :
272 : template <typename OutputType>
273 : const MooseArray<Number> &
274 0 : MooseVariableFV<OutputType>::dofValuesDuDotDu() const
275 : {
276 0 : return _element_data->dofValuesDuDotDu();
277 : }
278 :
279 : template <typename OutputType>
280 : const MooseArray<Number> &
281 0 : MooseVariableFV<OutputType>::dofValuesDuDotDotDu() const
282 : {
283 0 : return _element_data->dofValuesDuDotDotDu();
284 : }
285 :
286 : template <typename OutputType>
287 : const MooseArray<Number> &
288 0 : MooseVariableFV<OutputType>::dofValuesDuDotDuNeighbor() const
289 : {
290 0 : return _neighbor_data->dofValuesDuDotDu();
291 : }
292 :
293 : template <typename OutputType>
294 : const MooseArray<Number> &
295 0 : MooseVariableFV<OutputType>::dofValuesDuDotDotDuNeighbor() const
296 : {
297 0 : return _neighbor_data->dofValuesDuDotDotDu();
298 : }
299 :
300 : template <typename OutputType>
301 : void
302 385530 : MooseVariableFV<OutputType>::prepareIC()
303 : {
304 385530 : _element_data->prepareIC();
305 385530 : }
306 :
307 : template <typename OutputType>
308 : void
309 26958530 : MooseVariableFV<OutputType>::computeElemValues()
310 : {
311 26958530 : _element_data->setGeometry(Moose::Volume);
312 26958530 : _element_data->computeValues();
313 26958530 : }
314 :
315 : template <typename OutputType>
316 : void
317 192068 : MooseVariableFV<OutputType>::computeElemValuesFace()
318 : {
319 192068 : _element_data->setGeometry(Moose::Face);
320 192068 : _element_data->computeValues();
321 192068 : }
322 :
323 : template <typename OutputType>
324 : void
325 152188 : MooseVariableFV<OutputType>::computeNeighborValuesFace()
326 : {
327 152188 : _neighbor_data->setGeometry(Moose::Face);
328 152188 : _neighbor_data->computeValues();
329 152188 : }
330 :
331 : template <typename OutputType>
332 : void
333 0 : MooseVariableFV<OutputType>::computeNeighborValues()
334 : {
335 0 : _neighbor_data->setGeometry(Moose::Volume);
336 0 : _neighbor_data->computeValues();
337 0 : }
338 :
339 : template <typename OutputType>
340 : void
341 27086703 : MooseVariableFV<OutputType>::computeFaceValues(const FaceInfo & fi)
342 : {
343 27086703 : _element_data->setGeometry(Moose::Face);
344 27086703 : _neighbor_data->setGeometry(Moose::Face);
345 :
346 27086703 : const auto facetype = fi.faceType(std::make_pair(this->number(), this->sys().number()));
347 27086703 : if (facetype == FaceInfo::VarFaceNeighbors::NEITHER)
348 0 : return;
349 27086703 : else if (facetype == FaceInfo::VarFaceNeighbors::BOTH)
350 : {
351 24974221 : _element_data->computeValuesFace(fi);
352 24974221 : _neighbor_data->computeValuesFace(fi);
353 : }
354 2112482 : else if (facetype == FaceInfo::VarFaceNeighbors::ELEM)
355 2084964 : _element_data->computeValuesFace(fi);
356 27518 : else if (facetype == FaceInfo::VarFaceNeighbors::NEIGHBOR)
357 27518 : _neighbor_data->computeValuesFace(fi);
358 : else
359 0 : mooseError("robert wrote broken MooseVariableFV code");
360 : }
361 :
362 : template <typename OutputType>
363 : OutputType
364 0 : MooseVariableFV<OutputType>::getValue(const Elem * elem) const
365 : {
366 0 : Moose::initDofIndices(const_cast<MooseVariableFV<OutputType> &>(*this), *elem);
367 : mooseAssert(this->_dof_indices.size() == 1, "Wrong size for dof indices");
368 0 : OutputType value = (*this->_sys.currentSolution())(this->_dof_indices[0]);
369 0 : return value;
370 : }
371 :
372 : template <typename OutputType>
373 : typename OutputTools<OutputType>::OutputGradient
374 0 : MooseVariableFV<OutputType>::getGradient(const Elem * /*elem*/) const
375 : {
376 0 : return {};
377 : }
378 :
379 : template <typename OutputType>
380 : void
381 0 : MooseVariableFV<OutputType>::setNodalValue(const OutputType & /*value*/, unsigned int /*idx*/)
382 : {
383 0 : mooseError("FV variables do not support setNodalValue");
384 : }
385 :
386 : template <typename OutputType>
387 : void
388 390600 : MooseVariableFV<OutputType>::setDofValue(const OutputData & value, unsigned int index)
389 : {
390 390600 : _element_data->setDofValue(value, index);
391 390600 : }
392 :
393 : template <typename OutputType>
394 : void
395 4000 : MooseVariableFV<OutputType>::setDofValues(const DenseVector<OutputData> & values)
396 : {
397 4000 : _element_data->setDofValues(values);
398 4000 : }
399 :
400 : template <typename OutputType>
401 : void
402 0 : MooseVariableFV<OutputType>::setLowerDofValues(const DenseVector<OutputData> &)
403 : {
404 0 : lowerDError();
405 : }
406 :
407 : template <typename OutputType>
408 : std::pair<bool, const FVDirichletBCBase *>
409 252631446 : MooseVariableFV<OutputType>::getDirichletBC(const FaceInfo & fi) const
410 : {
411 257058338 : for (const auto bnd_id : fi.boundaryIDs())
412 15587941 : if (auto it = _boundary_id_to_dirichlet_bc.find(bnd_id);
413 15587941 : it != _boundary_id_to_dirichlet_bc.end())
414 11161049 : return {true, it->second};
415 :
416 241470397 : return {false, nullptr};
417 : }
418 :
419 : template <typename OutputType>
420 : std::pair<bool, std::vector<const FVFluxBC *>>
421 2165368 : MooseVariableFV<OutputType>::getFluxBCs(const FaceInfo & fi) const
422 : {
423 3767413 : for (const auto bnd_id : fi.boundaryIDs())
424 2312093 : if (auto it = _boundary_id_to_flux_bc.find(bnd_id); it != _boundary_id_to_flux_bc.end())
425 710048 : return {true, it->second};
426 :
427 2910640 : return std::make_pair(false, std::vector<const FVFluxBC *>());
428 : }
429 :
430 : template <typename OutputType>
431 : ADReal
432 243049329 : MooseVariableFV<OutputType>::getElemValue(const Elem * const elem, const StateArg & state) const
433 : {
434 : mooseAssert(elem,
435 : "The elem shall exist! This typically occurs when the "
436 : "user wants to evaluate non-existing elements (nullptr) at physical boundaries.");
437 : mooseAssert(
438 : this->hasBlocks(elem->subdomain_id()),
439 : "The variable should be defined on the element's subdomain! This typically occurs when the "
440 : "user wants to evaluate the elements right next to the boundary of two variables (block "
441 : "boundary). The subdomain which is queried: " +
442 : Moose::stringify(this->activeSubdomains()) + " the subdomain of the element " +
443 : std::to_string(elem->subdomain_id()));
444 :
445 243049329 : Moose::initDofIndices(const_cast<MooseVariableFV<OutputType> &>(*this), *elem);
446 :
447 : mooseAssert(
448 : this->_dof_indices.size() == 1,
449 : "There should only be one dof-index for a constant monomial variable on any given element");
450 :
451 243049329 : const dof_id_type index = this->_dof_indices[0];
452 :
453 : // It's not safe to use solutionState(0) because it returns the libMesh System solution member
454 : // which is wrong during things like finite difference Jacobian evaluation, e.g. when PETSc
455 : // perturbs the solution vector we feed these perturbations into the current_local_solution
456 : // while the libMesh solution is frozen in the non-perturbed state
457 243049329 : const auto & global_soln =
458 243049329 : (state.state == 0)
459 243049329 : ? *this->_sys.currentSolution()
460 145593984 : : std::as_const(this->_sys).solutionState(state.state, state.iteration_type);
461 :
462 243049329 : ADReal value = global_soln(index);
463 :
464 268513537 : if (ADReal::do_derivatives && state.state == 0 &&
465 25464208 : this->_sys.number() == this->_subproblem.currentNlSysNum())
466 25168687 : Moose::derivInsert(value.derivatives(), index, 1.);
467 :
468 243049329 : return value;
469 0 : }
470 :
471 : template <typename OutputType>
472 : bool
473 247443481 : MooseVariableFV<OutputType>::isDirichletBoundaryFace(const FaceInfo & fi,
474 : const Elem *,
475 : const Moose::StateArg &) const
476 : {
477 247443481 : const auto & pr = getDirichletBC(fi);
478 :
479 : // First member of this pair indicates whether we have a DirichletBC
480 247443481 : return pr.first;
481 : }
482 :
483 : template <typename OutputType>
484 : ADReal
485 3732645 : MooseVariableFV<OutputType>::getDirichletBoundaryFaceValue(const FaceInfo & fi,
486 : const Elem * const libmesh_dbg_var(elem),
487 : const Moose::StateArg & state) const
488 : {
489 : mooseAssert(isDirichletBoundaryFace(fi, elem, state),
490 : "This function should only be called on Dirichlet boundary faces.");
491 :
492 3732645 : const auto & diri_pr = getDirichletBC(fi);
493 :
494 : mooseAssert(diri_pr.first,
495 : "This functor should only be called if we are on a Dirichlet boundary face.");
496 :
497 3732645 : const FVDirichletBCBase & bc = *diri_pr.second;
498 :
499 7465290 : return ADReal(bc.boundaryValue(fi, state));
500 : }
501 :
502 : template <typename OutputType>
503 : bool
504 160774383 : MooseVariableFV<OutputType>::isExtrapolatedBoundaryFace(const FaceInfo & fi,
505 : const Elem * const elem,
506 : const Moose::StateArg & state) const
507 : {
508 160774383 : if (isDirichletBoundaryFace(fi, elem, state))
509 2611735 : return false;
510 : else
511 158162648 : return !this->isInternalFace(fi);
512 : }
513 :
514 : template <typename OutputType>
515 : ADReal
516 680730 : MooseVariableFV<OutputType>::getExtrapolatedBoundaryFaceValue(const FaceInfo & fi,
517 : const bool two_term_expansion,
518 : const bool correct_skewness,
519 : const Elem * elem_to_extrapolate_from,
520 : const StateArg & state) const
521 : {
522 : mooseAssert(
523 : isExtrapolatedBoundaryFace(fi, elem_to_extrapolate_from, state) || !two_term_expansion,
524 : "We allow Dirichlet boundary conditions to call this method. However, the only way to "
525 : "ensure we don't have infinite recursion, with Green Gauss gradients calling back to the "
526 : "Dirichlet boundary condition calling back to this method, is to do a one term expansion");
527 :
528 680730 : ADReal boundary_value;
529 : bool elem_to_extrapolate_from_is_fi_elem;
530 680730 : std::tie(elem_to_extrapolate_from, elem_to_extrapolate_from_is_fi_elem) =
531 0 : [this, &fi, elem_to_extrapolate_from]() -> std::pair<const Elem *, bool>
532 : {
533 680730 : if (elem_to_extrapolate_from)
534 : // Somebody already specified the element to extropolate from
535 676612 : return {elem_to_extrapolate_from, elem_to_extrapolate_from == fi.elemPtr()};
536 : else
537 : {
538 4118 : const auto [elem_guaranteed_to_have_dofs,
539 4118 : other_elem,
540 4118 : elem_guaranteed_to_have_dofs_is_fi_elem] =
541 : Moose::FV::determineElemOneAndTwo(fi, *this);
542 : // We only care about the element guaranteed to have degrees of freedom and current C++
543 : // doesn't allow us to not assign one of the returned items like python does
544 4118 : libmesh_ignore(other_elem);
545 : // We will extrapolate from the element guaranteed to have degrees of freedom
546 4118 : return {elem_guaranteed_to_have_dofs, elem_guaranteed_to_have_dofs_is_fi_elem};
547 : }
548 680730 : }();
549 :
550 680730 : if (two_term_expansion)
551 : {
552 114364 : const Point vector_to_face = elem_to_extrapolate_from_is_fi_elem
553 114364 : ? (fi.faceCentroid() - fi.elemCentroid())
554 1835 : : (fi.faceCentroid() - fi.neighborCentroid());
555 114364 : boundary_value = adGradSln(elem_to_extrapolate_from, state, correct_skewness) * vector_to_face +
556 : getElemValue(elem_to_extrapolate_from, state);
557 : }
558 : else
559 566366 : boundary_value = getElemValue(elem_to_extrapolate_from, state);
560 :
561 1361460 : return boundary_value;
562 0 : }
563 :
564 : template <typename OutputType>
565 : ADReal
566 428564 : MooseVariableFV<OutputType>::getBoundaryFaceValue(const FaceInfo & fi,
567 : const StateArg & state,
568 : const bool correct_skewness) const
569 : {
570 : mooseAssert(!this->isInternalFace(fi),
571 : "A boundary face value has been requested on an internal face.");
572 :
573 428564 : if (isDirichletBoundaryFace(fi, nullptr, state))
574 426062 : return getDirichletBoundaryFaceValue(fi, nullptr, state);
575 2502 : else if (isExtrapolatedBoundaryFace(fi, nullptr, state))
576 2502 : return getExtrapolatedBoundaryFaceValue(
577 2502 : fi, _two_term_boundary_expansion, correct_skewness, nullptr, state);
578 :
579 0 : mooseError("Unknown boundary face type!");
580 : }
581 :
582 : template <typename OutputType>
583 : const VectorValue<ADReal> &
584 38047849 : MooseVariableFV<OutputType>::adGradSln(const Elem * const elem,
585 : const StateArg & state,
586 : const bool correct_skewness) const
587 : {
588 : // We ensure that no caching takes place when we compute skewness-corrected
589 : // quantities.
590 38047849 : if (_cache_cell_gradients && !correct_skewness && state.state == 0)
591 : {
592 22636286 : auto it = _elem_to_grad.find(elem);
593 :
594 22636286 : if (it != _elem_to_grad.end())
595 17039239 : return it->second;
596 : }
597 :
598 21008610 : auto grad = FV::greenGaussGradient(
599 21008610 : ElemArg({elem, correct_skewness}), state, *this, _two_term_boundary_expansion, this->_mesh);
600 :
601 21008610 : if (_cache_cell_gradients && !correct_skewness && state.state == 0)
602 : {
603 5597047 : auto pr = _elem_to_grad.emplace(elem, std::move(grad));
604 : mooseAssert(pr.second, "Insertion should have just happened.");
605 5597047 : return pr.first->second;
606 : }
607 : else
608 : {
609 15411563 : _temp_cell_gradient = std::move(grad);
610 15411563 : return _temp_cell_gradient;
611 : }
612 21008610 : }
613 :
614 : template <typename OutputType>
615 : VectorValue<ADReal>
616 11877055 : MooseVariableFV<OutputType>::uncorrectedAdGradSln(const FaceInfo & fi,
617 : const StateArg & state,
618 : const bool correct_skewness) const
619 : {
620 11877055 : const bool var_defined_on_elem = this->hasBlocks(fi.elem().subdomain_id());
621 11877055 : const Elem * const elem_one = var_defined_on_elem ? &fi.elem() : fi.neighborPtr();
622 11877055 : const Elem * const elem_two = var_defined_on_elem ? fi.neighborPtr() : &fi.elem();
623 :
624 11877055 : const VectorValue<ADReal> elem_one_grad = adGradSln(elem_one, state, correct_skewness);
625 :
626 : // If we have a neighbor then we interpolate between the two to the face. If we do not, then we
627 : // apply a zero Hessian assumption and use the element centroid gradient as the uncorrected face
628 : // gradient
629 11877055 : if (elem_two && this->hasBlocks(elem_two->subdomain_id()))
630 : {
631 11502731 : const VectorValue<ADReal> & elem_two_grad = adGradSln(elem_two, state, correct_skewness);
632 :
633 : // Uncorrected gradient value
634 11502731 : return Moose::FV::linearInterpolation(elem_one_grad, elem_two_grad, fi, var_defined_on_elem);
635 : }
636 : else
637 374324 : return elem_one_grad;
638 11877055 : }
639 :
640 : template <typename OutputType>
641 : VectorValue<ADReal>
642 15508921 : MooseVariableFV<OutputType>::adGradSln(const FaceInfo & fi,
643 : const StateArg & state,
644 : const bool correct_skewness) const
645 : {
646 15508921 : const bool var_defined_on_elem = this->hasBlocks(fi.elem().subdomain_id());
647 15508921 : const Elem * const elem = &fi.elem();
648 15508921 : const Elem * const neighbor = fi.neighborPtr();
649 :
650 15508921 : const bool is_internal_face = this->isInternalFace(fi);
651 :
652 15508921 : const ADReal side_one_value = (!is_internal_face && !var_defined_on_elem)
653 15508921 : ? getBoundaryFaceValue(fi, state, correct_skewness)
654 : : getElemValue(elem, state);
655 15508921 : const ADReal side_two_value = (var_defined_on_elem && !is_internal_face)
656 15508921 : ? getBoundaryFaceValue(fi, state, correct_skewness)
657 : : getElemValue(neighbor, state);
658 :
659 15508921 : const auto delta =
660 15508921 : this->isInternalFace(fi)
661 15508921 : ? fi.dCNMag()
662 427284 : : (fi.faceCentroid() - (var_defined_on_elem ? fi.elemCentroid() : fi.neighborCentroid()))
663 427284 : .norm();
664 :
665 : // This is the component of the gradient which is parallel to the line connecting
666 : // the cell centers. Therefore, we can use our second order, central difference
667 : // scheme to approximate it.
668 15508921 : auto face_grad = ((side_two_value - side_one_value) / delta) * fi.eCN();
669 :
670 : // We only need non-orthogonal correctors in 2+ dimensions
671 15508921 : if (this->_mesh.dimension() > 1)
672 : {
673 : // We are using an orthogonal approach for the non-orthogonal correction, for more information
674 : // see Hrvoje Jasak's PhD Thesis (Imperial College, 1996)
675 11877055 : const auto & interpolated_gradient = uncorrectedAdGradSln(fi, state, correct_skewness);
676 11877055 : face_grad += interpolated_gradient - (interpolated_gradient * fi.eCN()) * fi.eCN();
677 11877055 : }
678 :
679 31017842 : return face_grad;
680 15508921 : }
681 :
682 : template <typename OutputType>
683 : void
684 259347 : MooseVariableFV<OutputType>::residualSetup()
685 : {
686 259347 : if (!_dirichlet_map_setup)
687 20881 : determineBoundaryToDirichletBCMap();
688 259347 : if (!_flux_map_setup)
689 20881 : determineBoundaryToFluxBCMap();
690 :
691 259347 : clearCaches();
692 259347 : }
693 :
694 : template <typename OutputType>
695 : void
696 57433 : MooseVariableFV<OutputType>::jacobianSetup()
697 : {
698 57433 : clearCaches();
699 57433 : }
700 :
701 : template <typename OutputType>
702 : void
703 316780 : MooseVariableFV<OutputType>::clearCaches()
704 : {
705 316780 : _elem_to_grad.clear();
706 316780 : }
707 :
708 : template <typename OutputType>
709 : unsigned int
710 5808 : MooseVariableFV<OutputType>::oldestSolutionStateRequested() const
711 : {
712 5808 : unsigned int state = 0;
713 5808 : state = std::max(state, _element_data->oldestSolutionStateRequested());
714 5808 : state = std::max(state, _neighbor_data->oldestSolutionStateRequested());
715 5808 : return state;
716 : }
717 :
718 : template <typename OutputType>
719 : void
720 31683 : MooseVariableFV<OutputType>::clearAllDofIndices()
721 : {
722 31683 : _element_data->clearDofIndices();
723 31683 : _neighbor_data->clearDofIndices();
724 31683 : }
725 :
726 : template <typename OutputType>
727 : typename MooseVariableFV<OutputType>::ValueType
728 86240534 : MooseVariableFV<OutputType>::evaluate(const FaceArg & face, const StateArg & state) const
729 : {
730 86240534 : const FaceInfo * const fi = face.fi;
731 : mooseAssert(fi, "The face information must be non-null");
732 86240534 : if (isDirichletBoundaryFace(*fi, face.face_side, state))
733 3306583 : return getDirichletBoundaryFaceValue(*fi, face.face_side, state);
734 82933951 : else if (isExtrapolatedBoundaryFace(*fi, face.face_side, state))
735 : {
736 678228 : bool two_term_boundary_expansion = _two_term_boundary_expansion;
737 678228 : if (face.limiter_type == Moose::FV::LimiterType::Upwind)
738 219 : if ((face.elem_is_upwind && face.face_side == fi->elemPtr()) ||
739 0 : (!face.elem_is_upwind && face.face_side == fi->neighborPtr()))
740 219 : two_term_boundary_expansion = false;
741 678228 : return getExtrapolatedBoundaryFaceValue(
742 678228 : *fi, two_term_boundary_expansion, face.correct_skewness, face.face_side, state);
743 : }
744 : else
745 : {
746 : mooseAssert(this->isInternalFace(*fi),
747 : "We must be either Dirichlet, extrapolated, or internal");
748 82255723 : return Moose::FV::interpolate(*this, face, state);
749 : }
750 : }
751 :
752 : template <typename OutputType>
753 : typename MooseVariableFV<OutputType>::ValueType
754 0 : MooseVariableFV<OutputType>::evaluate(const NodeArg & node_arg, const StateArg & state) const
755 : {
756 0 : const auto & node_to_elem_map = this->_mesh.nodeToElemMap();
757 0 : const auto & elem_ids = libmesh_map_find(node_to_elem_map, node_arg.node->id());
758 0 : ValueType sum = 0;
759 0 : Real total_weight = 0;
760 : mooseAssert(elem_ids.size(), "There should always be at least one element connected to a node");
761 0 : for (const auto elem_id : elem_ids)
762 : {
763 0 : const Elem * const elem = this->_mesh.queryElemPtr(elem_id);
764 : mooseAssert(elem, "We should have this element available");
765 0 : if (!this->hasBlocks(elem->subdomain_id()))
766 0 : continue;
767 0 : const ElemPointArg elem_point{
768 0 : elem, *node_arg.node, _face_interp_method == Moose::FV::InterpMethod::SkewCorrectedAverage};
769 0 : const auto weight = 1 / (*node_arg.node - elem->vertex_average()).norm();
770 0 : sum += weight * (*this)(elem_point, state);
771 0 : total_weight += weight;
772 : }
773 0 : return sum / total_weight;
774 0 : }
775 :
776 : template <typename OutputType>
777 : typename MooseVariableFV<OutputType>::DotType
778 : MooseVariableFV<OutputType>::evaluateDot(const ElemArg &, const StateArg &) const
779 : {
780 : mooseError("evaluateDot not implemented for this class of finite volume variables");
781 : }
782 :
783 : template <>
784 : ADReal
785 55560 : MooseVariableFV<Real>::evaluateDot(const ElemArg & elem_arg, const StateArg & state) const
786 : {
787 55560 : const Elem * const elem = elem_arg.elem;
788 : mooseAssert(state.state == 0,
789 : "We dot not currently support any time derivative evaluations other than for the "
790 : "current time-step");
791 : mooseAssert(_time_integrator && _time_integrator->dt(),
792 : "A time derivative is being requested but we do not have a time integrator so we'll "
793 : "have no idea how to compute it");
794 :
795 55560 : Moose::initDofIndices(const_cast<MooseVariableFV<Real> &>(*this), *elem);
796 :
797 : mooseAssert(
798 : this->_dof_indices.size() == 1,
799 : "There should only be one dof-index for a constant monomial variable on any given element");
800 :
801 55560 : const dof_id_type dof_index = this->_dof_indices[0];
802 :
803 55560 : if (_var_kind == Moose::VAR_SOLVER)
804 : {
805 55560 : ADReal dot = (*_solution)(dof_index);
806 104136 : if (ADReal::do_derivatives && state.state == 0 &&
807 48576 : _sys.number() == _subproblem.currentNlSysNum())
808 48576 : Moose::derivInsert(dot.derivatives(), dof_index, 1.);
809 55560 : _time_integrator->computeADTimeDerivatives(dot, dof_index, _ad_real_dummy);
810 55560 : return dot;
811 55560 : }
812 : else
813 0 : return (*_sys.solutionUDot())(dof_index);
814 : }
815 :
816 : template <>
817 : ADReal
818 0 : MooseVariableFV<Real>::evaluateDot(const FaceArg & face, const StateArg & state) const
819 : {
820 0 : const FaceInfo * const fi = face.fi;
821 : mooseAssert(fi, "The face information must be non-null");
822 0 : if (isDirichletBoundaryFace(*fi, face.face_side, state))
823 0 : return ADReal(0.0); // No time derivative if boundary value is set
824 0 : else if (isExtrapolatedBoundaryFace(*fi, face.face_side, state))
825 : {
826 : mooseAssert(face.face_side && this->hasBlocks(face.face_side->subdomain_id()),
827 : "If we are an extrapolated boundary face, then our FunctorBase::checkFace method "
828 : "should have assigned a non-null element that we are defined on");
829 0 : const auto elem_arg = ElemArg({face.face_side, face.correct_skewness});
830 : // For extrapolated boundary faces, note that we take the value of the time derivative at the
831 : // cell in contact with the face
832 0 : return evaluateDot(elem_arg, state);
833 : }
834 : else
835 : {
836 : mooseAssert(this->isInternalFace(*fi),
837 : "We must be either Dirichlet, extrapolated, or internal");
838 0 : return Moose::FV::interpolate<ADReal, FunctorEvaluationKind::Dot>(*this, face, state);
839 : }
840 : }
841 :
842 : template <>
843 : ADReal
844 0 : MooseVariableFV<Real>::evaluateDot(const ElemQpArg & elem_qp, const StateArg & state) const
845 : {
846 0 : return evaluateDot(ElemArg({elem_qp.elem, /*correct_skewness*/ false}), state);
847 : }
848 :
849 : template <typename OutputType>
850 : void
851 108528 : MooseVariableFV<OutputType>::prepareAux()
852 : {
853 108528 : _element_data->prepareAux();
854 108528 : _neighbor_data->prepareAux();
855 108528 : }
856 :
857 : template <typename OutputType>
858 : void
859 27035 : MooseVariableFV<OutputType>::determineBoundaryToDirichletBCMap()
860 : {
861 : mooseAssert(!Threads::in_threads,
862 : "This routine has not been implemented for threads. Please query this routine before "
863 : "a threaded region or contact a MOOSE developer to discuss.");
864 :
865 27035 : _boundary_id_to_dirichlet_bc.clear();
866 27035 : std::vector<FVDirichletBCBase *> bcs;
867 :
868 : // I believe because query() returns by value but condition returns by reference that binding to a
869 : // const lvalue reference results in the query() getting destructed and us holding onto a dangling
870 : // reference. I think that condition returned by value we would be able to bind to a const lvalue
871 : // reference here. But as it is we'll bind to a regular lvalue
872 27035 : const auto base_query = this->_subproblem.getMooseApp()
873 27035 : .theWarehouse()
874 : .query()
875 27035 : .template condition<AttribSystem>("FVDirichletBC")
876 27035 : .template condition<AttribThread>(_tid)
877 27035 : .template condition<AttribVar>(_var_num)
878 27035 : .template condition<AttribSysNum>(this->_sys.number());
879 :
880 120085 : for (const auto bnd_id : this->_mesh.getBoundaryIDs())
881 : {
882 93050 : auto base_query_copy = base_query;
883 186100 : base_query_copy.template condition<AttribBoundaries>(std::set<BoundaryID>({bnd_id}))
884 93050 : .queryInto(bcs);
885 : mooseAssert(bcs.size() <= 1, "cannot have multiple dirichlet BCs on the same boundary");
886 93050 : if (!bcs.empty())
887 38815 : _boundary_id_to_dirichlet_bc.emplace(bnd_id, bcs[0]);
888 : }
889 :
890 27035 : _dirichlet_map_setup = true;
891 27035 : }
892 :
893 : template <typename OutputType>
894 : void
895 27035 : MooseVariableFV<OutputType>::determineBoundaryToFluxBCMap()
896 : {
897 : mooseAssert(!Threads::in_threads,
898 : "This routine has not been implemented for threads. Please query this routine before "
899 : "a threaded region or contact a MOOSE developer to discuss.");
900 :
901 27035 : _boundary_id_to_flux_bc.clear();
902 27035 : std::vector<const FVFluxBC *> bcs;
903 :
904 : // I believe because query() returns by value but condition returns by reference that binding to a
905 : // const lvalue reference results in the query() getting destructed and us holding onto a dangling
906 : // reference. I think that condition returned by value we would be able to bind to a const lvalue
907 : // reference here. But as it is we'll bind to a regular lvalue
908 27035 : const auto base_query = this->_subproblem.getMooseApp()
909 27035 : .theWarehouse()
910 : .query()
911 27035 : .template condition<AttribSystem>("FVFluxBC")
912 27035 : .template condition<AttribThread>(_tid)
913 27035 : .template condition<AttribVar>(_var_num)
914 27035 : .template condition<AttribSysNum>(this->_sys.number());
915 :
916 120085 : for (const auto bnd_id : this->_mesh.getBoundaryIDs())
917 : {
918 93050 : auto base_query_copy = base_query;
919 186100 : base_query_copy.template condition<AttribBoundaries>(std::set<BoundaryID>({bnd_id}))
920 93050 : .queryInto(bcs);
921 93050 : if (!bcs.empty())
922 26773 : _boundary_id_to_flux_bc.emplace(bnd_id, bcs);
923 : }
924 :
925 27035 : _flux_map_setup = true;
926 27035 : }
927 :
928 : template class MooseVariableFV<Real>;
929 : // TODO: implement vector fv variable support. This will require some template
930 : // specializations for various member functions in this and the FV variable
931 : // classes. And then you will need to uncomment out the line below:
932 : // template class MooseVariableFV<RealVectorValue>;
|