https://mooseframework.inl.gov
Loading...
Searching...
No Matches
INSFVRhieChowInterpolatorSegregated.C
Go to the documentation of this file.
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
11#include "INSFVAttributes.h"
12#include "SubProblem.h"
13#include "MooseMesh.h"
14#include "NS.h"
15#include "Assembly.h"
21
22#include "libmesh/mesh_base.h"
23#include "libmesh/elem_range.h"
24#include "metaphysicl/dualsemidynamicsparsenumberarray.h"
25
26#include "NonlinearSystem.h"
27#include "libmesh/petsc_matrix.h"
28#include "libmesh/petsc_vector.h"
29
31
34{
36
37 params.addClassDescription("Computes H/A and 1/A together with face velocities for segregated "
38 "momentum-pressure equations.");
39
40 // We disable the execution of this, should only provide functions
41 // for the SIMPLENonlinearAssembly executioner
42 ExecFlagEnum & exec_enum = params.set<ExecFlagEnum>("execute_on", true);
44 exec_enum = {EXEC_NONE};
45 params.suppressParameter<ExecFlagEnum>("execute_on");
46
47 return params;
48}
49
51 const InputParameters & params)
53 _HbyA(_moose_mesh, blockIDs(), "HbyA"),
54 _Ainv(_moose_mesh, blockIDs(), "Ainv", false),
56 name(),
57 [this](const auto & r, const auto & t) -> ADRealVectorValue
58 {
59 ADRealVectorValue velocity((*_u)(r, t));
60 if (_dim >= 2)
61 velocity(1) = (*_v)(r, t);
62 if (_dim >= 3)
63 velocity(2) = (*_w)(r, t);
64 return velocity;
65 },
66 std::set<ExecFlagType>({EXEC_ALWAYS}),
67 _moose_mesh,
68 blockIDs())),
69 _face_velocity(_moose_mesh, blockIDs(), "face_values")
70{
71 if (_displaced)
72 paramError("use_displaced_mesh",
73 "The segregated Rhie-Chow user object does not currently support operation on a "
74 "displaced mesh");
75
76 // Register the elemental/face functors which will be queried in the pressure equation
77 for (const auto tid : make_range(libMesh::n_threads()))
78 {
79 UserObject::_subproblem.addFunctor("Ainv", _Ainv, tid);
80 UserObject::_subproblem.addFunctor("HbyA", _HbyA, tid);
81 }
82
83 if (_velocity_interp_method == Moose::FV::InterpMethod::Average)
84 paramError("velocity_interp_method",
85 "Segregated momentum-pressure solvers do not allow average interpolation methods!");
86
87 if (!dynamic_cast<SIMPLENonlinearAssembly *>(getMooseApp().getExecutioner()))
88 mooseError(this->name(), " should only be used with a segregated thermal-hydraulics solver!");
89}
90
91void
93 std::vector<NonlinearSystemBase *> momentum_systems,
94 const std::vector<unsigned int> & momentum_system_numbers,
95 const TagID pressure_gradient_tag)
96{
97 _momentum_systems = momentum_systems;
98 _momentum_system_numbers = momentum_system_numbers;
99 _pressure_gradient_tag = pressure_gradient_tag;
100
102 for (auto & system : _momentum_systems)
104 dynamic_cast<NonlinearImplicitSystem *>(&system->system()));
105}
106
107void
109{
110 _HbyA.clear();
111 _Ainv.clear();
112 _face_velocity.clear();
113}
114
115void
117{
118 for (const auto & pair : _HbyA)
119 _HbyA[pair.first] = 0;
120
121 for (const auto & pair : _Ainv)
122 _Ainv[pair.first] = 0;
123}
124
125void
127{
128 for (auto & fi : _fe_problem.mesh().faceInfo())
129 {
130 if (hasBlocks(fi->elemPtr()->subdomain_id()) ||
131 (fi->neighborPtr() && hasBlocks(fi->neighborPtr()->subdomain_id())))
132 {
133 // On internal face we do a regular interpoaltion with geometric weights
134 if (_u->isInternalFace(*fi))
135 {
136 const Moose::FaceArg face{
137 fi, Moose::FV::LimiterType::CentralDifference, true, false, nullptr, nullptr};
138
140 }
141 // On the boundary, we just take the boundary values
142 else
143 {
144 const Elem * const boundary_elem =
145 hasBlocks(fi->elemPtr()->subdomain_id()) ? fi->elemPtr() : fi->neighborPtr();
146
147 const Moose::FaceArg boundary_face{
148 fi, Moose::FV::LimiterType::CentralDifference, true, false, boundary_elem, nullptr};
149
150 _face_velocity[fi->id()] =
151 MetaPhysicL::raw_value((*_vel)(boundary_face, Moose::currentState()));
152 }
153 }
154 }
155}
156
157VectorValue<ADReal>
159 const FaceInfo & fi,
160 const Moose::StateArg & /*time*/,
161 const THREAD_ID /*tid*/,
162 const bool /*subtract_mesh_velocity*/) const
163{
164 if (m != Moose::FV::InterpMethod::RhieChow)
165 mooseError("Segregated solution algorithms only support Rhie-Chow interpolation!");
166 return _face_velocity.evaluate(&fi);
167}
168
169void
171{
172 const auto time_arg = Moose::currentState();
173
174 for (auto & fi : _fe_problem.mesh().faceInfo())
175 {
176 if (hasBlocks(fi->elemPtr()->subdomain_id()) ||
177 (fi->neighborPtr() && hasBlocks(fi->neighborPtr()->subdomain_id())))
178 {
179 // On internal face we just use the interpolated H/A and the pressure face gradient
180 // So u_f = -(H/A)_f - (1/A)_f*grad(p)_f
181 // Notice the (-) sign on H/A which is because we use the Jacobian/Residual
182 // computations and we get -H instead of H.
183 if (_u->isInternalFace(*fi))
184 {
185 const Moose::FaceArg face{
186 fi, Moose::FV::LimiterType::CentralDifference, true, false, nullptr, nullptr};
187
188 RealVectorValue Ainv;
189 RealVectorValue HbyA = MetaPhysicL::raw_value(_HbyA(face, time_arg));
190
191 interpolate(Moose::FV::InterpMethod::Average,
192 Ainv,
193 _Ainv(makeElemArg(fi->elemPtr()), time_arg),
194 _Ainv(makeElemArg(fi->neighborPtr()), time_arg),
195 *fi,
196 true);
197
198 RealVectorValue grad_p = MetaPhysicL::raw_value(_p->gradient(face, time_arg));
199 for (const auto comp_index : make_range(_dim))
200 _face_velocity[fi->id()](comp_index) =
201 -HbyA(comp_index) - Ainv(comp_index) * grad_p(comp_index);
202 }
203 else
204 {
205 const Elem * const boundary_elem =
206 hasBlocks(fi->elemPtr()->subdomain_id()) ? fi->elemPtr() : fi->neighborPtr();
207 const Moose::FaceArg boundary_face{
208 fi, Moose::FV::LimiterType::CentralDifference, true, false, boundary_elem, nullptr};
209
210 // If we have a dirichlet boundary conditions, this sill give us the exact value of the
211 // velocity on the face as expected (see populateHbyA())
212 if (_u->isDirichletBoundaryFace(*fi, boundary_elem, time_arg))
213 _face_velocity[fi->id()] = -MetaPhysicL::raw_value(_HbyA(boundary_face, time_arg));
214 else
215 {
216 const RealVectorValue & Ainv = MetaPhysicL::raw_value(_Ainv(boundary_face, time_arg));
217 const RealVectorValue & HbyA = MetaPhysicL::raw_value(_HbyA(boundary_face, time_arg));
218 const RealVectorValue & grad_p =
219 MetaPhysicL::raw_value(_p->gradient(boundary_face, time_arg));
220 for (const auto comp_index : make_range(_dim))
221 _face_velocity[fi->id()](comp_index) =
222 -HbyA(comp_index) - Ainv(comp_index) * grad_p(comp_index);
223 }
224 }
225 }
226 }
227}
228
229void
231{
232 std::vector<unsigned int> var_nums = {_momentum_implicit_systems[0]->variable_number(_u->name())};
233 if (_v)
234 var_nums.push_back(_momentum_implicit_systems[1]->variable_number(_v->name()));
235 if (_w)
236 var_nums.push_back(_momentum_implicit_systems[2]->variable_number(_w->name()));
237
238 const auto time_arg = Moose::currentState();
239
240 for (auto & elem :
241 as_range(_mesh.active_local_elements_begin(), _mesh.active_local_elements_end()))
242 {
243 if (hasBlocks(elem->subdomain_id()))
244 {
245 const auto elem_arg = makeElemArg(elem);
246 const RealVectorValue Ainv = _Ainv(elem_arg, time_arg);
247 const RealVectorValue & grad_p = raw_value(_p->gradient(elem_arg, time_arg));
248
249 for (auto comp_index : make_range(_dim))
250 {
251 // If we are doing segregated momentum components we need to access different vector
252 // components otherwise everything is in the same vector (with different variable names)
253 const unsigned int system_number = _momentum_implicit_systems[comp_index]->number();
254 const auto index = elem->dof_number(system_number, var_nums[comp_index], 0);
255
256 // We set the dof value in the solution vector the same logic applies:
257 // u_C = -(H/A)_C - (1/A)_C*grad(p)_C where C is the cell index
258 _momentum_implicit_systems[comp_index]->solution->set(
259 index, -(*_HbyA_raw[comp_index])(index)-Ainv(comp_index) * grad_p(comp_index));
260 }
261 }
262 }
263
264 for (auto system_i : index_range(_momentum_implicit_systems))
265 {
266 _momentum_implicit_systems[system_i]->solution->close();
267 _momentum_implicit_systems[system_i]->update();
268 _momentum_systems[system_i]->setSolution(
269 *_momentum_implicit_systems[system_i]->current_local_solution);
270 }
271}
272
273void
275 const std::vector<std::unique_ptr<NumericVector<Number>>> & raw_hbya,
276 const std::vector<unsigned int> & var_nums)
277{
278 for (auto & fi : _fe_problem.mesh().faceInfo())
279 {
280 if (hasBlocks(fi->elemPtr()->subdomain_id()) ||
281 (fi->neighborPtr() && hasBlocks(fi->neighborPtr()->subdomain_id())))
282 {
283 // If we are on an internal face, we just interpolate the values to the faces.
284 // Otherwise, depending on the boundary type, we take the velocity value or
285 // extrapolated HbyA values.
286 if (_u->isInternalFace(*fi))
287 {
288 const Elem * elem = fi->elemPtr();
289 const Elem * neighbor = fi->neighborPtr();
290 for (auto comp_index : make_range(_dim))
291 {
292 unsigned int system_number = _momentum_implicit_systems[comp_index]->number();
293 const auto dof_index_elem = elem->dof_number(system_number, var_nums[comp_index], 0);
294 const auto dof_index_neighbor =
295 neighbor->dof_number(system_number, var_nums[comp_index], 0);
296
297 interpolate(Moose::FV::InterpMethod::Average,
298 _HbyA[fi->id()](comp_index),
299 (*raw_hbya[comp_index])(dof_index_elem),
300 (*raw_hbya[comp_index])(dof_index_neighbor),
301 *fi,
302 true);
303 }
304 }
305 else
306 {
307 const Elem * const boundary_elem =
308 hasBlocks(fi->elemPtr()->subdomain_id()) ? fi->elemPtr() : fi->neighborPtr();
309
310 const Moose::FaceArg boundary_face{
311 fi, Moose::FV::LimiterType::CentralDifference, true, false, boundary_elem, nullptr};
312
313 if (_u->isDirichletBoundaryFace(*fi, boundary_elem, Moose::currentState()))
314 _HbyA[fi->id()] = -MetaPhysicL::raw_value((*_vel)(boundary_face, Moose::currentState()));
315 else
316 for (const auto comp_index : make_range(_dim))
317 {
318 unsigned int system_number = _momentum_implicit_systems[comp_index]->number();
319 const auto dof_index_elem =
320 boundary_elem->dof_number(system_number, var_nums[comp_index], 0);
321 _HbyA[fi->id()](comp_index) = (*raw_hbya[comp_index])(dof_index_elem);
322 }
323 }
324 }
325 }
326}
327
328void
330{
331 if (verbose)
332 {
333 _console << "************************************" << std::endl;
334 _console << "Computing HbyA" << std::endl;
335 _console << "************************************" << std::endl;
336 }
338 "The momentum system shall be linked before calling this function!");
339
340 NonlinearImplicitSystem * momentum_system = _momentum_implicit_systems[0];
341
342 std::vector<unsigned int> var_nums = {_momentum_implicit_systems[0]->variable_number(_u->name())};
343 if (_v)
344 var_nums.push_back(_momentum_implicit_systems[1]->variable_number(_v->name()));
345 if (_w)
346 var_nums.push_back(_momentum_implicit_systems[2]->variable_number(_w->name()));
347
348 _HbyA_raw.clear();
349 for (auto system_i : index_range(_momentum_systems))
350 {
352
353 momentum_system = _momentum_implicit_systems[system_i];
354
355 NumericVector<Number> & rhs = *(momentum_system->rhs);
356 NumericVector<Number> & current_local_solution = *(momentum_system->current_local_solution);
357 NumericVector<Number> & solution = *(momentum_system->solution);
358 PetscMatrix<Number> * mmat = dynamic_cast<PetscMatrix<Number> *>(momentum_system->matrix);
359 mooseAssert(mmat,
360 "The matrices used in the segregated INSFVRhieChow objects need to be convertable "
361 "to PetscMAtrix!");
362
363 if (verbose)
364 {
365 _console << "Matrix in rc object" << std::endl;
366 mmat->print();
367 }
368
369 auto Ainv = current_local_solution.zero_clone();
370 PetscVector<Number> * Ainv_petsc = dynamic_cast<PetscVector<Number> *>(Ainv.get());
371
372 mmat->get_diagonal(*Ainv_petsc);
373
374 auto working_vector = momentum_system->current_local_solution->zero_clone();
375 PetscVector<Number> * working_vector_petsc =
376 dynamic_cast<PetscVector<Number> *>(working_vector.get());
377 mooseAssert(working_vector_petsc,
378 "The vectors used in the segregated INSFVRhieChow objects need to be convertable "
379 "to PetscVectors!");
380
381 *working_vector_petsc = 1.0;
382 Ainv_petsc->pointwise_divide(*working_vector_petsc, *Ainv_petsc);
383
384 _HbyA_raw.push_back(current_local_solution.zero_clone());
385 NumericVector<Number> & HbyA = *(_HbyA_raw.back());
386
387 if (verbose)
388 {
389 _console << "Velocity solution in H(u)" << std::endl;
390 solution.print();
391 }
392
393 // We fill the 1/A functor
394 auto active_local_begin =
395 _mesh.evaluable_elements_begin(momentum_system->get_dof_map(), var_nums[system_i]);
396 auto active_local_end =
397 _mesh.evaluable_elements_end(momentum_system->get_dof_map(), var_nums[system_i]);
398
399 const auto & state = Moose::currentState();
400 for (auto it = active_local_begin; it != active_local_end; ++it)
401 {
402 const Elem * elem = *it;
403 if (this->hasBlocks(elem->subdomain_id()))
404 {
405 const Moose::ElemArg & elem_arg = makeElemArg(elem);
406 Real coord_multiplier;
407 const auto coord_type = _fe_problem.getCoordSystem(elem->subdomain_id());
408 const unsigned int rz_radial_coord =
410
412 elem->vertex_average(), coord_multiplier, coord_type, rz_radial_coord);
413
414 const Real volume = _moose_mesh.elemInfo(elem->id()).volume();
415 const auto dof_index = elem->dof_number(momentum_system->number(), var_nums[system_i], 0);
416 _Ainv[elem->id()](system_i) = MetaPhysicL::raw_value(epsilon(_tid)(elem_arg, state)) *
417 (*Ainv_petsc)(dof_index)*volume * coord_multiplier;
418 }
419 }
420
421 // Now we set the diagonal of our system matrix to 0 so we can create H*u
422 // TODO: Add a function for this in libmesh
423 *working_vector_petsc = 0.0;
424 LibmeshPetscCall(MatDiagonalSet(mmat->mat(), working_vector_petsc->vec(), INSERT_VALUES));
425
426 if (verbose)
427 {
428 _console << "H" << std::endl;
429 mmat->print();
430 }
431
432 // We need to subtract the contribution of the pressure gradient from the residual (right hand
433 // side). We plug working_vector=0 in here to get the right hand side contribution
435
436 // Now we reorganize the association between tags and vectors to make sure
437 // that the next solve is perfect
438 _momentum_systems[system_i]->associateVectorToTag(
439 momentum_system->get_vector(_fe_problem.vectorTagName(0)), 0);
440 _momentum_systems[system_i]->associateVectorToTag(
441 momentum_system->get_vector(_fe_problem.vectorTagName(_pressure_gradient_tag)),
443 _momentum_systems[system_i]->setSolution(current_local_solution);
444
445 if (verbose)
446 {
447 _console << "total RHS" << std::endl;
448 rhs.print();
449 _console << "pressure RHS" << std::endl;
450 HbyA.print();
451 }
452
453 // We correct the right hand side to exclude the pressure contribution
454 HbyA.scale(-1.0);
455 HbyA.add(-1.0, rhs);
456
457 if (verbose)
458 {
459 _console << "H RHS" << std::endl;
460 HbyA.print();
461 }
462
463 // Create H(u)
464 mmat->vector_mult(*working_vector_petsc, solution);
465
466 if (verbose)
467 {
468 _console << " H(u)" << std::endl;
469 working_vector_petsc->print();
470 }
471
472 // Create H(u) - RHS
473 HbyA.add(*working_vector_petsc);
474
475 if (verbose)
476 {
477 _console << " H(u)-rhs-relaxation_source" << std::endl;
478 HbyA.print();
479 }
480
481 // Create 1/A*(H(u)-RHS)
482 HbyA.pointwise_mult(HbyA, *Ainv);
483
484 if (verbose)
485 {
486 _console << " (H(u)-rhs)/A" << std::endl;
487 HbyA.print();
488 }
489 }
490
491 populateHbyA(_HbyA_raw, var_nums);
492
493 if (verbose)
494 {
495 _console << "************************************" << std::endl;
496 _console << "DONE Computing HbyA " << std::endl;
497 _console << "************************************" << std::endl;
498 }
499}
registerMooseObject("NavierStokesApp", INSFVRhieChowInterpolatorSegregated)
void mooseError(Args &&... args)
unsigned int TagID
unsigned int THREAD_ID
const ExecFlagType EXEC_ALWAYS
const ExecFlagType EXEC_NONE
const std::string name
Definition Setup.h:21
bool hasBlocks(const SubdomainName &name) const
const ConsoleStream _console
Real volume() const
void addAvailableFlags(const ExecFlagType &flag, Args... flags)
void setCurrentNonlinearSystem(const unsigned int nl_sys_num)
virtual MooseMesh & mesh() override
virtual void computeResidualTag(const NumericVector< libMesh::Number > &soln, NumericVector< libMesh::Number > &residual, TagID tag)
ValueType evaluate(const FaceInfo *const fi) const
Evaluate the face functor using a FaceInfo argument.
Moose::ElemArg makeElemArg(const Elem *elem, bool correct_skewnewss=false) const
A user object which implements the Rhie Chow interpolation for segregated momentum-pressure systems.
void computeHbyA(bool verbose)
Computes the inverse of the digaonal (1/A) of the system matrix plus the H/A components for the press...
std::vector< NonlinearSystemBase * > _momentum_systems
Pointers to the nonlinear system(s) corresponding to the momentum equation(s)
VectorValue< ADReal > getVelocity(const Moose::FV::InterpMethod m, const FaceInfo &fi, const Moose::StateArg &time, const THREAD_ID tid, bool subtract_mesh_velocity) const override
Get the face velocity (used in advection terms)
INSFVRhieChowInterpolatorSegregated(const InputParameters &params)
void computeFaceVelocity()
Update the values of the face velocities in the containers.
TagID _pressure_gradient_tag
Residual tag corresponding to the pressure gradient contribution.
void populateHbyA(const std::vector< std::unique_ptr< NumericVector< Number > > > &raw_hbya, const std::vector< unsigned int > &var_nums)
Populate the face values of the H/A field.
std::vector< std::unique_ptr< NumericVector< Number > > > _HbyA_raw
We hold on to the cell-based HbyA vectors so that we can easily reconstruct the cell velocities as we...
std::vector< unsigned int > _momentum_system_numbers
Numbers of the momentum system(s)
void initFaceVelocities()
Initialize the container for face velocities.
std::unique_ptr< PiecewiseByBlockLambdaFunctor< ADRealVectorValue > > _vel
A functor for computing the (non-RC corrected) velocity.
void linkMomentumSystem(std::vector< NonlinearSystemBase * > momentum_systems, const std::vector< unsigned int > &momentum_system_numbers, const TagID pressure_gradient_tag)
Update the momentum system-related information.
FaceCenteredMapFunctor< RealVectorValue, std::unordered_map< dof_id_type, RealVectorValue > > _face_velocity
A map functor from faces to face velocities which are used in the advection terms.
CellCenteredMapFunctor< RealVectorValue, std::unordered_map< dof_id_type, RealVectorValue > > _Ainv
A map functor from element IDs to $1/A_i$.
std::vector< libMesh::NonlinearImplicitSystem * > _momentum_implicit_systems
Pointers to the momentum equation implicit system(s)
void computeCellVelocity()
Update the cell values of the velocity variables.
FaceCenteredMapFunctor< RealVectorValue, std::unordered_map< dof_id_type, RealVectorValue > > _HbyA
A map functor from faces to $HbyA_{ij} = (A_{offdiag}*\mathrm{(predicted~velocity)} - \mathrm{Source}...
const std::string & name() const
void mooseError(Args &&... args) const
const std::vector< const FaceInfo * > & faceInfo() const
const ElemInfo & elemInfo(const dof_id_type id) const
virtual bool isDirichletBoundaryFace(const FaceInfo &fi, const Elem *elem, const Moose::StateArg &state) const
bool isInternalFace(const FaceInfo &) const
GradientType gradient(const ElemArg &elem, const StateArg &state) const
INSFVPressureVariable *const _p
The thread 0 copy of the pressure variable.
INSFVVelocityVariable *const _w
The thread 0 copy of the z-velocity variable (null if the problem is not 3D)
MooseMesh & _moose_mesh
The MooseMesh that this user object operates on.
const libMesh::MeshBase & _mesh
The libMesh mesh that this object acts on.
static InputParameters validParams()
virtual const Moose::FunctorBase< ADReal > & epsilon(THREAD_ID tid) const
A virtual method that allows us to only implement getVelocity once for free and porous flows.
INSFVVelocityVariable *const _v
The thread 0 copy of the y-velocity variable (null if the problem is 1D)
INSFVVelocityVariable *const _u
The thread 0 copy of the x-velocity variable.
const unsigned int _dim
The dimension of the mesh, e.g. 3 for hexes and tets, 2 for quads and tris.
Executioner set up to solve a thermal-hydraulics problem using the SIMPLENonlinearAssembly algorithm.
virtual TagName vectorTagName(const TagID tag) const
unsigned int getAxisymmetricRadialCoord() const
Moose::CoordinateSystemType getCoordSystem(SubdomainID sid) const
void addFunctor(const std::string &name, const Moose::FunctorBase< T > &functor, const THREAD_ID tid)
SubProblem & _subproblem
FEProblemBase & _fe_problem
const THREAD_ID _tid
auto raw_value(const Eigen::Map< T > &in)
void coordTransformFactor(const P &point, C &factor, const Moose::CoordinateSystemType coord_type, const unsigned int rz_radial_coord=libMesh::invalid_uint)
StateArg currentState()
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
const unsigned int invalid_uint
IntRange< T > make_range(T beg, T end)
unsigned int n_threads()