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