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
19using namespace libMesh;
20
21template <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
42template <typename T>
46
47template <typename T>
48void
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 auto & dof_map = _var.dofMap();
129 const bool add_p_level =
130 dof_map.should_p_refine(dof_map.var_group_from_var_number(_var.number()));
131
132 for (_n = 0; _n != n_nodes; ++_n)
133 {
134 _nc = FEInterface::n_dofs_at_node(_fe_type, _current_elem, _n, add_p_level);
135
136 // for nodes that are in more than one subdomain, only compute the initial
137 // condition once on the lowest numbered block
138 auto curr_node = _current_elem->node_ptr(_n);
139 const auto & block_ids = _sys.mesh().getNodeBlockIds(*curr_node);
140
141 auto priority_block = *(block_ids.begin());
142 for (auto id : block_ids)
143 if (_var.hasBlocks(id))
144 {
145 priority_block = id;
146 break;
147 }
148
149 if (!hasBlocks(priority_block) && _var.isNodal())
150 {
151 for (decltype(_nc) i = 0; i < _nc; ++i)
152 {
153 mask(_current_dof) = false;
154 _current_dof++;
155 }
156 continue;
157 }
158
159 if (!_current_elem->is_vertex(_n))
160 {
161 _current_dof += _nc;
162 continue;
163 }
164
165 if (_cont == DISCONTINUOUS || _cont == H_CURL || _cont == H_DIV)
166 libmesh_assert(_nc == 0);
167 else if (_cont == C_ZERO)
168 setCZeroVertices();
169 else if (_fe_type.family == HERMITE)
170 setHermiteVertices();
171 else if (_cont == C_ONE)
172 setOtherCOneVertices();
173 else if (_cont == SIDE_DISCONTINUOUS)
174 continue;
175 else
176 libmesh_error();
177 } // loop over nodes
178
179 // From here on out we won't be sampling at nodes anymore
180 _current_node = nullptr;
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
261template <typename T>
262void
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
275template <>
276void
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
290template <typename T>
291T
293{
294 return grad(i);
295}
296
297template <>
300{
301 return grad.row(i);
302}
303
304template <>
307{
308 return grad.col(i);
309}
310
311template <typename T>
312void
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
393template <>
394void
398
399template <typename T>
400void
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
420template <>
421void
425
426template <typename T>
427void
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
475template <typename T>
476void
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
502template <>
503void
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
540template <typename T>
541void
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
555template 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.
virtual unsigned int size() const override final
static void dofs_on_side(const Elem *const elem, const unsigned int dim, const FEType &fe_t, unsigned int s, std::vector< unsigned int > &di, const bool add_p_level=true)
static unsigned int n_dofs_at_node(const unsigned int dim, const FEType &fe_t, const ElemType t, const unsigned int n)
static void dofs_on_edge(const Elem *const elem, const unsigned int dim, const FEType &fe_t, unsigned int e, std::vector< unsigned int > &di, const bool add_p_level=true)
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
SIDE_DISCONTINUOUS
libmesh_assert(ctx)
static constexpr Real TOLERANCE
Eigen::Matrix< Real, Eigen::Dynamic, 1 > RealEigenVector
Definition MooseTypes.h:147
const dof_id_type n_nodes