https://mooseframework.inl.gov
Loading...
Searching...
No Matches
AdvectiveFluxCalculatorBase.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 "Conversion.h" // for stringify
12
13#include "libmesh/string_to_enum.h"
14#include "libmesh/mesh_tools.h"
15#include "libmesh/parallel_sync.h"
16
19{
22 "Base class to compute K_ij (a measure of advective flux from node i to node j) "
23 "and R+ and R- (which quantify amount of antidiffusion to add) in the "
24 "Kuzmin-Turek FEM-TVD multidimensional scheme");
25 MooseEnum flux_limiter_type("MinMod VanLeer MC superbee None", "VanLeer");
26 params.addParam<MooseEnum>("flux_limiter_type",
27 flux_limiter_type,
28 "Type of flux limiter to use. 'None' means that no antidiffusion "
29 "will be added in the Kuzmin-Turek scheme");
30 params.addRangeCheckedParam<Real>(
31 "allowable_MB_wastage",
32 5.0,
33 "allowable_MB_wastage > 0.0",
34 "This object will issue a memory warning if the internal node-numbering data structure "
35 "wastes more than allowable_MB_wastage megabytes. This data structure uses sequential "
36 "node-numbering which is optimized for speed rather than memory efficiency");
37
38 params.addRelationshipManager("ElementPointNeighborLayers",
39 Moose::RelationshipManagerType::GEOMETRIC |
40 Moose::RelationshipManagerType::ALGEBRAIC |
41 Moose::RelationshipManagerType::COUPLING,
42 [](const InputParameters &, InputParameters & rm_params)
43 { rm_params.set<unsigned short>("layers") = 2; });
44
45 params.set<ExecFlagEnum>("execute_on", true) = {EXEC_LINEAR};
46 return params;
47}
48
50 : ElementUserObject(parameters),
51 _resizing_needed(true),
52 _flux_limiter_type(getParam<MooseEnum>("flux_limiter_type").getEnum<FluxLimiterTypeEnum>()),
53 _kij(),
54 _flux_out(),
55 _dflux_out_du(),
56 _dflux_out_dKjk(),
57 _valence(),
58 _u_nodal(),
59 _u_nodal_computed_by_thread(),
60 _connections(),
61 _number_of_nodes(0),
62 _my_pid(processor_id()),
63 _nodes_to_receive(),
64 _nodes_to_send(),
65 _pairs_to_receive(),
66 _pairs_to_send(),
67 _allowable_MB_wastage(getParam<Real>("allowable_MB_wastage"))
68{
71 "execute_on",
72 "The AdvectiveFluxCalculator UserObject " + name() +
73 " execute_on parameter must include, at least, 'linear'. This is to ensure that "
74 "this UserObject computes all necessary quantities just before the Kernels evaluate "
75 "their Residuals");
76}
77
78void
80{
81 // If needed, size and initialize quantities appropriately, and compute _valence
83 {
84 /*
85 * Populate _connections for all nodes that can be seen by this processor and on relevant
86 * blocks
87 *
88 * MULTIPROC NOTE: this must loop over local elements and 2 layers of ghosted elements.
89 * The Kernel will only loop over local elements, so will only use _kij, etc, for
90 * linked node-node pairs that appear in the local elements. Nevertheless, we
91 * need to build _kij, etc, for the nodes in the ghosted elements in order to simplify
92 * Jacobian computations
93 */
95 for (const auto & elem : _fe_problem.getNonlinearEvaluableElementRange())
96 if (this->hasBlocks(elem->subdomain_id()))
97 for (unsigned i = 0; i < elem->n_nodes(); ++i)
98 _connections.addGlobalNode(elem->node_id(i));
100 for (const auto & elem : _fe_problem.getNonlinearEvaluableElementRange())
101 if (this->hasBlocks(elem->subdomain_id()))
102 for (unsigned i = 0; i < elem->n_nodes(); ++i)
103 for (unsigned j = 0; j < elem->n_nodes(); ++j)
104 _connections.addConnection(elem->node_id(i), elem->node_id(j));
106
108
109 Real mb_wasted = (_connections.sizeSequential() - _number_of_nodes) * 4.0 / 1048576;
110 gatherMax(mb_wasted);
111 if (mb_wasted > _allowable_MB_wastage)
113 "In at least one processor, the sequential node-numbering internal data structure used "
114 "in " +
115 name() + " is using memory inefficiently.\nThe memory wasted is " +
116 Moose::stringify(mb_wasted) +
117 " megabytes.\n The node-numbering data structure has been optimized for speed at the "
118 "expense of memory, but that may not be an appropriate optimization for your case, "
119 "because the node numbering is not close to sequential in your case.\n");
120
121 // initialize _kij
122 _kij.resize(_number_of_nodes);
123 for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
124 _kij[sequential_i].assign(
125 _connections.sequentialConnectionsToSequentialID(sequential_i).size(), 0.0);
126
127 /*
128 * Build _valence[i], which is the number of times the sequential node i is encountered when
129 * looping over the entire mesh (and on relevant blocks)
130 *
131 * MULTIPROC NOTE: this must loop over local elements and >=1 layer of ghosted elements.
132 * The Kernel will only loop over local elements, so will only use _valence for
133 * linked node-node pairs that appear in the local elements. But other processors will
134 * loop over neighboring elements, so avoid multiple counting of the residual and Jacobian
135 * this processor must record how many times each node-node link of its local elements
136 * appears in the local elements and >=1 layer, and pass that info to the Kernel
137 */
138 _valence.assign(_number_of_nodes, 0);
139 for (const auto & elem : _fe_problem.getNonlinearEvaluableElementRange())
140 if (this->hasBlocks(elem->subdomain_id()))
141 for (unsigned i = 0; i < elem->n_nodes(); ++i)
142 {
143 const dof_id_type node_i = elem->node_id(i);
144 const dof_id_type sequential_i = _connections.sequentialID(node_i);
145 _valence[sequential_i] += 1;
146 }
147
148 _u_nodal.assign(_number_of_nodes, 0.0);
150 _flux_out.assign(_number_of_nodes, 0.0);
151 _dflux_out_du.assign(_number_of_nodes, std::map<dof_id_type, Real>());
152 for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
153 zeroedConnection(_dflux_out_du[sequential_i], _connections.globalID(sequential_i));
155 for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
156 {
157 const std::vector<dof_id_type> con_i =
159 const std::size_t num_con_i = con_i.size();
160 _dflux_out_dKjk[sequential_i].resize(num_con_i);
161 for (std::size_t j = 0; j < con_i.size(); ++j)
162 {
163 const dof_id_type sequential_j = con_i[j];
164 const std::size_t num_con_j =
166 _dflux_out_dKjk[sequential_i][j].assign(num_con_j, 0.0);
167 }
168 }
169
170 if (_app.n_processors() > 1)
172
173 _resizing_needed = false;
174
175 // Clear and size all member vectors
176 _dij.assign(_number_of_nodes, {});
177 _dDij_dKij.assign(_number_of_nodes, {});
178 _dDij_dKji.assign(_number_of_nodes, {});
179 _dDii_dKij.assign(_number_of_nodes, {});
180 _dDii_dKji.assign(_number_of_nodes, {});
181 _lij.assign(_number_of_nodes, {});
182 _rP.assign(_number_of_nodes, 0.0);
183 _rM.assign(_number_of_nodes, 0.0);
184 _drP.assign(_number_of_nodes, {});
185 _drM.assign(_number_of_nodes, {});
186 _drP_dk.assign(_number_of_nodes, {});
187 _drM_dk.assign(_number_of_nodes, {});
188 _fa.assign(_number_of_nodes, {});
189 _dfa.assign(_number_of_nodes, {});
190 _dFij_dKik.assign(_number_of_nodes, {});
191 _dFij_dKjk.assign(_number_of_nodes, {});
192 }
193}
194
195void
197{
199
200 // Signal that _kij, _valence, etc need to be rebuilt
201 _resizing_needed = true;
202}
203
204void
206{
207 // Zero _kij and falsify _u_nodal_computed_by_thread ready for building in execute() and
208 // finalize()
210 for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
211 _kij[sequential_i].assign(_connections.sequentialConnectionsToSequentialID(sequential_i).size(),
212 0.0);
213}
214
215void
217{
218 // compute _kij contributions from this element that is local to this processor
219 // and record _u_nodal
220 for (unsigned i = 0; i < _current_elem->n_nodes(); ++i)
221 {
222 const dof_id_type node_i = _current_elem->node_id(i);
223 const dof_id_type sequential_i = _connections.sequentialID(node_i);
224 if (!_u_nodal_computed_by_thread[sequential_i])
225 {
226 _u_nodal[sequential_i] = computeU(i);
227 _u_nodal_computed_by_thread[sequential_i] = true;
228 }
229 for (unsigned j = 0; j < _current_elem->n_nodes(); ++j)
230 {
231 const dof_id_type node_j = _current_elem->node_id(j);
232 for (unsigned qp = 0; qp < _qrule->n_points(); ++qp)
233 executeOnElement(node_i, node_j, i, j, qp);
234 }
235 }
236}
237
238void
240 dof_id_type global_i, dof_id_type global_j, unsigned local_i, unsigned local_j, unsigned qp)
241{
242 // KT Eqn (20)
243 const dof_id_type sequential_i = _connections.sequentialID(global_i);
244 const unsigned index_i_to_j = _connections.indexOfGlobalConnection(global_i, global_j);
245 _kij[sequential_i][index_i_to_j] += _JxW[qp] * _coord[qp] * computeVelocity(local_i, local_j, qp);
246}
247
248void
250{
251 const auto & afc = static_cast<const AdvectiveFluxCalculatorBase &>(uo);
252 // add the values of _kij computed by different threads
253 for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
254 {
255 const std::size_t num_con_i =
257 for (std::size_t j = 0; j < num_con_i; ++j)
258 _kij[sequential_i][j] += afc._kij[sequential_i][j];
259 }
260
261 // gather the values of _u_nodal computed by different threads
262 for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
263 if (!_u_nodal_computed_by_thread[sequential_i] && afc._u_nodal_computed_by_thread[sequential_i])
264 _u_nodal[sequential_i] = afc._u_nodal[sequential_i];
265}
266
267void
269{
270 // Overall: ensure _kij is fully built, then compute Kuzmin-Turek D, L, f^a and
271 // relevant Jacobian information, and then the relevant quantities into _flux_out and
272 // _dflux_out_du, _dflux_out_dKjk
273
274 if (_app.n_processors() > 1)
276
277 // Calculate KuzminTurek D matrix
278 // See Eqn (32)
279 // This adds artificial diffusion, which eliminates any spurious oscillations
280 // The idea is that D will remove all negative off-diagonal elements when it is added to K
281 // This is identical to full upwinding
282 for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
283 {
284 const std::vector<dof_id_type> & con_i =
286 const std::size_t num_con_i = con_i.size();
287 _dij[sequential_i].assign(num_con_i, 0.0);
288 _dDij_dKij[sequential_i].assign(num_con_i, 0.0);
289 _dDij_dKji[sequential_i].assign(num_con_i, 0.0);
290 _dDii_dKij[sequential_i].assign(num_con_i, 0.0);
291 _dDii_dKji[sequential_i].assign(num_con_i, 0.0);
292 const unsigned index_i_to_i =
293 _connections.indexOfSequentialConnection(sequential_i, sequential_i);
294 for (std::size_t j = 0; j < num_con_i; ++j)
295 {
296 const dof_id_type sequential_j = con_i[j];
297 if (sequential_i == sequential_j)
298 continue;
299 const unsigned index_j_to_i =
300 _connections.indexOfSequentialConnection(sequential_j, sequential_i);
301 const Real kij = _kij[sequential_i][j];
302 const Real kji = _kij[sequential_j][index_j_to_i];
303 if ((kij <= kji) && (kij < 0.0))
304 {
305 _dij[sequential_i][j] = -kij;
306 _dDij_dKij[sequential_i][j] = -1.0;
307 _dDii_dKij[sequential_i][j] += 1.0;
308 }
309 else if ((kji <= kij) && (kji < 0.0))
310 {
311 _dij[sequential_i][j] = -kji;
312 _dDij_dKji[sequential_i][j] = -1.0;
313 _dDii_dKji[sequential_i][j] += 1.0;
314 }
315 else
316 _dij[sequential_i][j] = 0.0;
317 _dij[sequential_i][index_i_to_i] -= _dij[sequential_i][j];
318 }
319 }
320
321 // Calculate KuzminTurek L matrix
322 // See Fig 2: L = K + D
323 for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
324 {
325 const std::vector<dof_id_type> & con_i =
327 const std::size_t num_con_i = con_i.size();
328 _lij[sequential_i].assign(num_con_i, 0.0);
329 for (std::size_t j = 0; j < num_con_i; ++j)
330 _lij[sequential_i][j] = _kij[sequential_i][j] + _dij[sequential_i][j];
331 }
332
333 // Compute KuzminTurek R matrices
334 // See Eqns (49) and (12)
335 for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
336 {
337 _rP[sequential_i] = rPlus(sequential_i, _drP[sequential_i], _drP_dk[sequential_i]);
338 _rM[sequential_i] = rMinus(sequential_i, _drM[sequential_i], _drM_dk[sequential_i]);
339 }
340
341 // Calculate KuzminTurek f^{a} matrix
342 // This is the antidiffusive flux
343 // See Eqn (50)
344 for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
345 {
346 const std::vector<dof_id_type> & con_i =
348 const unsigned num_con_i = con_i.size();
349 _fa[sequential_i].assign(num_con_i, 0.0);
350 _dfa[sequential_i].resize(num_con_i);
351 _dFij_dKik[sequential_i].resize(num_con_i);
352 _dFij_dKjk[sequential_i].resize(num_con_i);
353 for (std::size_t j = 0; j < num_con_i; ++j)
354 {
355 for (const auto & global_k : con_i)
356 _dfa[sequential_i][j][global_k] = 0;
357 const dof_id_type global_j = con_i[j];
358 const std::vector<dof_id_type> & con_j = _connections.globalConnectionsToGlobalID(global_j);
359 const unsigned num_con_j = con_j.size();
360 for (const auto & global_k : con_j)
361 _dfa[sequential_i][j][global_k] = 0;
362 _dFij_dKik[sequential_i][j].assign(num_con_i, 0.0);
363 _dFij_dKjk[sequential_i][j].assign(num_con_j, 0.0);
364 }
365 }
366
367 for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
368 {
369 const dof_id_type global_i = _connections.globalID(sequential_i);
370 const Real u_i = _u_nodal[sequential_i];
371 const std::vector<dof_id_type> & con_i =
373 const std::size_t num_con_i = con_i.size();
374 for (std::size_t j = 0; j < num_con_i; ++j)
375 {
376 const dof_id_type sequential_j = con_i[j];
377 if (sequential_i == sequential_j)
378 continue;
379 const unsigned index_j_to_i =
380 _connections.indexOfSequentialConnection(sequential_j, sequential_i);
381 const Real Lij = _lij[sequential_i][j];
382 const Real Lji = _lij[sequential_j][index_j_to_i];
383 if (Lji >= Lij) // node i is upwind of node j.
384 {
385 const Real Dij = _dij[sequential_i][j];
386 const dof_id_type global_j = _connections.globalID(sequential_j);
387 const Real u_j = _u_nodal[sequential_j];
388 Real prefactor = 0.0;
389 std::vector<Real> dprefactor_du(num_con_i,
390 0.0); // dprefactor_du[j] = d(prefactor)/du[sequential_j]
391 std::vector<Real> dprefactor_dKij(
392 num_con_i, 0.0); // dprefactor_dKij[j] = d(prefactor)/dKij[sequential_i][j].
393 Real dprefactor_dKji = 0; // dprefactor_dKji = d(prefactor)/dKij[sequential_j][index_j_to_i]
394 if (u_i >= u_j)
395 {
396 if (Lji <= _rP[sequential_i] * Dij)
397 {
398 prefactor = Lji;
399 dprefactor_dKij[j] += _dDij_dKji[sequential_j][index_j_to_i];
400 dprefactor_dKji += 1.0 + _dDij_dKij[sequential_j][index_j_to_i];
401 }
402 else
403 {
404 prefactor = _rP[sequential_i] * Dij;
405 for (std::size_t ind_j = 0; ind_j < num_con_i; ++ind_j)
406 dprefactor_du[ind_j] = _drP[sequential_i][ind_j] * Dij;
407 dprefactor_dKij[j] += _rP[sequential_i] * _dDij_dKij[sequential_i][j];
408 dprefactor_dKji += _rP[sequential_i] * _dDij_dKji[sequential_i][j];
409 for (std::size_t ind_j = 0; ind_j < num_con_i; ++ind_j)
410 dprefactor_dKij[ind_j] += _drP_dk[sequential_i][ind_j] * Dij;
411 }
412 }
413 else
414 {
415 if (Lji <= _rM[sequential_i] * Dij)
416 {
417 prefactor = Lji;
418 dprefactor_dKij[j] += _dDij_dKji[sequential_j][index_j_to_i];
419 dprefactor_dKji += 1.0 + _dDij_dKij[sequential_j][index_j_to_i];
420 }
421 else
422 {
423 prefactor = _rM[sequential_i] * Dij;
424 for (std::size_t ind_j = 0; ind_j < num_con_i; ++ind_j)
425 dprefactor_du[ind_j] = _drM[sequential_i][ind_j] * Dij;
426 dprefactor_dKij[j] += _rM[sequential_i] * _dDij_dKij[sequential_i][j];
427 dprefactor_dKji += _rM[sequential_i] * _dDij_dKji[sequential_i][j];
428 for (std::size_t ind_j = 0; ind_j < num_con_i; ++ind_j)
429 dprefactor_dKij[ind_j] += _drM_dk[sequential_i][ind_j] * Dij;
430 }
431 }
432 _fa[sequential_i][j] = prefactor * (u_i - u_j);
433 _dfa[sequential_i][j][global_i] = prefactor;
434 _dfa[sequential_i][j][global_j] = -prefactor;
435 for (std::size_t ind_j = 0; ind_j < num_con_i; ++ind_j)
436 {
437 _dfa[sequential_i][j][_connections.globalID(con_i[ind_j])] +=
438 dprefactor_du[ind_j] * (u_i - u_j);
439 _dFij_dKik[sequential_i][j][ind_j] += dprefactor_dKij[ind_j] * (u_i - u_j);
440 }
441 _dFij_dKjk[sequential_i][j][index_j_to_i] += dprefactor_dKji * (u_i - u_j);
442 }
443 }
444 }
445
446 for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
447 {
448 const std::vector<dof_id_type> & con_i =
450 const std::size_t num_con_i = con_i.size();
451 for (std::size_t j = 0; j < num_con_i; ++j)
452 {
453 const dof_id_type sequential_j = con_i[j];
454 if (sequential_i == sequential_j)
455 continue;
456 const unsigned index_j_to_i =
457 _connections.indexOfSequentialConnection(sequential_j, sequential_i);
458 if (_lij[sequential_j][index_j_to_i] < _lij[sequential_i][j]) // node_i is downwind of node_j.
459 {
460 _fa[sequential_i][j] = -_fa[sequential_j][index_j_to_i];
461 for (const auto & dof_deriv : _dfa[sequential_j][index_j_to_i])
462 _dfa[sequential_i][j][dof_deriv.first] = -dof_deriv.second;
463 for (std::size_t k = 0; k < num_con_i; ++k)
464 _dFij_dKik[sequential_i][j][k] = -_dFij_dKjk[sequential_j][index_j_to_i][k];
465 const std::size_t num_con_j =
467 for (std::size_t k = 0; k < num_con_j; ++k)
468 _dFij_dKjk[sequential_i][j][k] = -_dFij_dKik[sequential_j][index_j_to_i][k];
469 }
470 }
471 }
472
473 // zero _flux_out and its derivatives
474 _flux_out.assign(_number_of_nodes, 0.0);
475 // The derivatives are a bit complicated.
476 // If i is upwind of a node "j" then _flux_out[i] depends on all nodes connected to i.
477 // But if i is downwind of a node "j" then _flux_out depends on all nodes connected with node
478 // j.
479 _dflux_out_du.assign(_number_of_nodes, std::map<dof_id_type, Real>());
480 for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
481 for (const auto & j : _connections.globalConnectionsToSequentialID(sequential_i))
482 {
483 _dflux_out_du[sequential_i][j] = 0.0;
484 for (const auto & neighbors_j : _connections.globalConnectionsToGlobalID(j))
485 _dflux_out_du[sequential_i][neighbors_j] = 0.0;
486 }
488 for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
489 {
490 const std::vector<dof_id_type> & con_i =
492 const std::size_t num_con_i = con_i.size();
493 _dflux_out_dKjk[sequential_i].resize(num_con_i);
494 for (std::size_t j = 0; j < num_con_i; ++j)
495 {
496 const dof_id_type sequential_j = con_i[j];
497 const std::vector<dof_id_type> & con_j =
499 _dflux_out_dKjk[sequential_i][j].assign(con_j.size(), 0.0);
500 }
501 }
502
503 // Add everything together
504 // See step 3 in Fig 2, noting Eqn (36)
505 for (dof_id_type sequential_i = 0; sequential_i < _number_of_nodes; ++sequential_i)
506 {
507 const std::vector<dof_id_type> & con_i =
509 const size_t num_con_i = con_i.size();
510 const dof_id_type index_i_to_i =
511 _connections.indexOfSequentialConnection(sequential_i, sequential_i);
512 for (std::size_t j = 0; j < num_con_i; ++j)
513 {
514 const dof_id_type sequential_j = con_i[j];
515 const dof_id_type global_j = _connections.globalID(sequential_j);
516 const Real u_j = _u_nodal[sequential_j];
517
518 // negative sign because residual = -Lu (KT equation (19))
519 _flux_out[sequential_i] -= _lij[sequential_i][j] * u_j + _fa[sequential_i][j];
520
521 _dflux_out_du[sequential_i][global_j] -= _lij[sequential_i][j];
522 for (const auto & dof_deriv : _dfa[sequential_i][j])
523 _dflux_out_du[sequential_i][dof_deriv.first] -= dof_deriv.second;
524
525 _dflux_out_dKjk[sequential_i][index_i_to_i][j] -= 1.0 * u_j; // from the K in L = K + D
526
527 if (sequential_j == sequential_i)
528 for (dof_id_type k = 0; k < num_con_i; ++k)
529 _dflux_out_dKjk[sequential_i][index_i_to_i][k] -= _dDii_dKij[sequential_i][k] * u_j;
530 else
531 _dflux_out_dKjk[sequential_i][index_i_to_i][j] -= _dDij_dKij[sequential_i][j] * u_j;
532 for (dof_id_type k = 0; k < num_con_i; ++k)
533 _dflux_out_dKjk[sequential_i][index_i_to_i][k] -= _dFij_dKik[sequential_i][j][k];
534
535 if (sequential_j == sequential_i)
536 for (unsigned k = 0; k < con_i.size(); ++k)
537 {
538 const unsigned index_k_to_i =
539 _connections.indexOfSequentialConnection(con_i[k], sequential_i);
540 _dflux_out_dKjk[sequential_i][k][index_k_to_i] -= _dDii_dKji[sequential_i][k] * u_j;
541 }
542 else
543 {
544 const unsigned index_j_to_i =
545 _connections.indexOfSequentialConnection(sequential_j, sequential_i);
546 _dflux_out_dKjk[sequential_i][j][index_j_to_i] -= _dDij_dKji[sequential_i][j] * u_j;
547 }
548 for (unsigned k = 0;
549 k < _connections.sequentialConnectionsToSequentialID(sequential_j).size();
550 ++k)
551 _dflux_out_dKjk[sequential_i][j][k] -= _dFij_dKjk[sequential_i][j][k];
552 }
553 }
554}
555
556Real
557AdvectiveFluxCalculatorBase::rPlus(dof_id_type sequential_i,
558 std::vector<Real> & dlimited_du,
559 std::vector<Real> & dlimited_dk) const
560{
561 const std::size_t num_con = _connections.sequentialConnectionsToSequentialID(sequential_i).size();
562 dlimited_du.assign(num_con, 0.0);
563 dlimited_dk.assign(num_con, 0.0);
565 return 0.0;
566 std::vector<Real> dp_du;
567 std::vector<Real> dp_dk;
568 const Real p = PQPlusMinus(sequential_i, PQPlusMinusEnum::PPlus, dp_du, dp_dk);
569 if (p == 0.0)
570 // Comment after Eqn (49): if P=0 then there's no antidiffusion, so no need to remove it
571 return 1.0;
572 std::vector<Real> dq_du;
573 std::vector<Real> dq_dk;
574 const Real q = PQPlusMinus(sequential_i, PQPlusMinusEnum::QPlus, dq_du, dq_dk);
575
576 const Real r = q / p;
577 Real limited;
578 Real dlimited_dr;
579 limitFlux(1.0, r, limited, dlimited_dr);
580
581 const Real p2 = std::pow(p, 2);
582 for (std::size_t j = 0; j < num_con; ++j)
583 {
584 const Real dr_du = dq_du[j] / p - q * dp_du[j] / p2;
585 const Real dr_dk = dq_dk[j] / p - q * dp_dk[j] / p2;
586 dlimited_du[j] = dlimited_dr * dr_du;
587 dlimited_dk[j] = dlimited_dr * dr_dk;
588 }
589 return limited;
590}
591
592Real
593AdvectiveFluxCalculatorBase::rMinus(dof_id_type sequential_i,
594 std::vector<Real> & dlimited_du,
595 std::vector<Real> & dlimited_dk) const
596{
597 const std::size_t num_con = _connections.sequentialConnectionsToSequentialID(sequential_i).size();
598 dlimited_du.assign(num_con, 0.0);
599 dlimited_dk.assign(num_con, 0.0);
601 return 0.0;
602 std::vector<Real> dp_du;
603 std::vector<Real> dp_dk;
604 const Real p = PQPlusMinus(sequential_i, PQPlusMinusEnum::PMinus, dp_du, dp_dk);
605 if (p == 0.0)
606 // Comment after Eqn (49): if P=0 then there's no antidiffusion, so no need to remove it
607 return 1.0;
608 std::vector<Real> dq_du;
609 std::vector<Real> dq_dk;
610 const Real q = PQPlusMinus(sequential_i, PQPlusMinusEnum::QMinus, dq_du, dq_dk);
611
612 const Real r = q / p;
613 Real limited;
614 Real dlimited_dr;
615 limitFlux(1.0, r, limited, dlimited_dr);
616
617 const Real p2 = std::pow(p, 2);
618 for (std::size_t j = 0; j < num_con; ++j)
619 {
620 const Real dr_du = dq_du[j] / p - q * dp_du[j] / p2;
621 const Real dr_dk = dq_dk[j] / p - q * dp_dk[j] / p2;
622 dlimited_du[j] = dlimited_dr * dr_du;
623 dlimited_dk[j] = dlimited_dr * dr_dk;
624 }
625 return limited;
626}
627
628void
629AdvectiveFluxCalculatorBase::limitFlux(Real a, Real b, Real & limited, Real & dlimited_db) const
630{
631 limited = 0.0;
632 dlimited_db = 0.0;
634 return;
635
636 if ((a >= 0.0 && b <= 0.0) || (a <= 0.0 && b >= 0.0))
637 return;
638 const Real s = (a > 0.0 ? 1.0 : -1.0);
639
640 const Real lal = std::abs(a);
641 const Real lbl = std::abs(b);
642 const Real dlbl = (b >= 0.0 ? 1.0 : -1.0); // d(lbl)/db
643 switch (_flux_limiter_type)
644 {
646 {
647 if (lal <= lbl)
648 {
649 limited = s * lal;
650 dlimited_db = 0.0;
651 }
652 else
653 {
654 limited = s * lbl;
655 dlimited_db = s * dlbl;
656 }
657 return;
658 }
660 {
661 limited = s * 2 * lal * lbl / (lal + lbl);
662 dlimited_db = s * 2 * lal * (dlbl / (lal + lbl) - lbl * dlbl / std::pow(lal + lbl, 2));
663 return;
664 }
666 {
667 const Real av = 0.5 * std::abs(a + b);
668 if (2 * lal <= av && lal <= lbl)
669 {
670 // 2 * lal is the smallest
671 limited = s * 2.0 * lal;
672 dlimited_db = 0.0;
673 }
674 else if (2 * lbl <= av && lbl <= lal)
675 {
676 // 2 * lbl is the smallest
677 limited = s * 2.0 * lbl;
678 dlimited_db = s * 2.0 * dlbl;
679 }
680 else
681 {
682 // av is the smallest
683 limited = s * av;
684 // if (a>0 and b>0) then d(av)/db = 0.5 = 0.5 * dlbl
685 // if (a<0 and b<0) then d(av)/db = -0.5 = 0.5 * dlbl
686 // if a and b have different sign then limited=0, above
687 dlimited_db = s * 0.5 * dlbl;
688 }
689 return;
690 }
692 {
693 const Real term1 = std::min(2.0 * lal, lbl);
694 const Real term2 = std::min(lal, 2.0 * lbl);
695 if (term1 >= term2)
696 {
697 if (2.0 * lal <= lbl)
698 {
699 limited = s * 2 * lal;
700 dlimited_db = 0.0;
701 }
702 else
703 {
704 limited = s * lbl;
705 dlimited_db = s * dlbl;
706 }
707 }
708 else
709 {
710 if (lal <= 2.0 * lbl)
711 {
712 limited = s * lal;
713 dlimited_db = 0.0;
714 }
715 else
716 {
717 limited = s * 2.0 * lbl;
718 dlimited_db = s * 2.0 * dlbl;
719 }
720 }
721 return;
722 }
723 default:
724 return;
725 }
726}
727
728const std::map<dof_id_type, Real> &
730{
732}
733
734const std::vector<std::vector<Real>> &
736{
738}
739
740Real
742{
743 return _flux_out[_connections.sequentialID(node_i)];
744}
745
746unsigned
748{
749 return _valence[_connections.sequentialID(node_i)];
750}
751
752void
753AdvectiveFluxCalculatorBase::zeroedConnection(std::map<dof_id_type, Real> & the_map,
754 dof_id_type node_i) const
755{
756 the_map.clear();
757 for (const auto & node_j : _connections.globalConnectionsToGlobalID(node_i))
758 the_map[node_j] = 0.0;
759}
760
761Real
763 const PQPlusMinusEnum pq_plus_minus,
764 std::vector<Real> & derivs,
765 std::vector<Real> & dpqdk) const
766{
767 // Find the value of u at sequential_i
768 const Real u_i = _u_nodal[sequential_i];
769
770 // Connections to sequential_i
771 const std::vector<dof_id_type> con_i =
773 const std::size_t num_con = con_i.size();
774 // The neighbor number of sequential_i to sequential_i
775 const unsigned i_index_i = _connections.indexOfSequentialConnection(sequential_i, sequential_i);
776
777 // Initialize the results
778 Real result = 0.0;
779 derivs.assign(num_con, 0.0);
780 dpqdk.assign(num_con, 0.0);
781
782 // Sum over all nodes connected with node_i.
783 for (std::size_t j = 0; j < num_con; ++j)
784 {
785 const dof_id_type sequential_j = con_i[j];
786 if (sequential_j == sequential_i)
787 continue;
788 const Real kentry = _kij[sequential_i][j];
789
790 // Find the value of u at node_j
791 const Real u_j = _u_nodal[sequential_j];
792 const Real ujminusi = u_j - u_i;
793
794 // Evaluate the i-j contribution to the result
795 switch (pq_plus_minus)
796 {
798 {
799 if (ujminusi < 0.0 && kentry < 0.0)
800 {
801 result += kentry * ujminusi;
802 derivs[j] += kentry;
803 derivs[i_index_i] -= kentry;
804 dpqdk[j] += ujminusi;
805 }
806 break;
807 }
809 {
810 if (ujminusi > 0.0 && kentry < 0.0)
811 {
812 result += kentry * ujminusi;
813 derivs[j] += kentry;
814 derivs[i_index_i] -= kentry;
815 dpqdk[j] += ujminusi;
816 }
817 break;
818 }
820 {
821 if (ujminusi > 0.0 && kentry > 0.0)
822 {
823 result += kentry * ujminusi;
824 derivs[j] += kentry;
825 derivs[i_index_i] -= kentry;
826 dpqdk[j] += ujminusi;
827 }
828 break;
829 }
831 {
832 if (ujminusi < 0.0 && kentry > 0.0)
833 {
834 result += kentry * ujminusi;
835 derivs[j] += kentry;
836 derivs[i_index_i] -= kentry;
837 dpqdk[j] += ujminusi;
838 }
839 break;
840 }
841 }
842 }
843
844 return result;
845}
846
847void
849{
863 _nodes_to_receive.clear();
864 for (const auto & elem : _fe_problem.getNonlinearEvaluableElementRange())
865 if (this->hasBlocks(elem->subdomain_id()))
866 {
867 const processor_id_type elem_pid = elem->processor_id();
868 if (elem_pid != _my_pid)
869 {
870 if (_nodes_to_receive.find(elem_pid) == _nodes_to_receive.end())
871 _nodes_to_receive[elem_pid] = std::vector<dof_id_type>();
872 for (unsigned i = 0; i < elem->n_nodes(); ++i)
873 if (std::find(_nodes_to_receive[elem_pid].begin(),
874 _nodes_to_receive[elem_pid].end(),
875 elem->node_id(i)) == _nodes_to_receive[elem_pid].end())
876 _nodes_to_receive[elem_pid].push_back(elem->node_id(i));
877 }
878 }
879
880 // exchange this info with other processors, building _nodes_to_send at the same time
881 _nodes_to_send.clear();
882 auto nodes_action_functor = [this](processor_id_type pid, const std::vector<dof_id_type> & nts)
883 { _nodes_to_send[pid] = nts; };
884 Parallel::push_parallel_vector_data(this->comm(), _nodes_to_receive, nodes_action_functor);
885
886 // At the moment, _nodes_to_send and _nodes_to_receive contain global node numbers
887 // It is slightly more efficient to convert to sequential node numbers
888 // so that we don't have to keep doing things like
889 // _u_nodal[_connections.sequentialID(_nodes_to_send[pid][i])
890 // every time we send/receive
891 for (auto & kv : _nodes_to_send)
892 {
893 const processor_id_type pid = kv.first;
894 const std::size_t num_nodes = kv.second.size();
895 for (unsigned i = 0; i < num_nodes; ++i)
897 }
898 for (auto & kv : _nodes_to_receive)
899 {
900 const processor_id_type pid = kv.first;
901 const std::size_t num_nodes = kv.second.size();
902 for (unsigned i = 0; i < num_nodes; ++i)
904 }
905
906 // Build pairs_to_receive
907 _pairs_to_receive.clear();
908 for (const auto & elem : _fe_problem.getNonlinearEvaluableElementRange())
909 if (this->hasBlocks(elem->subdomain_id()))
910 {
911 const processor_id_type elem_pid = elem->processor_id();
912 if (elem_pid != _my_pid)
913 {
914 if (_pairs_to_receive.find(elem_pid) == _pairs_to_receive.end())
915 _pairs_to_receive[elem_pid] = std::vector<std::pair<dof_id_type, dof_id_type>>();
916 for (unsigned i = 0; i < elem->n_nodes(); ++i)
917 for (unsigned j = 0; j < elem->n_nodes(); ++j)
918 {
919 std::pair<dof_id_type, dof_id_type> the_pair(elem->node_id(i), elem->node_id(j));
920 if (std::find(_pairs_to_receive[elem_pid].begin(),
921 _pairs_to_receive[elem_pid].end(),
922 the_pair) == _pairs_to_receive[elem_pid].end())
923 _pairs_to_receive[elem_pid].push_back(the_pair);
924 }
925 }
926 }
927
928 _pairs_to_send.clear();
929 auto pairs_action_functor =
930 [this](processor_id_type pid, const std::vector<std::pair<dof_id_type, dof_id_type>> & pts)
931 { _pairs_to_send[pid] = pts; };
932 Parallel::push_parallel_vector_data(this->comm(), _pairs_to_receive, pairs_action_functor);
933
934 // _pairs_to_send and _pairs_to_receive have been built using global node IDs
935 // since all processors know about that. However, using global IDs means
936 // that every time we send/receive, we keep having to do things like
937 // _kij[_connections.sequentialID(_pairs_to_send[pid][i].first)][_connections.indexOfGlobalConnection(_pairs_to_send[pid][i].first,
938 // _pairs_to_send[pid][i].second)] which is quite inefficient. So:
939 for (auto & kv : _pairs_to_send)
940 {
941 const processor_id_type pid = kv.first;
942 const std::size_t num_pairs = kv.second.size();
943 for (unsigned i = 0; i < num_pairs; ++i)
944 {
946 _pairs_to_send[pid][i].first, _pairs_to_send[pid][i].second);
947 _pairs_to_send[pid][i].first = _connections.sequentialID(_pairs_to_send[pid][i].first);
948 }
949 }
950 for (auto & kv : _pairs_to_receive)
951 {
952 const processor_id_type pid = kv.first;
953 const std::size_t num_pairs = kv.second.size();
954 for (unsigned i = 0; i < num_pairs; ++i)
955 {
957 _pairs_to_receive[pid][i].first, _pairs_to_receive[pid][i].second);
958 _pairs_to_receive[pid][i].first = _connections.sequentialID(_pairs_to_receive[pid][i].first);
959 }
960 }
961}
962
963void
965{
966 // Exchange ghosted _u_nodal information with other processors
967 std::map<processor_id_type, std::vector<Real>> unodal_to_send;
968 for (const auto & kv : _nodes_to_send)
969 {
970 const processor_id_type pid = kv.first;
971 unodal_to_send[pid] = std::vector<Real>();
972 for (const auto & nd : kv.second)
973 unodal_to_send[pid].push_back(_u_nodal[nd]);
974 }
975
976 auto unodal_action_functor =
977 [this](processor_id_type pid, const std::vector<Real> & unodal_received)
978 {
979 const std::size_t msg_size = unodal_received.size();
980 mooseAssert(msg_size == _nodes_to_receive[pid].size(),
981 "Message size, " << msg_size
982 << ", incompatible with nodes_to_receive, which has size "
983 << _nodes_to_receive[pid].size());
984 for (unsigned i = 0; i < msg_size; ++i)
985 _u_nodal[_nodes_to_receive[pid][i]] = unodal_received[i];
986 };
987 Parallel::push_parallel_vector_data(this->comm(), unodal_to_send, unodal_action_functor);
988
989 // Exchange _kij information with other processors
990 std::map<processor_id_type, std::vector<Real>> kij_to_send;
991 for (const auto & kv : _pairs_to_send)
992 {
993 const processor_id_type pid = kv.first;
994 kij_to_send[pid] = std::vector<Real>();
995 for (const auto & pr : kv.second)
996 kij_to_send[pid].push_back(_kij[pr.first][pr.second]);
997 }
998
999 auto kij_action_functor = [this](processor_id_type pid, const std::vector<Real> & kij_received)
1000 {
1001 const std::size_t msg_size = kij_received.size();
1002 mooseAssert(msg_size == _pairs_to_receive[pid].size(),
1003 "Message size, " << msg_size
1004 << ", incompatible with pairs_to_receive, which has size "
1005 << _pairs_to_receive[pid].size());
1006 for (unsigned i = 0; i < msg_size; ++i)
1007 _kij[_pairs_to_receive[pid][i].first][_pairs_to_receive[pid][i].second] += kij_received[i];
1008 };
1009 Parallel::push_parallel_vector_data(this->comm(), kij_to_send, kij_action_functor);
1010}
const Real p
const ExecFlagType EXEC_LINEAR
Base class to compute Advective fluxes.
PQPlusMinusEnum
Signals to the PQPlusMinus method what should be computed.
std::vector< std::vector< Real > > _drM
drM[i][j] = d(rM[i])/d(u[j]). Here j indexes the j^th node connected to i
std::vector< std::vector< Real > > _fa
fa[sequential_i][j] sequential_j is the j^th connection to sequential_i
FluxLimiterTypeEnum
Determines Flux Limiter type (Page 135 of Kuzmin and Turek) "None" means that limitFlux=0 always,...
AdvectiveFluxCalculatorBase(const InputParameters &parameters)
Real rPlus(dof_id_type sequential_i, std::vector< Real > &dlimited_du, std::vector< Real > &dlimited_dk) const
Returns the value of R_{i}^{+}, Eqn (49) of KT.
std::vector< Real > _u_nodal
_u_nodal[i] = value of _u at sequential node number i
std::vector< std::vector< Real > > _dDij_dKji
dDij_dKji[i][j] = d(D[i][j])/d(K[j][i]) for i!=j
std::vector< std::vector< Real > > _kij
Kuzmin-Turek K_ij matrix.
virtual void executeOnElement(dof_id_type global_i, dof_id_type global_j, unsigned local_i, unsigned local_j, unsigned qp)
This is called by multiple times in execute() in a double loop over _current_elem's nodes (local_i an...
std::vector< std::vector< std::vector< Real > > > _dFij_dKik
dFij_dKik[sequential_i][j][k] = d(fa[sequential_i][j])/d(K[sequential_i][k]).
std::vector< std::vector< Real > > _lij
virtual void buildCommLists()
When using multiple processors, other processors will compute:
std::map< processor_id_type, std::vector< std::pair< dof_id_type, dof_id_type > > > _pairs_to_send
_pairs_to_send[proc_id] indicates the k(i, j) pairs that we will send to proc_id _pairs_to_send is fi...
std::vector< std::vector< Real > > _dDii_dKij
dDii_dKij[i][j] = d(D[i][i])/d(K[i][j])
virtual void threadJoin(const UserObject &uo) override
PorousFlowConnectedNodes _connections
Holds the sequential and global nodal IDs, and info regarding mesh connections between them.
std::map< processor_id_type, std::vector< dof_id_type > > _nodes_to_receive
_nodes_to_receive[proc_id] = list of sequential nodal IDs.
std::vector< std::vector< Real > > _dij
Vectors used in finalize()
virtual Real computeVelocity(unsigned i, unsigned j, unsigned qp) const =0
Computes the transfer velocity between current node i and current node j at the current qp in the cur...
void zeroedConnection(std::map< dof_id_type, Real > &the_map, dof_id_type node_i) const
Clears the_map, then, using _kij, constructs the_map so that the_map[node_id] = 0....
std::vector< std::map< dof_id_type, Real > > _dflux_out_du
_dflux_out_du[i][j] = d(flux_out[i])/d(u[j]).
std::vector< std::vector< Real > > _drP_dk
drP_dk[i][j] = d(rP[i])/d(K[i][j]). Here j indexes the j^th node connected to i
std::vector< std::vector< std::vector< Real > > > _dflux_out_dKjk
_dflux_out_dKjk[sequential_i][j][k] = d(flux_out[sequential_i])/d(K[j][k]).
Real rMinus(dof_id_type sequential_i, std::vector< Real > &dlimited_du, std::vector< Real > &dlimited_dk) const
Returns the value of R_{i}^{-}, Eqn (49) of KT.
Real getFluxOut(dof_id_type node_i) const
Returns the flux out of lobal node id.
std::vector< std::vector< Real > > _drM_dk
drM_dk[i][j] = d(rM[i])/d(K[i][j]). Here j indexes the j^th node connected to i
std::map< processor_id_type, std::vector< std::pair< dof_id_type, dof_id_type > > > _pairs_to_receive
_pairs_to_receive[proc_id] indicates the k(i, j) pairs that will be sent to us from proc_id _pairs_to...
std::vector< std::vector< Real > > _dDii_dKji
dDii_dKji[i][j] = d(D[i][i])/d(K[j][i])
virtual Real computeU(unsigned i) const =0
Computes the value of u at the local node id of the current element (_current_elem)
std::vector< std::vector< std::vector< Real > > > _dFij_dKjk
dFij_dKjk[sequential_i][j][k] = d(fa[sequential_i][j])/d(K[sequential_j][k]).
enum AdvectiveFluxCalculatorBase::FluxLimiterTypeEnum _flux_limiter_type
std::vector< bool > _u_nodal_computed_by_thread
_u_nodal_computed_by_thread(i) = true if _u_nodal[i] has been computed in execute() by the thread on ...
const std::map< dof_id_type, Real > & getdFluxOutdu(dof_id_type node_i) const
Returns r where r[j] = d(flux out of global node i)/du(global node j) used in Jacobian computations.
std::vector< std::vector< Real > > _dDij_dKij
dDij_dKij[i][j] = d(D[i][j])/d(K[i][j]) for i!=j
std::vector< unsigned > _valence
_valence[i] = number of times, in a loop over elements seen by this processor (viz,...
processor_id_type _my_pid
processor ID of this object
std::size_t _number_of_nodes
Number of nodes held by the _connections object.
const Real _allowable_MB_wastage
A mooseWarning is issued if mb_wasted = (_connections.sizeSequential() - _connections....
std::map< processor_id_type, std::vector< dof_id_type > > _nodes_to_send
_nodes_to_send[proc_id] = list of sequential nodal IDs.
Real PQPlusMinus(dof_id_type sequential_i, const PQPlusMinusEnum pq_plus_minus, std::vector< Real > &derivs, std::vector< Real > &dpq_dk) const
Returns the value of P_{i}^{+}, P_{i}^{-}, Q_{i}^{+} or Q_{i}^{-} (depending on pq_plus_minus) which ...
const std::vector< std::vector< Real > > & getdFluxOutdKjk(dof_id_type node_i) const
Returns r where r[j][k] = d(flux out of global node i)/dK[connected node j][connected node k] used in...
virtual void exchangeGhostedInfo()
Sends and receives multi-processor information regarding u_nodal and k_ij.
void limitFlux(Real a, Real b, Real &limited, Real &dlimited_db) const
flux limiter, L, on Page 135 of Kuzmin and Turek
bool _resizing_needed
whether _kij, etc, need to be sized appropriately (and valence recomputed) at the start of the timest...
std::vector< std::vector< Real > > _drP
drP[i][j] = d(rP[i])/d(u[j]). Here j indexes the j^th node connected to i
std::vector< std::vector< std::map< dof_id_type, Real > > > _dfa
dfa[sequential_i][j][global_k] = d(fa[sequential_i][j])/du[global_k].
unsigned getValence(dof_id_type node_i) const
Returns the valence of the global node i Valence is the number of times the node is encountered in a ...
std::vector< Real > _flux_out
_flux_out[i] = flux of "heat" from sequential node i
bool hasBlocks(const SubdomainName &name) const
static InputParameters validParams()
const QBase *const & _qrule
const Elem *const & _current_elem
const MooseArray< Real > & _coord
const MooseArray< Real > & _JxW
const libMesh::ConstElemRange & getNonlinearEvaluableElementRange()
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
void addRelationshipManager(const std::string &name, Moose::RelationshipManagerType rm_type, Moose::RelationshipManagerInputParameterCallback input_parameter_callback=nullptr)
void addClassDescription(const std::string &doc_string)
T & set(const std::string &name, bool quiet_mode=false)
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
virtual void meshChanged()
const std::string & name() const
void paramError(const std::string &param, Args... args) const
void mooseWarning(Args &&... args) const
bool isValueSet(const std::string &value) const
void finalizeAddingConnections()
Signal that all global node IDs have been added to the internal data structures.
const std::vector< dof_id_type > & sequentialConnectionsToSequentialID(dof_id_type sequential_node_ID) const
Return all the nodes (sequential node IDs) connected to the given sequential node ID All elements of ...
void addConnection(dof_id_type global_node_from, dof_id_type global_node_to)
Specifies that global_node_to is connected to global_node_from.
dof_id_type globalID(dof_id_type sequential_node_ID) const
Return the global node ID (node number in the mesh) corresponding to the provided sequential node ID.
std::size_t numNodes() const
number of nodes known by this class
unsigned indexOfSequentialConnection(dof_id_type sequential_node_ID_from, dof_id_type sequential_node_ID_to) const
Return the index of sequential_node_ID_to in the sequentialConnectionsToSequentialID(sequential_node_...
const std::vector< dof_id_type > & globalConnectionsToSequentialID(dof_id_type sequential_node_ID) const
Return all the nodes (global node IDs) connected to the given sequential node ID.
void finalizeAddingGlobalNodes()
Signal that all global node IDs have been added to the internal data structures.
void clear()
clear all data in readiness for adding global nodes and connections
void addGlobalNode(dof_id_type global_node_ID)
Add the given global_node_ID to the internal data structures If the global node ID has already been a...
unsigned indexOfGlobalConnection(dof_id_type global_node_ID_from, dof_id_type global_node_ID_to) const
Return the index of global_node_ID_to in the globalConnectionsToGlobalID(global_node_ID_from) vector.
std::size_t sizeSequential() const
Return the size of _sequential_id, for checking memory efficiency.
dof_id_type sequentialID(dof_id_type global_node_ID) const
Return the sequential node ID corresponding to the global node ID This is guaranteed to lie in the ra...
const std::vector< dof_id_type > & globalConnectionsToGlobalID(dof_id_type global_node_ID) const
Return all the nodes (global node IDs) connected to the given global node ID.
const ExecFlagEnum & _execute_enum
void gatherMax(T &value)
FEProblemBase & _fe_problem
const Parallel::Communicator & comm() const
processor_id_type n_processors() const
std::string stringify(const T &t)