https://mooseframework.inl.gov
InitialConditionTempl.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 
10 #include "InitialConditionTempl.h"
11 #include "FEProblem.h"
12 #include "Assembly.h"
13 #include "MooseVariableFE.h"
14 #include "SystemBase.h"
15 
16 #include "libmesh/fe_interface.h"
17 #include "libmesh/quadrature.h"
18 
19 using namespace libMesh;
20 
21 template <typename T>
23  : InitialConditionBase(parameters),
24  _fe_problem(*getCheckedPointerParam<FEProblemBase *>("_fe_problem_base")),
25  _tid(getParam<THREAD_ID>("_tid")),
26  _t(_fe_problem.time()),
27  _var(_sys.getActualFieldVariable<T>(parameters.get<THREAD_ID>("_tid"),
28  parameters.get<VariableName>("variable"))),
29  _fe_var(dynamic_cast<MooseVariableFE<T> *>(&_var)),
30  _assembly(
31  _fe_problem.assembly(_tid, _var.kind() == Moose::VAR_SOLVER ? _var.sys().number() : 0)),
32  _coord_sys(_assembly.coordSystem()),
33  _current_elem(_var.currentElem()),
34  _current_elem_volume(_assembly.elemVolume()),
35  _current_node(nullptr),
36  _qp(0),
37  _fe_type(_var.feType()),
38  _dof_indices(_var.dofIndices())
39 {
40 }
41 
42 template <typename T>
44 {
45 }
46 
47 template <typename T>
48 void
50 {
51  // -- NOTE ----
52  // The following code is a copy from libMesh project_vector.C plus it adds some features, so we
53  // can couple variable values
54  // and we also do not call any callbacks, but we use our initial condition system directly.
55  // Eventually we should try to fix things so we're not duplicating code
56  // ------------
57 
58  // The dimension of the current element
59  _dim = _current_elem->dim();
60  // The number of nodes on the new element
61  const unsigned int n_nodes = _current_elem->n_nodes();
62 
63  // Get FE objects of the appropriate type
64  // We cannot use the FE object in Assembly, since the following code is messing with the
65  // quadrature rules
66  // for projections and would screw it up. However, if we implement projections from one mesh to
67  // another,
68  // this code should use that implementation.
69  std::unique_ptr<FEBaseType> fe(FEBaseType::build(_dim, _fe_type));
70 
71  // Prepare variables for projection
72  std::unique_ptr<QBase> qrule(_fe_type.default_quadrature_rule(_dim));
73  std::unique_ptr<QBase> qedgerule(_fe_type.default_quadrature_rule(1));
74  std::unique_ptr<QBase> qsiderule(_fe_type.default_quadrature_rule(_dim - 1));
75 
76  // The values of the shape functions at the quadrature points
77  _phi = &fe->get_phi();
78 
79  // The gradients of the shape functions at the quadrature points on the child element.
80  _dphi = nullptr;
81 
82  _cont = fe->get_continuity();
83 
84  if (_cont == C_ONE)
85  {
86  const std::vector<std::vector<GradientShapeType>> & ref_dphi = fe->get_dphi();
87  _dphi = &ref_dphi;
88  }
89 
90  // The Jacobian * quadrature weight at the quadrature points
91  _JxW = &fe->get_JxW();
92  // The XYZ locations of the quadrature points
93  _xyz_values = &fe->get_xyz();
94 
95  // Update the DOF indices for this element based on the current mesh
96  _var.prepareIC();
97 
98  // The number of DOFs on the element for this finite element type
99  const unsigned int n_dofs = _dof_indices.size() / _var.count();
100  mooseAssert(_dof_indices.size() % _var.count() == 0,
101  "The number of degrees of freedom should be cleanly divisible by the variable count");
102 
103  if (n_dofs == 0)
104  return;
105 
106  // Fixed vs. free DoFs on edge/face projections
107  _dof_is_fixed.clear();
108  _dof_is_fixed.resize(n_dofs, false);
109  _free_dof.clear();
110  _free_dof.resize(n_dofs, 0);
111 
112  // Zero the interpolated values
113  _Ue.resize(n_dofs);
114  _Ue.zero();
115 
116  DenseVector<char> mask(n_dofs, true);
117 
118  // In general, we need a series of
119  // projections to ensure a unique and continuous
120  // solution. We start by interpolating nodes, then
121  // hold those fixed and project edges, then
122  // hold those fixed and project faces, then
123  // hold those fixed and project interiors
124 
125  // Interpolate node values first
126  _current_dof = 0;
127 
128  for (_n = 0; _n != n_nodes; ++_n)
129  {
130  _nc = FEInterface::n_dofs_at_node(_fe_type, _current_elem, _n);
131 
132  // for nodes that are in more than one subdomain, only compute the initial
133  // condition once on the lowest numbered block
134  auto curr_node = _current_elem->node_ptr(_n);
135  const auto & block_ids = _sys.mesh().getNodeBlockIds(*curr_node);
136 
137  auto priority_block = *(block_ids.begin());
138  for (auto id : block_ids)
139  if (_var.hasBlocks(id))
140  {
141  priority_block = id;
142  break;
143  }
144 
145  if (!hasBlocks(priority_block) && _var.isNodal())
146  {
147  for (decltype(_nc) i = 0; i < _nc; ++i)
148  {
149  mask(_current_dof) = false;
150  _current_dof++;
151  }
152  continue;
153  }
154 
155  if (!_current_elem->is_vertex(_n))
156  {
157  _current_dof += _nc;
158  continue;
159  }
160 
161  if (_cont == DISCONTINUOUS || _cont == H_CURL || _cont == H_DIV)
162  libmesh_assert(_nc == 0);
163  else if (_cont == C_ZERO)
164  setCZeroVertices();
165  else if (_fe_type.family == HERMITE)
166  setHermiteVertices();
167  else if (_cont == C_ONE)
168  setOtherCOneVertices();
169  else if (_cont == SIDE_DISCONTINUOUS)
170  continue;
171  else
172  libmesh_error();
173  } // loop over nodes
174 
175  // From here on out we won't be sampling at nodes anymore
176  _current_node = nullptr;
177 
178  auto & dof_map = _var.dofMap();
179  const bool add_p_level =
180  dof_map.should_p_refine(dof_map.var_group_from_var_number(_var.number()));
181 
182  // In 3D, project any edge values next
183  if (_dim > 2 && _cont != DISCONTINUOUS)
184  for (unsigned int e = 0; e != _current_elem->n_edges(); ++e)
185  {
186  FEInterface::dofs_on_edge(_current_elem, _dim, _fe_type, e, _side_dofs, add_p_level);
187 
188  // Some edge dofs are on nodes and already
189  // fixed, others are free to calculate
190  _free_dofs = 0;
191  for (unsigned int i = 0; i != _side_dofs.size(); ++i)
192  if (!_dof_is_fixed[_side_dofs[i]])
193  _free_dof[_free_dofs++] = i;
194 
195  // There may be nothing to project
196  if (!_free_dofs)
197  continue;
198 
199  // Initialize FE data on the edge
200  fe->attach_quadrature_rule(qedgerule.get());
201  fe->edge_reinit(_current_elem, e);
202  _n_qp = qedgerule->n_points();
203 
204  choleskySolve(false);
205  }
206 
207  // Project any side values (edges in 2D, faces in 3D)
208  if (_dim > 1 && _cont != DISCONTINUOUS)
209  for (unsigned int s = 0; s != _current_elem->n_sides(); ++s)
210  {
211  FEInterface::dofs_on_side(_current_elem, _dim, _fe_type, s, _side_dofs, add_p_level);
212 
213  // Some side dofs are on nodes/edges and already
214  // fixed, others are free to calculate
215  _free_dofs = 0;
216  for (unsigned int i = 0; i != _side_dofs.size(); ++i)
217  if (!_dof_is_fixed[_side_dofs[i]])
218  _free_dof[_free_dofs++] = i;
219 
220  // There may be nothing to project
221  if (!_free_dofs)
222  continue;
223 
224  // Initialize FE data on the side
225  fe->attach_quadrature_rule(qsiderule.get());
226  fe->reinit(_current_elem, s);
227  _n_qp = qsiderule->n_points();
228 
229  choleskySolve(false);
230  }
231 
232  // Project the interior values, finally
233 
234  // Some interior dofs are on nodes/edges/sides and
235  // already fixed, others are free to calculate
236  _free_dofs = 0;
237  for (unsigned int i = 0; i != n_dofs; ++i)
238  if (!_dof_is_fixed[i])
239  _free_dof[_free_dofs++] = i;
240 
241  // There may be nothing to project
242  if (_free_dofs)
243  {
244  // Initialize FE data
245  fe->attach_quadrature_rule(qrule.get());
246  fe->reinit(_current_elem);
247  _n_qp = qrule->n_points();
248 
249  choleskySolve(true);
250  } // if there are free interior dofs
251 
252  // Make sure every DoF got reached!
253  for (unsigned int i = 0; i != n_dofs; ++i)
254  libmesh_assert(_dof_is_fixed[i]);
255 
256  for (size_t i = 0; i < mask.size(); i++)
257  if (mask(i))
258  _var.setDofValue(_Ue(i), i);
259 }
260 
261 template <typename T>
262 void
264 {
265  // Assume that C_ZERO elements have a single nodal
266  // value shape function
267  libmesh_assert(_nc == 1);
268  _qp = _n;
269  _current_node = _current_elem->node_ptr(_n);
270  _Ue(_current_dof) = value(*_current_node);
271  _dof_is_fixed[_current_dof] = true;
272  _current_dof++;
273 }
274 
275 template <>
276 void
278 {
279  _qp = _n;
280  _current_node = _current_elem->node_ptr(_n);
281  auto point_value = value(*_current_node);
282  for (decltype(_nc) i = 0; i < _nc; ++i)
283  {
284  _Ue(_current_dof) = point_value(i);
285  _dof_is_fixed[_current_dof] = true;
286  _current_dof++;
287  }
288 }
289 
290 template <typename T>
291 T
293 {
294  return grad(i);
295 }
296 
297 template <>
300 {
301  return grad.row(i);
302 }
303 
304 template <>
307 {
308  return grad.col(i);
309 }
310 
311 template <typename T>
312 void
314 {
315  // The hermite element vertex shape functions are weird
316  _qp = _n;
317  _current_node = _current_elem->node_ptr(_n);
318  _Ue(_current_dof) = value(*_current_node);
319  _dof_is_fixed[_current_dof] = true;
320  _current_dof++;
321  GradientType grad = gradient(*_current_node);
322  // x derivative
323  _Ue(_current_dof) = gradientComponent(grad, 0);
324  _dof_is_fixed[_current_dof] = true;
325  _current_dof++;
326  if (_dim > 1)
327  {
328  // We'll finite difference mixed derivatives
329  Point nxminus = _current_elem->point(_n), nxplus = _current_elem->point(_n);
330  nxminus(0) -= TOLERANCE;
331  nxplus(0) += TOLERANCE;
332  GradientType gxminus = gradient(nxminus);
333  GradientType gxplus = gradient(nxplus);
334  // y derivative
335  _Ue(_current_dof) = gradientComponent(grad, 1);
336  _dof_is_fixed[_current_dof] = true;
337  _current_dof++;
338  // xy derivative
339  _Ue(_current_dof) =
340  (gradientComponent(gxplus, 1) - gradientComponent(gxminus, 1)) / 2. / TOLERANCE;
341  _dof_is_fixed[_current_dof] = true;
342  _current_dof++;
343 
344  if (_dim > 2)
345  {
346  // z derivative
347  _Ue(_current_dof) = gradientComponent(grad, 2);
348  _dof_is_fixed[_current_dof] = true;
349  _current_dof++;
350  // xz derivative
351  _Ue(_current_dof) =
352  (gradientComponent(gxplus, 2) - gradientComponent(gxminus, 2)) / 2. / TOLERANCE;
353  _dof_is_fixed[_current_dof] = true;
354  _current_dof++;
355  // We need new points for yz
356  Point nyminus = _current_elem->point(_n), nyplus = _current_elem->point(_n);
357  nyminus(1) -= TOLERANCE;
358  nyplus(1) += TOLERANCE;
359  GradientType gyminus = gradient(nyminus);
360  GradientType gyplus = gradient(nyplus);
361  // xz derivative
362  _Ue(_current_dof) =
363  (gradientComponent(gyplus, 2) - gradientComponent(gyminus, 2)) / 2. / TOLERANCE;
364  _dof_is_fixed[_current_dof] = true;
365  _current_dof++;
366  // Getting a 2nd order xyz is more tedious
367  Point nxmym = _current_elem->point(_n), nxmyp = _current_elem->point(_n),
368  nxpym = _current_elem->point(_n), nxpyp = _current_elem->point(_n);
369  nxmym(0) -= TOLERANCE;
370  nxmym(1) -= TOLERANCE;
371  nxmyp(0) -= TOLERANCE;
372  nxmyp(1) += TOLERANCE;
373  nxpym(0) += TOLERANCE;
374  nxpym(1) -= TOLERANCE;
375  nxpyp(0) += TOLERANCE;
376  nxpyp(1) += TOLERANCE;
377  GradientType gxmym = gradient(nxmym);
378  GradientType gxmyp = gradient(nxmyp);
379  GradientType gxpym = gradient(nxpym);
380  GradientType gxpyp = gradient(nxpyp);
381  DataType gxzplus =
382  (gradientComponent(gxpyp, 2) - gradientComponent(gxmyp, 2)) / 2. / TOLERANCE;
383  DataType gxzminus =
384  (gradientComponent(gxpym, 2) - gradientComponent(gxmym, 2)) / 2. / TOLERANCE;
385  // xyz derivative
386  _Ue(_current_dof) = (gxzplus - gxzminus) / 2. / TOLERANCE;
387  _dof_is_fixed[_current_dof] = true;
388  _current_dof++;
389  }
390  }
391 }
392 
393 template <>
394 void
396 {
397 }
398 
399 template <typename T>
400 void
402 {
403  // Assume that other C_ONE elements have a single nodal
404  // value shape function and nodal gradient component
405  // shape functions
406  libmesh_assert(_nc == 1 + _dim);
407  _current_node = _current_elem->node_ptr(_n);
408  _Ue(_current_dof) = value(*_current_node);
409  _dof_is_fixed[_current_dof] = true;
410  _current_dof++;
411  GradientType grad = gradient(*_current_node);
412  for (unsigned int i = 0; i != _dim; ++i)
413  {
414  _Ue(_current_dof) = gradientComponent(grad, i);
415  _dof_is_fixed[_current_dof] = true;
416  _current_dof++;
417  }
418 }
419 
420 template <>
421 void
423 {
424 }
425 
426 template <typename T>
427 void
429 {
430  // Loop over the quadrature points
431  for (_qp = 0; _qp < _n_qp; _qp++)
432  {
433  // solution at the quadrature point
434  auto fineval = value((*_xyz_values)[_qp]);
435  // solution grad at the quadrature point
436  GradientType finegrad;
437  if (_cont == C_ONE)
438  finegrad = gradient((*_xyz_values)[_qp]);
439 
440  auto dofs_size = is_volume ? (_dof_indices.size() / _var.count()) : _side_dofs.size();
441 
442  // Form edge projection matrix
443  for (decltype(dofs_size) geomi = 0, freei = 0; geomi != dofs_size; ++geomi)
444  {
445  auto i = is_volume ? geomi : _side_dofs[geomi];
446 
447  // fixed DoFs aren't test functions
448  if (_dof_is_fixed[i])
449  continue;
450  for (decltype(dofs_size) geomj = 0, freej = 0; geomj != dofs_size; ++geomj)
451  {
452  auto j = is_volume ? geomj : _side_dofs[geomj];
453  if (_dof_is_fixed[j])
454  _Fe(freei) -= (*_phi)[i][_qp] * (*_phi)[j][_qp] * (*_JxW)[_qp] * _Ue(j);
455  else
456  _Ke(freei, freej) += (*_phi)[i][_qp] * (*_phi)[j][_qp] * (*_JxW)[_qp];
457  if (_cont == C_ONE)
458  {
459  if (_dof_is_fixed[j])
460  _Fe(freei) -= dotHelper((*_dphi)[i][_qp], (*_dphi)[j][_qp]) * (*_JxW)[_qp] * _Ue(j);
461  else
462  _Ke(freei, freej) += dotHelper((*_dphi)[i][_qp], (*_dphi)[j][_qp]) * (*_JxW)[_qp];
463  }
464  if (!_dof_is_fixed[j])
465  freej++;
466  }
467  _Fe(freei) += (*_phi)[i][_qp] * fineval * (*_JxW)[_qp];
468  if (_cont == C_ONE)
469  _Fe(freei) += dotHelper(finegrad, (*_dphi)[i][_qp]) * (*_JxW)[_qp];
470  freei++;
471  }
472  }
473 }
474 
475 template <typename T>
476 void
478 {
479  _Ke.resize(_free_dofs, _free_dofs);
480  _Ke.zero();
481  _Fe.resize(_free_dofs);
482  _Fe.zero();
483 
484  choleskyAssembly(is_volume);
485 
486  // The new edge coefficients
487  DenseVector<DataType> U(_free_dofs);
488 
489  _Ke.cholesky_solve(_Fe, U);
490 
491  // Transfer new edge solutions to element
492  for (unsigned int i = 0; i != _free_dofs; ++i)
493  {
494  auto the_dof = is_volume ? _free_dof[i] : _side_dofs[_free_dof[i]];
495  DataType & ui = _Ue(the_dof);
496  libmesh_assert(std::abs(ui) < TOLERANCE || std::abs(ui - U(i)) < TOLERANCE);
497  ui = U(i);
498  _dof_is_fixed[the_dof] = true;
499  }
500 }
501 
502 template <>
503 void
505 {
506  _Ke.resize(_free_dofs, _free_dofs);
507  _Ke.zero();
508  _Fe.resize(_free_dofs);
509  for (unsigned int i = 0; i < _free_dofs; ++i)
510  _Fe(i).setZero(_var.count());
511 
512  choleskyAssembly(is_volume);
513 
514  // The new edge coefficients
515  DenseVector<DataType> U = _Fe;
516 
517  for (unsigned int i = 0; i < _var.count(); ++i)
518  {
519  DenseVector<Real> v(_free_dofs), x(_free_dofs);
520  for (unsigned int j = 0; j < _free_dofs; ++j)
521  v(j) = _Fe(j)(i);
522 
523  _Ke.cholesky_solve(v, x);
524 
525  for (unsigned int j = 0; j < _free_dofs; ++j)
526  U(j)(i) = x(j);
527  }
528 
529  // Transfer new edge solutions to element
530  for (unsigned int i = 0; i != _free_dofs; ++i)
531  {
532  auto the_dof = is_volume ? _free_dof[i] : _side_dofs[_free_dof[i]];
533  DataType & ui = _Ue(the_dof);
534  libmesh_assert(ui.matrix().norm() < TOLERANCE || (ui - U(i)).matrix().norm() < TOLERANCE);
535  ui = U(i);
536  _dof_is_fixed[the_dof] = true;
537  }
538 }
539 
540 template <typename T>
541 void
543 {
544  _var.reinitNode();
545  _var.computeNodalValues(); // has to call this to resize the internal array
546  auto return_value = value(p);
547 
548  _var.setNodalValue(return_value); // update variable data, which is referenced by others, so the
549  // value is up-to-date
550 
551  // We are done, so update the solution vector
552  _var.insert(_var.sys().solution());
553 }
554 
555 template class InitialConditionTempl<Real>;
MetaPhysicL::DualNumber< V, D, asd > abs(const MetaPhysicL::DualNumber< V, D, asd > &a)
Definition: EigenADReal.h:50
OutputTools< T >::OutputData DataType
Class for stuff related to variables.
Definition: Adaptivity.h:31
OutputTools< T >::OutputGradient GradientType
HERMITE
void setOtherCOneVertices()
set the temporary solution vector for node projections of non-Hermitian C1 variables ...
This is a template class that implements the workhorse compute and computeNodal methods.
void choleskyAssembly(bool is_volume)
Assemble a small local system for cholesky solve.
KOKKOS_INLINE_FUNCTION void choleskySolve(Real *const A, Real *const x, Real *const b, const unsigned int n)
Perform an in-place linear solve using Cholesky decomposition Matrix and right-hand-side vector are m...
Definition: KokkosUtils.h:74
virtual void computeNodal(const Point &p) override
Workhorse method for projecting the initial conditions for boundary restricted initial conditions...
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
InitialConditionBase serves as the abstract base class for InitialConditions and VectorInitialConditi...
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
Specialization of SubProblem for solving nonlinear equations plus auxiliary equations.
DISCONTINUOUS
H_CURL
SIDE_DISCONTINUOUS
const dof_id_type n_nodes
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
void setCZeroVertices()
set the temporary solution vector for node projections of C0 variables
C_ZERO
libmesh_assert(ctx)
void setHermiteVertices()
set the temporary solution vector for node projections of Hermite variables
InitialConditionTempl(const InputParameters &parameters)
Constructor.
virtual void compute() override
Workhorse method for projecting the initial conditions for block initial conditions.
H_DIV
void choleskySolve(bool is_volume)
Perform the cholesky solves for edge, side, and interior projections.
C_ONE
virtual unsigned int size() const override final
Eigen::Matrix< Real, Eigen::Dynamic, 1 > RealEigenVector
Definition: MooseTypes.h:147
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
T gradientComponent(GradientType grad, unsigned int i)
const Elem & get(const ElemType type_in)
unsigned int THREAD_ID
Definition: MooseTypes.h:237