libMesh
Loading...
Searching...
No Matches
claw_system.C
Go to the documentation of this file.
1// The libMesh Finite Element Library.
2// Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3
4// This library is free software; you can redistribute it and/or
5// modify it under the terms of the GNU Lesser General Public
6// License as published by the Free Software Foundation; either
7// version 2.1 of the License, or (at your option) any later version.
8
9// This library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12// Lesser General Public License for more details.
13
14// You should have received a copy of the GNU Lesser General Public
15// License along with this library; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18// local includes
19#include "claw_system.h"
20
21// LibMesh includes
22#include "libmesh/dense_matrix.h"
23#include "libmesh/dof_map.h"
24#include "libmesh/equation_systems.h"
25#include "libmesh/exodusII_io.h"
26#include "libmesh/fe.h" // FEBase::buid()
27#include "libmesh/fe_interface.h" // FEInterface::inverse_map()
28#include "libmesh/getpot.h" // GetPot input file parsing
29#include "libmesh/libmesh_logging.h" // LOG_SCOPE
30#include "libmesh/linear_solver.h" // LinearSolver::reuse_preconditioner()
31#include "libmesh/mesh_base.h" // MeshBase::active_local_element_ptr_range()
32#include "libmesh/numeric_vector.h"
33#include "libmesh/quadrature_gauss.h"
34#include "libmesh/sparse_matrix.h"
35
36namespace libMesh
37{
38
40 const std::string & name,
41 const unsigned int number)
42 : Parent(es, name, number),
43 _mass_matrix(SparseMatrix<Number>::build(es.comm())),
44 _jump_matrix(SparseMatrix<Number>::build(es.comm())),
45 _temporal_discretization_type(ForwardEuler),
46 _delta_t(0.),
47 _n_time_steps(0),
48 _LxF_constant(1.),
49 _write_interval(1),
50 _time(0.)
51{
52 // set assemble_before_solve flag to false
53 // so that we control matrix assembly.
54 this->assemble_before_solve = false;
55
56}
57
58
59ClawSystem::~ClawSystem() = default;
60
61std::string ClawSystem::system_type () const
62{
63 return "ClawSystem";
64}
65
67ClawSystem::string_to_enum(std::string string_type)
68{
69 if (string_type == "ForwardEuler")
70 return ForwardEuler;
71
72 if (string_type == "RK4")
73 return RK4;
74
75 // If we got here, there must be an error
76 libmesh_error_msg("Error: Invalid temporal discretization");
77}
78
79std::string
81{
82 switch (enum_type)
83 {
84 case ForwardEuler:
85 return "ForwardEuler";
86
87 case RK4:
88 return "RK4";
89
90 default:
91 libmesh_error_msg("Error: Invalid temporal discretization");
92 }
93}
94
96{
97 // Set the ImplicitSystem "matrix" equal to the mass matrix. This is
98 // the only part of the explicit method that requires a "solve()",
99 // however we save the LU factorization from this and apply it
100 // during subsequent solve() steps.
101 this->matrix->zero();
102 this->matrix->close();
103 this->matrix->add(1., *_mass_matrix);
104
105 this->set_time(0.);
106
107 // For the first solve, make sure we generate a new preconditioner
108 linear_solver->reuse_preconditioner(false);
109
110 auto old_solution = NumericVector<Number>::build(this->comm());
111 old_solution->init (this->n_dofs(), this->n_local_dofs(), false, libMeshEnums::PARALLEL);
112
113 // Temporary and "stage" vectors - only initialized if doing RK timestepping
114 std::vector<std::unique_ptr<NumericVector<Number>>> k(4);
115 std::unique_ptr<NumericVector<Number>> temp;
117 {
118 temp = NumericVector<Number>::build(this->comm());
119 temp->init (this->n_dofs(), this->n_local_dofs(), false, libMeshEnums::PARALLEL);
120
121 for (auto & vec : k)
122 {
123 vec = NumericVector<Number>::build(this->comm());
124 vec->init (this->n_dofs(), this->n_local_dofs(), false, libMeshEnums::PARALLEL);
125 }
126 }
127
128 // Begin time stepping loop
129 for (unsigned int step=1; step<=_n_time_steps; step++)
130 {
131 *old_solution = *solution;
132
134 {
135 case ForwardEuler:
136 {
137 this->assemble_claw_rhs(*old_solution);
138
139 // For explicit timestepping schemes, the solve() step
140 // "inverts" the mass matrix and then back substitutes the rhs
141 // to update the solution. In practice, the mass matrix
142 // factorization is computed only once and then the factors
143 // are re-used at each step.
144 this->solve();
145
146 // Tell the system to reuse the preconditioner (for the mass matrix)
147 if (step == 1)
148 linear_solver->reuse_preconditioner(true);
149
150 solution->scale(_delta_t);
151 solution->add(*old_solution);
152
153 break;
154 }
155
156 case RK4:
157 {
158 // Compute stage 1 (explicit Euler step)
159 this->assemble_claw_rhs(*old_solution);
160 this->solve();
161 *k[0] = *solution;
162
163 // Tell the system to reuse the preconditioner (for the mass matrix)
164 if (step==1)
165 linear_solver->reuse_preconditioner(true);
166
167 // Compute stage 2
168 *temp = *old_solution;
169 temp->add(0.5*_delta_t, *k[0]);
170 this->assemble_claw_rhs(*temp);
171 this->solve();
172 *k[1] = *solution;
173
174 // Compute stage 3
175 *temp = *old_solution;
176 temp->add(0.5*_delta_t, *k[1]);
177 this->assemble_claw_rhs(*temp);
178 this->solve();
179 *k[2] = *solution;
180
181 // Compute stage 4
182 *temp = *old_solution;
183 temp->add(_delta_t, *k[2]);
184 this->assemble_claw_rhs(*temp);
185 this->solve();
186 *k[3] = *solution;
187
188 // The updated solution is now a linear combination of the 4 "stages"
189 // u_{n+1} = u_n + dt*(k1/6 + k2/3 + k3/3 + k4/6)
190 *solution = *old_solution;
191 solution->add(_delta_t/6., *k[0]);
192 solution->add(_delta_t/3., *k[1]);
193 solution->add(_delta_t/3., *k[2]);
194 solution->add(_delta_t/6., *k[3]);
195
196 break;
197 }
198
199 default:
200 libmesh_error_msg("Error: Invalid temporal discretization in ClawSystem::solve_conservation_law");
201 }
202
203 this->update();
204
205 this->set_time(_time + _delta_t);
206
207#ifdef LIBMESH_HAVE_EXODUS_API
208 if (step % get_write_interval() == 0)
209 {
210 libMesh::out << std::endl << "plotting time step " << step << ", time = " << _time << std::endl;
211
212 std::ostringstream file_name;
213 file_name << "claw_solution."
214 << std::setw(4)
215 << std::right
216 << std::setfill('0')
217 << step
218 << ".e";
219
220 // We are writing to a separate file so we write each step as step 1.
221 // It would be better to append each new solution to an existing file...
223 file_name.str(), this->get_equation_systems(), /*step=*/1, _time);
224 }
225#endif
226 } // end time stepping loop
227
228 linear_solver->reuse_preconditioner(false);
229}
230
231void ClawSystem::process_parameters_file (const std::string& parameters_filename)
232{
233 // First read in data from parameters_filename
234 GetPot infile(parameters_filename);
235
236 const std::string temporal_discretization_type_in = infile("temporal_discretization_type", "ForwardEuler");
237 const Real LxF_constant_in = infile("LxF_constant", 1.);
238 const Real delta_t_in = infile("delta_t", 0.05);
239 const unsigned int n_time_steps_in = infile("n_time_steps", 0);
240 const unsigned int write_interval_in = infile("write_interval", 1);
241
242 this->set_temporal_discretization_type(ClawSystem::string_to_enum(temporal_discretization_type_in));
243 this->set_LxF_constant(LxF_constant_in);
244 this->set_delta_t(delta_t_in);
245 this->set_n_time_steps(n_time_steps_in);
246 this->set_write_interval(write_interval_in);
247}
248
250{
251 // Print out info that describes the current setup
252 libMesh::out << std::endl << "==== ClawSystem ====" << std::endl;
253 libMesh::out << "system name: " << this->name() << std::endl;
254 libMesh::out << "LxF constant: " << get_LxF_constant() << std::endl;
255 libMesh::out << "delta_t: " << get_delta_t() << std::endl;
256 libMesh::out << "n_time_steps: " << get_n_time_steps() << std::endl;
257 libMesh::out << "temporal_discretization_type: " << ClawSystem::enum_to_string(this->get_temporal_discretization_type()) << std::endl;
258 libMesh::out << "write_interval: " << get_write_interval() << std::endl;
259 libMesh::out << std::endl;
260}
261
270
272{
273 LOG_SCOPE("assemble_mass_matrix()", "ClawSystem");
274
275 // clear the mass matrix before filling
276 _mass_matrix->zero();
277
278 // Convenient reference to the Mesh
279 const MeshBase & mesh = this->get_mesh();
280 const unsigned int mesh_dim = mesh.mesh_dimension();
281
282 // Construct an FE object to be used for assembly
283 FEType fe_type = this->variable_type(0);
284 std::unique_ptr<FEBase> fe = FEBase::build(mesh_dim, fe_type);
285
286 // Quadrature rules for numerical integration.
287 QGauss qrule (mesh_dim, fe_type.default_quadrature_order());
288 fe->attach_quadrature_rule (&qrule);
289
290 // Pre-request JxW and shape function values at interior quadrature points.
291 const std::vector<Real> & JxW = fe->get_JxW();
292 const std::vector<std::vector<Real>> & phi = fe->get_phi();
293
294 // Element mass matrix
296
297 // DofMap reference and vector used for getting dof_indices on each Elem
298 const DofMap & dof_map = get_dof_map();
299 std::vector<dof_id_type> dof_indices;
300
301 for (const auto & elem : mesh.active_local_element_ptr_range())
302 {
303 // Recompute shape function values, etc. on current Elem
304 fe->reinit(elem);
305
306 // Get DOFs for current Elem
307 dof_map.dof_indices (elem, dof_indices);
308
309 // The number of degrees of freedom on this element for variable 0.
310 const unsigned int n_q1_dofs = dof_indices.size();
311
312 // Resize the Elem mass matrix for the current Elem
313 Ke.resize(n_q1_dofs, n_q1_dofs);
314
315 // The number of quadrature points on the current Elem
316 unsigned int n_qpoints = qrule.n_points();
317
318 // Assemble element mass matrix. Here we loop over vars but assume that
319 // every var has the same number of DOFs as variable 0.
320 for (auto qp : make_range(n_qpoints))
321 for (auto i : make_range(n_q1_dofs))
322 for (auto j : make_range(n_q1_dofs))
323 Ke(i,j) += JxW[qp] * phi[j][qp] * phi[i][qp];
324
325 // Apply constraints, e.g. periodic constraints and "stamp" the
326 // local matrix into the global matrix
327 this->get_dof_map().constrain_element_matrix(Ke, dof_indices);
328 _mass_matrix->add_matrix (Ke, dof_indices);
329 }
330
331 // Close the mass matrix now that we are done with assembly
332 _mass_matrix->close();
333}
334
336{
337 LOG_SCOPE("assemble_advection_matrices()", "ClawSystem");
338
339 // Zero all advection matrices before filling
340 for (auto & mat : _advection_matrices)
341 mat->zero();
342
343 // Convenient reference to the Mesh
344 const MeshBase & mesh = this->get_mesh();
345 const unsigned int mesh_dim = mesh.mesh_dimension();
346
347 // Check that the mesh dimension matches the number of advection
348 // matrices we are going to assemble.
349 libmesh_error_msg_if(
350 mesh_dim != _advection_matrices.size(),
351 "Error: Expected number of advection matrices to match mesh dimension.");
352
353 // Construct an FE object to be used for assembly
354 FEType fe_type = this->variable_type(0);
355 std::unique_ptr<FEBase> fe = FEBase::build(mesh_dim, fe_type);
356
357 // Quadrature rules for numerical integration.
358 QGauss qrule (mesh_dim, fe_type.default_quadrature_order());
359 fe->attach_quadrature_rule (&qrule);
360
361 // Pre-request JxW and shape function values/derivatives at interior quadrature points
362 const std::vector<Real> & JxW = fe->get_JxW();
363 const std::vector<std::vector<Real>> & phi = fe->get_phi();
364 const std::vector<std::vector<RealGradient>> & dphi = fe->get_dphi();
365
366 // We are going to build a separate element advection matrix for each direction.
367 std::vector<DenseMatrix<Number>> K_vec(mesh_dim);
368
369 // DofMap reference and vector used for getting dof_indices on each Elem
370 const DofMap & dof_map = get_dof_map();
371 std::vector<dof_id_type> dof_indices;
372
373 for (const auto & elem : mesh.active_local_element_ptr_range())
374 {
375 // Recompute shape function values, etc. on current Elem
376 fe->reinit(elem);
377
378 // Get DOFs for current Elem
379 dof_map.dof_indices (elem, dof_indices);
380
381 // The number of degrees of freedom on this element
382 const unsigned int n_q1_dofs = dof_indices.size();
383
384 // Resize all the Elem advection matrices for the current Elem
385 for (auto & Ke : K_vec)
386 Ke.resize(n_q1_dofs, n_q1_dofs);
387
388 // The number of quadrature points on the current Elem
389 unsigned int n_qpoints = qrule.n_points();
390
391 // Assemble the convection operators. There are two separate
392 // operators in 2D (one for x-direction and one for y-direction).
393 // Note: in the FV (CONSTANT, MONOMIAL) case, these interior
394 // contributions are all zero because the spatial derivatives of
395 // the single constant "basis function" are all zero.
396 for (auto qp : make_range(n_qpoints))
397 for (auto i : make_range(n_q1_dofs))
398 for (auto j : make_range(n_q1_dofs))
399 for (auto dim : make_range(mesh_dim))
400 K_vec[dim](i,j) += JxW[qp] * phi[j][qp] * dphi[i][qp](dim);
401
402 // Apply constraints to Elem matrices and "stamp" into the global matrices
403 for (auto dim : make_range(mesh_dim))
404 {
405 dof_map.constrain_element_matrix(K_vec[dim], dof_indices);
406 _advection_matrices[dim]->add_matrix(K_vec[dim], dof_indices);
407 }
408 }
409
410 // Close all advection matrices now that we are done assembling
411 for (auto & mat : _advection_matrices)
412 mat->close();
413}
414
416{
417 LOG_SCOPE("assemble_avg_coupling_matrices()", "ClawSystem");
418
419 // Zero all average coupling matrices before filling
420 for (auto & mat : _avg_matrices)
421 mat->zero();
422
423 // Convenient reference to the Mesh
424 const MeshBase & mesh = this->get_mesh();
425 const unsigned int mesh_dim = mesh.mesh_dimension();
426
427 // Check that the mesh dimension matches the number of average coupling
428 // matrices we are going to assemble.
429 libmesh_error_msg_if(
430 mesh_dim != _avg_matrices.size(),
431 "Error: Expected number of average coupling matrices to match mesh dimension.");
432
433 // Construct an side FE objects for both "current" and "neighbor" Elems
434 FEType fe_type = this->variable_type(0);
435 std::unique_ptr<FEBase> fe_elem_face = FEBase::build(mesh_dim, fe_type);
436 std::unique_ptr<FEBase> fe_neighbor_face =FEBase::build(mesh_dim, fe_type);
437
438 // Quadrature rules for numerical integration.
439 QGauss qface(mesh_dim-1, fe_type.default_quadrature_order());
440 fe_elem_face->attach_quadrature_rule(&qface);
441 fe_neighbor_face->attach_quadrature_rule(&qface);
442
443 // Here we define some references to cell-specific data that
444 // will be used to assemble the linear system.
445 // Data for surface integrals on the element boundary
446 const std::vector<std::vector<Real>> & phi_face = fe_elem_face->get_phi();
447 const std::vector<Real> & JxW_face = fe_elem_face->get_JxW();
448 const std::vector<Point> & qface_normals = fe_elem_face->get_normals();
449 const std::vector<Point> & qface_points = fe_elem_face->get_xyz();
450
451 // Data for surface integrals on the neighbor boundary
452 const std::vector<std::vector<Real>> & phi_neighbor_face = fe_neighbor_face->get_phi();
453
454 // Data structures to contain the element/neighbor contributions on
455 // interior faces. We store these separately for each space dimension.
456 std::vector<DenseMatrix<Number>> Kne_vec(mesh_dim);
457 std::vector<DenseMatrix<Number>> Ken_vec(mesh_dim);
458 std::vector<DenseMatrix<Number>> Kee_vec(mesh_dim);
459 std::vector<DenseMatrix<Number>> Knn_vec(mesh_dim);
460
461 // DofMap reference and vectors used for getting dof_indices on each Elem
462 const DofMap & dof_map = get_dof_map();
463 std::vector<dof_id_type> dof_indices;
464 std::vector<dof_id_type> neighbor_dof_indices;
465
466 for (const auto * elem : mesh.active_local_element_ptr_range())
467 {
468 dof_map.dof_indices (elem, dof_indices);
469 const unsigned int n_dofs = dof_indices.size();
470
471 for (auto side : elem->side_index_range())
472 {
473 // Skip over boundary sides
474 if (elem->neighbor_ptr(side) != nullptr)
475 {
476 const Elem * neighbor = elem->neighbor_ptr(side);
477
478 // Get the global id of the element and the neighbor
479 const unsigned int elem_id = elem->id();
480 const unsigned int neighbor_id = neighbor->id();
481
482 // Compare element IDs since we don't want to compute the same contribution twice
483 if (elem_id < neighbor_id)
484 {
485 // Pointer to the element side
486 std::unique_ptr<const Elem> elem_side = elem->build_side_ptr(side);
487
488 // Reinitialize shape functions on the element side
489 fe_elem_face->reinit(elem, side);
490
491 // Find their locations on the neighbor
492 std::vector<Point> qface_neighbor_points;
494 neighbor->dim(),
495 neighbor,
496 /*physical_points*/qface_points,
497 /*reference_points*/qface_neighbor_points);
498
499 // Calculate the neighbor element shape functions at those locations
500 fe_neighbor_face->reinit(neighbor, &qface_neighbor_points);
501
502 // Get the degree of freedom indices for the
503 // neighbor. These define where in the global
504 // matrix this neighbor will contribute to.
505 dof_map.dof_indices (neighbor, neighbor_dof_indices);
506 const unsigned int n_neighbor_dofs = neighbor_dof_indices.size();
507
508 // Zero the element matrices summing into them. We use the
509 // resize member here because the number of degrees of
510 // freedom might have changed from the last element or
511 // neighbor. Note that Kne and Ken are not square matrices
512 // if neighbor and element have a different p level
513 for (auto dim : make_range(mesh_dim))
514 {
515 Kne_vec[dim].resize(n_neighbor_dofs, n_dofs);
516 Ken_vec[dim].resize(n_dofs, n_neighbor_dofs);
517 Kee_vec[dim].resize(n_dofs, n_dofs);
518 Knn_vec[dim].resize(n_neighbor_dofs, n_neighbor_dofs);
519 }
520
521 // Now we will build the "average" contribution due to the
522 // Lax-Friedrichs numerical flux term:
523 //
524 // "{{f(q)}}[v] \cdot n_e"
525 //
526 // For the advection problem, this is term is given by:
527 // 0.5 * ((\vec{u} q)_e + (\vec{u} q)_n) \cdot \hat{n}_e * [v]
528 //
529 // The assumption below is that the advective velocity is
530 // constant on the entire mesh, so it is pulled outside of
531 // the integration loop, hence the reason it does not appear
532 // explicitly below.
533 for (auto qp : make_range(qface.n_points()))
534 {
535 // Kee Matrix.
536 // This is the "q_e" contribution with v = phi^e_i.
537 for (auto i : make_range(n_dofs))
538 for (auto j : make_range(n_dofs))
539 for (auto dim : make_range(mesh_dim))
540 Kee_vec[dim](i,j) += 0.5 * JxW_face[qp] * phi_face[i][qp] * phi_face[j][qp] * qface_normals[qp](dim);
541
542 // Knn Matrix.
543 // This is the "q_n" contribution with v = -phi^n_i.
544 // Hence, there is a negative sign.
545 for (auto i : make_range(n_neighbor_dofs))
546 for (auto j : make_range(n_neighbor_dofs))
547 for (auto dim : make_range(mesh_dim))
548 Knn_vec[dim](i,j) += -0.5 * JxW_face[qp] * phi_neighbor_face[i][qp] * phi_neighbor_face[j][qp] * qface_normals[qp](dim);
549
550 // Kne Matrix.
551 // This is the "q_e" contribution with v = -phi^n_i.
552 // i goes over neighbor DOFs
553 // j goes over self DOFs
554 // Hence, there is a negative sign.
555 for (auto i : make_range(n_neighbor_dofs))
556 for (auto j : make_range(n_dofs))
557 for (auto dim : make_range(mesh_dim))
558 Kne_vec[dim](i,j) += -0.5 * JxW_face[qp] * phi_neighbor_face[i][qp] * phi_face[j][qp] * qface_normals[qp](dim);
559
560 // Ken Matrix.
561 // This is the "q_n" contribution with v = phi^e_i.
562 // i goes over self DOFs
563 // j goes over neighbor DOFs
564 for (auto i : make_range(n_dofs))
565 for (auto j : make_range(n_neighbor_dofs))
566 for (auto dim : make_range(mesh_dim))
567 Ken_vec[dim](i,j) += 0.5 * JxW_face[qp] * phi_face[i][qp] * phi_neighbor_face[j][qp] * qface_normals[qp](dim);
568 }
569
570 // The element and neighbor boundary matrix are now built
571 // for this side. Add them to the appropriate global matrices now.
572 for (auto dim : make_range(mesh_dim))
573 {
574 auto & mat = _avg_matrices[dim];
575 mat->add_matrix(Kne_vec[dim], neighbor_dof_indices, dof_indices);
576 mat->add_matrix(Ken_vec[dim], dof_indices, neighbor_dof_indices);
577 mat->add_matrix(Kee_vec[dim], dof_indices);
578 mat->add_matrix(Knn_vec[dim], neighbor_dof_indices);
579 }
580 }
581 }
582 }
583 }
584
585 // Close all average coupling matrices now that we are done assembling
586 for (auto & mat : _avg_matrices)
587 mat->close();
588}
589
591{
592 LOG_SCOPE("assemble_jump_coupling_matrix()", "ClawSystem");
593
594 // clear the matrix
595 _jump_matrix->zero();
596
597 const MeshBase & mesh = this->get_mesh();
598 const unsigned int mesh_dim = mesh.mesh_dimension();
599
600 // Construct face FE objects for both "current" and "neighbor" Elems
601 FEType fe_type = variable_type(0);
602 std::unique_ptr<FEBase> fe_elem_face = FEBase::build(mesh_dim, fe_type);
603 std::unique_ptr<FEBase> fe_neighbor_face = FEBase::build(mesh_dim, fe_type);
604
605 // Quadrature rules for numerical integration.
606 QGauss qface(mesh_dim-1, fe_type.default_quadrature_order());
607 fe_elem_face->attach_quadrature_rule(&qface);
608 fe_neighbor_face->attach_quadrature_rule(&qface);
609
610 // Here we define some references to cell-specific data that
611 // will be used to assemble the linear system.
612 // Data for surface integrals on the element boundary
613 const std::vector<std::vector<Real>> & phi_face = fe_elem_face->get_phi();
614 const std::vector<Real> & JxW_face = fe_elem_face->get_JxW();
615 const std::vector<Point> & qface_points = fe_elem_face->get_xyz();
616
617 // Data for surface integrals on the neighbor boundary
618 const std::vector<std::vector<Real>> & phi_neighbor_face = fe_neighbor_face->get_phi();
619
620 // Data structures to contain the element and neighbor boundary matrix
621 // contribution. This matrices will do the coupling beetwen the dofs of
622 // the element and those of his neighbors.
623 // Ken: matrix coupling elem and neighbor dofs
628
629 // DofMap reference and vectors used for storing dof_indices
630 const DofMap & dof_map = get_dof_map();
631 std::vector<dof_id_type> dof_indices;
632 std::vector<dof_id_type> neighbor_dof_indices;
633
634 for (const auto & elem : mesh.active_local_element_ptr_range())
635 {
636 dof_map.dof_indices (elem, dof_indices);
637 const unsigned int n_dofs = dof_indices.size();
638
639 for (auto side : elem->side_index_range())
640 {
641 // Skip over boundary sides
642 if (elem->neighbor_ptr(side) != nullptr)
643 {
644 const Elem * neighbor = elem->neighbor_ptr(side);
645
646 // Get the global id of the element and the neighbor
647 const unsigned int elem_id = elem->id();
648 const unsigned int neighbor_id = neighbor->id();
649
650 // Compare element IDs since we don't want to compute the same contribution twice
651 if (elem_id < neighbor_id)
652 {
653 // Pointer to the element side
654 std::unique_ptr<const Elem> elem_side = elem->build_side_ptr(side);
655
656 // Reinitialize shape functions on the element side
657 fe_elem_face->reinit(elem, side);
658
659 // Find side QP locations in neighbor side reference space
660 std::vector<Point> qface_neighbor_points;
662 neighbor->dim(),
663 neighbor,
664 /*physical_points*/qface_points,
665 /*reference_points*/qface_neighbor_points);
666
667 // Calculate the neighbor element shape functions at those locations
668 fe_neighbor_face->reinit(neighbor, &qface_neighbor_points);
669
670 // Get the degree of freedom indices for the
671 // neighbor. These define where in the global
672 // matrix this neighbor will contribute to.
673 dof_map.dof_indices (neighbor, neighbor_dof_indices);
674 const unsigned int n_neighbor_dofs = neighbor_dof_indices.size();
675
676 // Zero the element and neighbor side matrix before
677 // summing them. We use the resize member here because
678 // the number of degrees of freedom might have changed from
679 // the last element or neighbor.
680 // Note that Kne and Ken are not square matrices if neighbor
681 // and element have a different p level
682 Kne.resize (n_neighbor_dofs, n_dofs);
683 Ken.resize (n_dofs, n_neighbor_dofs);
684 Kee.resize (n_dofs, n_dofs);
685 Knn.resize (n_neighbor_dofs, n_neighbor_dofs);
686
687 // Now we will build the element and neighbor
688 // boundary matrices. This involves
689 // a double loop to integrate the test funcions
690 // (i) against the trial functions (j).
691 for (auto qp : make_range(qface.n_points()))
692 {
693 // Kee Matrix. This is the "q_e" contribution from the [q][v] term with v = phi^e_i
694 // (q_e - q_n) * phi^e_i
695 for (auto i : make_range(n_dofs))
696 for (auto j : make_range(n_dofs))
697 Kee(i,j) += 0.5 * JxW_face[qp] * phi_face[i][qp] * phi_face[j][qp];
698
699 // Knn Matrix. This is the "q_n" contribution from the [q][v] term with v = -phi^n_i
700 // (q_e - q_n) * -phi^n_i
701 for (auto i : make_range(n_neighbor_dofs))
702 for (auto j : make_range(n_neighbor_dofs))
703 Knn(i,j) += 0.5 * JxW_face[qp] * phi_neighbor_face[i][qp] * phi_neighbor_face[j][qp];
704
705 // Kne Matrix. This is the "q_e" contribution from the [q][v] term with v = -phi^n_i,
706 // so there is a negative sign.
707 // (q_e - q_n) * -phi^n_i
708 for (auto i : make_range(n_neighbor_dofs))
709 for (auto j : make_range(n_dofs))
710 Kne(i,j) -= 0.5 * JxW_face[qp] * phi_neighbor_face[i][qp] * phi_face[j][qp];
711
712 // Ken Matrix. This is the "q_n" contribution from the [q][v] term with v = phi^e_i,
713 // so there is a negative sign.
714 // (q_e - q_n) * phi^e_i
715 for (auto i : make_range(n_dofs))
716 for (auto j : make_range(n_neighbor_dofs))
717 Ken(i,j) -= 0.5 * JxW_face[qp] * phi_face[i][qp] * phi_neighbor_face[j][qp];
718 }
719
720 // The element and neighbor boundary matrix are now built
721 // for this side. Add them to the global matrix
722 // The \p SparseMatrix::add_matrix() members do this for us.
723 _jump_matrix->add_matrix(Kne, neighbor_dof_indices, dof_indices);
724 _jump_matrix->add_matrix(Ken, dof_indices, neighbor_dof_indices);
725 _jump_matrix->add_matrix(Kee, dof_indices);
726 _jump_matrix->add_matrix(Knn, neighbor_dof_indices);
727 }
728 }
729 }
730 }
731
732 // Close the matrix now that we are done assembling it
733 _jump_matrix->close();
734}
735
737{
738 LOG_SCOPE("assemble_boundary_condition_matrices()", "ClawSystem");
739
740 // Zero all boundary condition matrices before filling
741 for (auto & mat : _boundary_condition_matrices)
742 mat->zero();
743
744 // Construct FE objects for the cell and side domains
745 const MeshBase & mesh = this->get_mesh();
746 const unsigned int mesh_dim = mesh.mesh_dimension();
747
748 // Check that the mesh dimension matches the number of boundary
749 // condition matrices we are going to assemble.
750 libmesh_error_msg_if(
751 mesh_dim != _boundary_condition_matrices.size(),
752 "Error: Expected number of boundary condition matrices to match mesh dimension.");
753
754 // Construct side FE object to be used for assembly
755 FEType fe_type = variable_type(0);
756 std::unique_ptr<FEBase> fe_elem_face = FEBase::build(mesh_dim, fe_type);
757 QGauss qface(mesh_dim-1, fe_type.default_quadrature_order());
758
759 // Tell the face finite element object to use our quadrature rule.
760 fe_elem_face->attach_quadrature_rule(&qface);
761
762 // Here we define some references to cell-specific data that
763 // will be used to assemble the linear system.
764 // Data for surface integrals on the element boundary
765 const std::vector<std::vector<Real>> & phi_face = fe_elem_face->get_phi();
766 const std::vector<Real> & JxW_face = fe_elem_face->get_JxW();
767 const std::vector<Point> & qface_normals = fe_elem_face->get_normals();
768
769 // We are going to build a separate element boundary condition matrix for each direction.
770 std::vector<DenseMatrix<Number>> K_vec(mesh_dim);
771
772 // DofMap reference and vector used for getting dof_indices on each Elem
773 const DofMap & dof_map = get_dof_map();
774 std::vector<dof_id_type> dof_indices;
775
776 for (const auto & elem : mesh.active_local_element_ptr_range())
777 {
778 dof_map.dof_indices (elem, dof_indices);
779 const unsigned int n_dofs = dof_indices.size();
780
781 for (auto side : elem->side_index_range())
782 {
783 // skip interior sides
784 if (elem->neighbor_ptr(side) == nullptr)
785 {
786 // Reinitialize shape functions on the element side
787 fe_elem_face->reinit(elem, side);
788
789 // Resize all the Elem boundary condition matrices for the current Elem
790 for (auto & Ke : K_vec)
791 Ke.resize(n_dofs, n_dofs);
792
793 // Now we will build the element and neighbor
794 // boundary matrices. This involves
795 // a double loop to integrate the test funcions
796 // (i) against the trial functions (j).
797 for (auto qp : make_range(qface.n_points()))
798 for (auto i : make_range(n_dofs))
799 for (auto j : make_range(n_dofs))
800 for (auto dim : make_range(mesh_dim))
801 K_vec[dim](i,j) += JxW_face[qp] * phi_face[i][qp] * phi_face[j][qp] * qface_normals[qp](dim);
802
803 // "Stamp" Elem boundary condition matrices into the global boundary condition matrices
804 for (auto dim : make_range(mesh_dim))
805 _boundary_condition_matrices[dim]->add_matrix(K_vec[dim], dof_indices);
806 }
807 }
808 }
809
810 // Close all boundary condition matrices now that we are done assembling
811 for (auto & mat : _boundary_condition_matrices)
812 mat->close();
813}
814
820
823{
824 libmesh_error_msg_if(
825 dim >= _advection_matrices.size(),
826 "Error: Invalid dimension " << dim << " requested.");
827
828 return *_advection_matrices[dim];
829}
830
833{
834 libmesh_error_msg_if(
835 dim >= _avg_matrices.size(),
836 "Error: Invalid dimension " << dim << " requested.");
837
838 return *_avg_matrices[dim];
839}
840
846
849{
850 libmesh_error_msg_if(
852 "Error: Invalid dimension " << dim << " requested.");
853
855}
856
857void
859{
860 _delta_t = delta_t_in;
861}
862
863Real
865{
866 return _delta_t;
867}
868
869void
870ClawSystem::set_n_time_steps(unsigned int n_time_steps_in)
871{
872 _n_time_steps = n_time_steps_in;
873}
874
875unsigned int
880
881void
883{
884 _LxF_constant = LxF_constant_in;
885}
886
887Real
892
893void
898
904
905void
906ClawSystem::set_write_interval(unsigned int write_interval_in)
907{
908 _write_interval = write_interval_in;
909}
910
911unsigned int
916
917void
919{
920 _time = time_in;
921}
922
923Real
925{
926 return _time;
927}
928
930{
931 _mass_matrix->print_matlab("mass_matrix.m");
932 _jump_matrix->print_matlab("jump_matrix.m");
933
934 for (auto i : index_range(_avg_matrices))
935 _avg_matrices[i]->print_matlab("avg_matrix_" + std::to_string(i+1) + ".m");
936
937 for (auto i : index_range(_advection_matrices))
938 _advection_matrices[i]->print_matlab("advection_matrix_" + std::to_string(i+1) + ".m");
939
941 _boundary_condition_matrices[i]->print_matlab("boundary_condition_matrix_" + std::to_string(i+1) + ".m");
942}
943
945{
947
948 // Allocate SparseMatrices before attaching them to the DofMap so
949 // that they all use the same sparsity pattern.
950 for (unsigned int i=0; i<2; ++i)
951 {
955 }
956
957 DofMap & dof_map = this->get_dof_map();
958
959 // Helper lambda function that attaches the input matrix to the
960 // DofMap, initializes it, and finally zeros it.
961 auto prepare_matrix = [&](SparseMatrix<Number> & mat)
962 {
963 dof_map.attach_matrix(mat);
964 mat.init();
965 mat.zero();
966 };
967
968 prepare_matrix(*_mass_matrix);
969
970 for (auto & mat : _advection_matrices)
971 prepare_matrix(*mat);
972
973 for (auto & mat : _avg_matrices)
974 prepare_matrix(*mat);
975
976 prepare_matrix(*_jump_matrix);
977
978 for (auto & mat : _boundary_condition_matrices)
979 prepare_matrix(*mat);
980}
981
982} // namespace libMesh
unsigned int dim
virtual std::string system_type() const override
Definition claw_system.C:61
static TemporalDiscretizationType string_to_enum(std::string string_type)
Convert between string and enum for TemporalDiscretizationType.
Definition claw_system.C:67
void assemble_mass_matrix()
Helper functions called by assemble_all_matrices().
void solve_conservation_law()
Solve the conservation law.
Definition claw_system.C:95
std::vector< std::unique_ptr< SparseMatrix< Number > > > _avg_matrices
The "flux average" element boundary matrices.
void assemble_advection_matrices()
unsigned int get_n_time_steps()
TemporalDiscretizationType
Define an enumeration for temporal discretization types ForwardEuler = 1st-order Explicit Euler RK4 =...
Definition claw_system.h:45
void set_n_time_steps(unsigned int n_time_steps_in)
Set/get the number of time-steps.
Real _time
The current time in the conservation law solve.
unsigned int get_write_interval()
std::vector< std::unique_ptr< SparseMatrix< Number > > > _advection_matrices
The "advection" matrices.
virtual ~ClawSystem()
Destructor.
std::vector< std::unique_ptr< SparseMatrix< Number > > > _boundary_condition_matrices
The "boundary condition" matrices.
void set_temporal_discretization_type(TemporalDiscretizationType td_in)
Set/get the temporal discretization type.
TemporalDiscretizationType _temporal_discretization_type
String that defines the type of temporal discretization that we use.
void assemble_all_matrices()
Assemble the matrices we need to solve a conservation law.
void set_LxF_constant(Real LxF_constant_in)
Set/get the Lax-Friedrichs constant.
SparseMatrix< Number > & get_jump_matrix()
void set_write_interval(unsigned int write_interval_in)
Set/get write_interval.
std::unique_ptr< SparseMatrix< Number > > _mass_matrix
The mass matrix.
SparseMatrix< Number > & get_advection_matrix(unsigned int dim)
void assemble_boundary_condition_matrices()
void write_out_discretization_matrices()
Print discretization matrices to file in MATLAB-readable format.
void set_time(Real time_in)
Set/get the current time in the conservation law solve.
void assemble_avg_coupling_matrices()
SparseMatrix< Number > & get_boundary_condition_matrix(unsigned int dim)
SparseMatrix< Number > & get_mass_matrix()
Get a reference to one of the discretization matrices.
void assemble_jump_coupling_matrix()
Real _delta_t
The time step size.
static std::string enum_to_string(TemporalDiscretizationType enum_type)
Definition claw_system.C:80
unsigned int _n_time_steps
The number of time steps.
void set_delta_t(Real delta_t_in)
Set/get the time-step size.
SparseMatrix< Number > & get_avg_matrix(unsigned int dim)
ClawSystem(EquationSystems &es, const std::string &name, const unsigned int number)
Constructor.
Definition claw_system.C:39
virtual void process_parameters_file(const std::string &parameters_filename)
Set parameters for this system (e.g.
unsigned int _write_interval
The time step interval between writing out solutions.
Real _LxF_constant
The constant C in the Lax-Friedrichs flux.
virtual void assemble_claw_rhs(NumericVector< Number > &)=0
Assemble the right-hand side vector.
virtual void init_data() override
Initialize the system (e.g.
virtual void print_info()
Print out some info about the system's configuration.
std::unique_ptr< SparseMatrix< Number > > _jump_matrix
The "jump" element boundary matrix.
TemporalDiscretizationType get_temporal_discretization_type()
Defines a dense matrix for use in Finite Element-type computations.
void resize(const unsigned int new_m, const unsigned int new_n)
Resizes the matrix to the specified size and calls zero().
This class handles the numbering of degrees of freedom on a mesh.
Definition dof_map.h:181
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
Definition dof_map.C:2201
void constrain_element_matrix(DenseMatrix< Number > &matrix, std::vector< dof_id_type > &elem_dofs, bool asymmetric_constraint_rows=true) const
Constrains the element matrix.
Definition dof_map.h:2485
void attach_matrix(SparseMatrix< Number > &matrix)
Additional matrices may be attached to this DofMap.
Definition dof_map.C:240
dof_id_type id() const
Definition dof_object.h:819
This is the base class from which all geometric element types are derived.
Definition elem.h:96
virtual unsigned short dim() const =0
const Elem * neighbor_ptr(unsigned int i) const
Definition elem.h:2615
This is the EquationSystems class.
The ExodusII_IO class implements reading meshes in the ExodusII file format from Sandia National Labs...
Definition exodusII_io.h:53
void write_timestep(const std::string &fname, const EquationSystems &es, const int timestep, const Real time, const std::set< std::string > *system_names=nullptr)
Writes out the solution at a specific timestep.
static std::unique_ptr< FEGenericBase > build(const unsigned int dim, const FEType &type)
Builds a specific finite element type.
static Point inverse_map(const unsigned int dim, const Elem *elem, const Point &p, const Real tolerance=TOLERANCE, const bool secure=true, const bool extra_checks=true)
Definition fe_map.C:1512
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition fe_type.h:197
Order default_quadrature_order() const
Definition fe_type.h:415
SparseMatrix< Number > * matrix
The system matrix.
std::unique_ptr< LinearSolver< Number > > linear_solver
This class handles all the details of interfacing with various linear algebra packages like PETSc or ...
Manages consistently variables, degrees of freedom, coefficient vectors, matrices and linear solvers ...
virtual void init_data() override
Initializes new data members of the system.
virtual void solve() override
Assembles & solves the linear system A*x=b.
This is the MeshBase class.
Definition mesh_base.h:81
static std::unique_ptr< NumericVector< T > > build(const Parallel::Communicator &comm, SolverPackage solver_package=libMesh::default_solver_package(), ParallelType parallel_type=AUTOMATIC)
Builds a NumericVector on the processors in communicator comm using the linear solver package specifi...
const Parallel::Communicator & comm() const
unsigned int n_points() const
Definition quadrature.h:131
This class implements specific orders of Gauss quadrature.
Generic sparse matrix.
virtual void close()=0
Calls the SparseMatrix's internal assembly routines, ensuring that the values are consistent across p...
virtual void zero()=0
Set all entries to 0.
virtual void add(const numeric_index_type i, const numeric_index_type j, const T value)=0
Add value to the element (i,j).
SparseMatrix< Number > & add_matrix(std::string_view mat_name, ParallelType type=PARALLEL, MatrixBuildType mat_build_type=MatrixBuildType::AUTOMATIC)
Adds the additional matrix mat_name to this system.
Definition system.C:998
const std::string & name() const
Definition system.h:2385
dof_id_type n_dofs() const
Definition system.C:118
const FEType & variable_type(const unsigned int i) const
Definition system.C:2721
bool assemble_before_solve
Flag which tells the system to whether or not to call the user assembly function during each call to ...
Definition system.h:1609
dof_id_type n_local_dofs() const
Definition system.C:155
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition system.h:1655
virtual void update()
Update the local values to reflect the solution on neighboring processors.
Definition system.C:498
const DofMap & get_dof_map() const
Definition system.h:2417
const MeshBase & get_mesh() const
Definition system.h:2401
MeshBase & mesh
The libMesh namespace provides an interface to certain functionality in the library.
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition int_range.h:153
OStreamProxy out
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition int_range.h:176