libMesh
Loading...
Searching...
No Matches
calculator.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
19// Open the input mesh and corresponding solution file named in command line
20// arguments, parse the function specified in a command line argument,
21// Project its value onto the mesh, and output the new solution.
22
23
24// libMesh includes
25#include "L2system.h"
26#include "libmesh/libmesh.h"
27#include "libmesh/dof_map.h"
28#include "libmesh/enum_norm_type.h"
29#include "libmesh/equation_systems.h"
30#include "libmesh/getpot.h"
31#include "libmesh/mesh.h"
32#include "libmesh/mesh_tools.h"
33#include "libmesh/newton_solver.h"
34#include "libmesh/numeric_vector.h"
35#include "libmesh/parsed_fem_function.h"
36#include "libmesh/parsed_function.h"
37#include "libmesh/point.h"
38#include "libmesh/sparse_matrix.h"
39#include "libmesh/steady_solver.h"
40#include "libmesh/wrapped_functor.h"
41#include "libmesh/elem.h"
42#include "libmesh/parallel_implementation.h"
43#include "libmesh/string_to_enum.h"
44
45// For error integration
46#include "libmesh/error_vector.h"
47#include "libmesh/exact_solution.h"
48#include "libmesh/discontinuity_measure.h"
49#include "libmesh/kelly_error_estimator.h"
50#include "libmesh/fourth_error_estimators.h"
51
52// C++ includes
53#include <memory>
54#include <string>
55
56
57using namespace libMesh;
58
59
60// If there's a missing input argument, then print a help message
61void usage_error(const char * progname)
62{
63 libMesh::out << "Options: " << progname << '\n'
64 << " --dim d mesh dimension [default: autodetect]\n"
65 << " --inmesh filename input mesh file\n"
66 << " --inmat filename input constraint matrix [default: none]\n"
67 << " --mattol filename constraint tolerance when testing mesh connectivity\n"
68 << " [default: 0]\n"
69 << " --insoln filename input solution file\n"
70 << " --calc func function to calculate\n"
71 << " --insys sysnum input system number [default: 0]\n"
72 << " --outsoln filename output solution file [default: out_<insoln>]\n"
73 << " --family famname output FEM family [default: LAGRANGE]\n"
74 << " --order p output FEM order [default: 1]\n"
75 << " --subdomain \"sbd_ids\" each subdomain to check [default: all subdomains]\n"
76 << " --hilbert order Hilbert space to project in [default: 0 => H0]\n"
77 << " --fdm_eps eps Central diff for dfunc/dx [default: " << TOLERANCE << "]\n"
78 << " --error_q extra_q integrate projection error, with adjusted\n"
79 << " (extra) quadrature order [default: off, suggested: 0]\n"
80 << " --jump_error order calculate jump error indicator, for specified\n"
81 << " Hilbert order [default: off]\n"
82 << " --jump_slits calculate jumps across slits [default: off]\n"
83 << " --integral only calculate func integral, not projection\n"
84 << std::endl;
85
86 exit(1);
87}
88
89// Get an input argument, or print a help message if it's missing
90template <typename T>
91T assert_argument (const std::string & argname,
92 const char * progname,
93 const T & defaultarg)
94{
95 if (!libMesh::on_command_line(argname))
96 {
97 libMesh::err << ("No " + argname + " argument found!") << std::endl;
98 usage_error(progname);
99 }
100 return libMesh::command_line_next(argname, defaultarg);
101}
102
103
104struct Integrate {
105 Integrate (const System & sys,
106 const FEMFunctionBase<Number> & f) :
107 _sys(sys), _f(f.clone()), _integral(0) {}
108
110 _sys(other._sys), _f(other._f->clone()), _integral(0) {}
111
112 void operator() (const ConstElemRange & range) {
113 FEMContext context(_sys);
114
115 FEBase * elem_fe = nullptr;
116 context.get_element_fe(0, elem_fe);
117
118 const std::vector<Real> & JxW = elem_fe->get_JxW();
119 const std::vector<Point> & xyz = elem_fe->get_xyz();
120
121 _f->init_context(context);
122
123 // If _f didn't need anything, that wasn't a fluke
124 elem_fe->get_nothing();
125
126 // We only integrate on the highest dimensional elements in the
127 // mesh, lest we be adding apples to oranges
128 const unsigned int mesh_dim = _sys.get_mesh().mesh_dimension();
129
130 for (const auto & elem : range)
131 {
132 if (elem->dim() < mesh_dim)
133 continue;
134
135 context.pre_fe_reinit(_sys, elem);
136 context.elem_fe_reinit();
137
138 for (auto qp : index_range(JxW))
139 {
140 const Number output = (*_f)(context, xyz[qp]);
141
142 _integral += output * JxW[qp];
143 }
144 }
145 }
146
147 void join (const Integrate & other)
148 {
149 _integral += other._integral;
150 }
151
152 Number integral () const { return _integral; }
153
154private:
155 const System & _sys;
156
157 std::unique_ptr<FEMFunctionBase<Number>> _f;
158
160};
161
162
163int main(int argc, char ** argv)
164{
165 LibMeshInit init(argc, argv);
166
167 // In case the mesh file doesn't let us auto-infer dimension, we let
168 // the user specify it on the command line
169 const unsigned char requested_dim =
170 cast_int<unsigned char>(libMesh::command_line_next("--dim", 3));
171
172 // Load the old mesh from --inmesh filename.
173 // Keep it serialized; we don't want elements on the new mesh to be
174 // looking for data on old mesh elements that live off-processor.
175 Mesh old_mesh(init.comm(), requested_dim);
176
177 const std::string meshname =
178 assert_argument("--inmesh", argv[0], std::string(""));
179
180 libMesh::out << "Reading mesh " << meshname << std::endl;
181 old_mesh.read(meshname);
182
183 const std::string matname =
184 libMesh::command_line_next("--inmat", std::string(""));
185
186 if (matname != "")
187 {
188 libMesh::out << "Reading matrix " << matname << std::endl;
189
190 // For extraction matrices Coreform has been experimenting with
191 // PETSc solvers which take the transpose of what we expect, so
192 // we'll un-transpose here.
193 auto matrix = SparseMatrix<Number>::build (old_mesh.comm());
194 matrix->read(matname);
195 matrix->get_transpose(*matrix);
196
197 old_mesh.copy_constraint_rows(*matrix);
198 }
199
200 libMesh::out << "Mesh:" << std::endl;
201 old_mesh.print_info();
202
203 // If we're not using a distributed mesh, this is cheap info to add
204 if (old_mesh.is_serial_on_zero())
205 {
206 const Real mat_tol =
207 libMesh::command_line_next("--mattol", Real(0));
208
209 const dof_id_type n_components =
210 MeshTools::n_connected_components(old_mesh, mat_tol);
211 libMesh::out << "Mesh has " << n_components << " connected components." << std::endl;
212 }
213
214 const std::string solnname =
215 libMesh::command_line_next("--insoln", std::string(""));
216
217 // Load the old solution from --insoln filename, if that's been
218 // specified.
219 EquationSystems old_es(old_mesh);
220 std::string current_sys_name = "new_sys";
221
222 const std::string calcfunc =
223 assert_argument("--calc", argv[0], std::string(""));
224
225 const std::string family =
226 libMesh::command_line_next("--family", std::string("LAGRANGE"));
227
228 const unsigned int order = libMesh::command_line_next("--order", 1u);
229
230 std::unique_ptr<FEMFunctionBase<Number>> goal_function;
231
232 if (solnname != "")
233 {
234 libMesh::out << "Reading solution " << solnname << std::endl;
235
236 old_es.read(solnname,
241
242 libMesh::out << "Input system:\n\n";
243
244 old_es.print_info();
245
246 const unsigned int sysnum =
247 libMesh::command_line_next("--insys", 0);
248
249 libmesh_assert_less(sysnum, old_es.n_systems());
250
251 System & old_sys = old_es.get_system(sysnum);
252
253 current_sys_name = old_sys.name();
254
255 goal_function =
256 std::make_unique<ParsedFEMFunction<Number>>(old_sys, calcfunc);
257 }
258 else
259 {
260 current_sys_name = "bare_sys";
261
262 // FEMContext doesn't like seeing systems with no variables
263 System & dummy_sys = old_es.add_system<System>(current_sys_name);
264
265 // And if we're using an IsoGeometric Analysis mesh, we'd better
266 // make sure the user can specify order and family to make it
267 // iso.
268 dummy_sys.add_variable("dummy", static_cast<Order>(order),
269 Utility::string_to_enum<FEFamily>(family));
270
271 old_es.init();
272
273 goal_function =
274 std::make_unique<WrappedFunctor<Number>>(ParsedFunction<Number>(calcfunc));
275 }
276
277 libMesh::out << "Calculating with system " << current_sys_name << std::endl;
278
279 // Subdomains to integrate on
280 std::vector<libMesh::subdomain_id_type> subdomain_vec;
281 libMesh::command_line_vector("--subdomain", subdomain_vec);
282 std::set<libMesh::subdomain_id_type> subdomains_list(subdomain_vec.begin(),
283 subdomain_vec.end());
284
285 std::string default_outsolnname = "out_soln.xda";
286 if (solnname != "")
287 default_outsolnname = "out_"+solnname;
288 const std::string outsolnname =
289 libMesh::command_line_next("--outsoln", default_outsolnname);
290
291 // Output results in high precision
292 libMesh::out << std::setprecision(std::numeric_limits<Real>::max_digits10);
293
294 if (!libMesh::on_command_line("--integral"))
295 {
296 // Create a new mesh and a new EquationSystems.
297 // Copying the old mesh is faster than rereading it, and gets us
298 // any constraint rows from an input extraction operator matrix.
299 Mesh new_mesh(old_mesh);
300
301 EquationSystems new_es(new_mesh);
302
304
305 if (libMesh::on_command_line("--print_solutions"))
306 new_sys.print_solutions = true;
307
308 if (libMesh::on_command_line("--print_residuals"))
309 new_sys.print_residuals = true;
310
311 if (libMesh::on_command_line("--print_jacobians"))
312 new_sys.print_jacobians = true;
313
314 if (libMesh::on_command_line("--print_element_solutions"))
315 new_sys.print_element_solutions = true;
316
317 if (libMesh::on_command_line("--print_element_residuals"))
318 new_sys.print_element_residuals = true;
319
320 if (libMesh::on_command_line("--print_element_jacobians"))
321 new_sys.print_element_jacobians = true;
322
323 new_sys.hilbert_order() = libMesh::command_line_next("--hilbert", 0u);
324
325 new_sys.subdomains_list() = std::move(subdomains_list);
326
327 new_sys.time_solver =
328 std::make_unique<SteadySolver>(new_sys);
329
330 new_sys.fe_family() = family;
331 new_sys.fe_order() = order;
332
333 new_sys.set_goal_func(*goal_function);
334
335 const Real fdm_eps = libMesh::command_line_next("--fdm_eps", Real(TOLERANCE));
336
337 new_sys.set_fdm_eps(fdm_eps);
338
339 if (solnname != "")
340 new_sys.input_system = &old_es.get_system(current_sys_name);
341
342 new_es.init();
343
344 libMesh::out << "New system:\n\n";
345
346 new_es.print_info();
347
348 DiffSolver & solver = *(new_sys.time_solver->diff_solver().get());
349 solver.quiet = false;
350 solver.verbose = true;
351 solver.relative_step_tolerance = 1e-10;
352
353 new_sys.solve();
354
355 // Integrate the error if requested
356 if (libMesh::on_command_line("--error_q"))
357 {
358 const unsigned int error_q =
359 libMesh::command_line_next("--error_q", 0u);
360
361 // We just add "u" now but maybe we'll change that
362 for (auto v : make_range(new_sys.n_vars()))
363 {
364 ExactSolution exact_sol(new_es);
365 exact_sol.attach_exact_value(0, goal_function.get());
366 FDMGradient<Gradient> fdm_gradient(*goal_function, fdm_eps);
367 exact_sol.attach_exact_deriv(0, &fdm_gradient);
368 exact_sol.extra_quadrature_order(error_q);
369
370 const std::string var_name = new_sys.variable_name(v);
371 exact_sol.compute_error(current_sys_name, var_name);
372
373 libMesh::out << "L2 norm of " << var_name << ": " <<
374 new_sys.calculate_norm(*new_sys.solution, v, L2) <<
375 std::endl;
376
377 libMesh::out << "L2 error in " << var_name << ": " <<
378 exact_sol.l2_error(current_sys_name, var_name) <<
379 std::endl;
380
381 if (new_sys.hilbert_order() > 0)
382 {
383 libMesh::out << "H1 norm of " << var_name << ": " <<
384 new_sys.calculate_norm(*new_sys.solution, v, H1) <<
385 std::endl;
386
387 libMesh::out << "H1 error in " << var_name << ": " <<
388 exact_sol.h1_error(current_sys_name, var_name) <<
389 std::endl;
390 }
391 }
392 }
393
394 // Calculate the jump error indicator if requested
395 if (libMesh::on_command_line("--jump_error"))
396 {
397 const unsigned int jump_error_hilbert =
398 libMesh::command_line_next("--jump_error", 0u);
399
400 for (auto v : make_range(new_sys.n_vars()))
401 {
402 std::unique_ptr<JumpErrorEstimator> error_estimator;
403 SystemNorm error_norm;
404 error_norm.set_weight(0,0);
405 error_norm.set_weight(v+1,0);
406 error_norm.set_weight(v,1.0);
407
408 if (jump_error_hilbert == 0)
409 {
410 error_estimator = std::make_unique<DiscontinuityMeasure>();
411 error_norm.set_type(v, L2);
412 }
413 else if (jump_error_hilbert == 1)
414 {
415 error_estimator = std::make_unique<KellyErrorEstimator>();
416 error_norm.set_type(v, H1_SEMINORM);
417 }
418 else if (jump_error_hilbert == 2)
419 {
420 error_estimator = std::make_unique<LaplacianErrorEstimator>();
421 error_norm.set_type(v, H2_SEMINORM);
422 }
423 else
424 libmesh_not_implemented();
425
426 if (libMesh::on_command_line("--jump_slits"))
427 error_estimator->integrate_slits = true;
428
429 ErrorVector error_per_cell;
430 error_estimator->error_norm = error_norm;
431 error_estimator->estimate_error(new_sys, error_per_cell);
432
433 const std::string var_name = new_sys.variable_name(v);
434
435 // I really want to just stop supporting libstdc++-8.
436 // const Real total_error = std::reduce(error_per_cell.begin(), error_per_cell.end());
437
438 ErrorVectorReal total_error = 0;
439 for (auto cell_error : error_per_cell)
440 total_error += cell_error;
441
442 if (jump_error_hilbert == 0)
443 libMesh::out << "L2";
444 else
445 libMesh::out << "H" << jump_error_hilbert;
446
447 libMesh::out << " error estimate for " << var_name << ": " <<
448 total_error << std::endl;
449 }
450 }
451
452 // Write out the new solution file
453 new_es.write(outsolnname.c_str(),
456 libMesh::out << "Wrote solution " << outsolnname << std::endl;
457 }
458 else
459 {
460 // If we aren't doing a projection, then we just do an integral
461 System & old_sys = old_es.get_system<System>(current_sys_name);
462
463 ConstElemRange active_local_elem_range
464 (old_mesh.active_local_elements_begin(),
465 old_mesh.active_local_elements_end());
466
467 Integrate integrate(old_sys, *goal_function);
468
469 Threads::parallel_reduce (active_local_elem_range,
470 integrate);
471
472 Number integral = integrate.integral();
473 old_mesh.comm().sum(integral);
474 libMesh::out << "Integral is " << integral << std::endl;
475 }
476
477 return 0;
478}
void usage_error(const char *progname)
Definition calculator.C:61
T assert_argument(const std::string &argname, const char *progname, const T &defaultarg)
Definition calculator.C:91
std::set< libMesh::subdomain_id_type > & subdomains_list()
Definition L2system.h:51
libMesh::System * input_system
Definition L2system.h:71
void set_fdm_eps(libMesh::Real eps)
Definition L2system.h:54
void set_goal_func(libMesh::FEMFunctionBase< libMesh::Number > &goal)
Definition L2system.h:60
unsigned int & fe_order()
Definition L2system.h:50
std::string & fe_family()
Definition L2system.h:49
unsigned int & hilbert_order()
Definition L2system.h:53
This is a generic class that defines a solver to handle ImplicitSystem classes, including NonlinearIm...
Definition diff_solver.h:70
bool verbose
The DiffSolver may print a lot more to libMesh::out if verbose is set to true; default is false.
bool quiet
The DiffSolver should not print anything to libMesh::out unless quiet is set to false; default is tru...
bool print_jacobians
Set print_jacobians to true to print J whenever it is assembled.
bool print_residuals
Set print_residuals to true to print F whenever it is assembled.
bool print_element_jacobians
Set print_element_jacobians to true to print each J_elem contribution.
bool print_solutions
Set print_solutions to true to print U whenever it is used in an assembly() call.
bool print_element_solutions
Set print_element_solutions to true to print each U_elem input.
bool print_element_residuals
Set print_element_residuals to true to print each R_elem contribution.
std::unique_ptr< TimeSolver > time_solver
A pointer to the solver object we're going to use.
virtual bool is_serial_on_zero() const override final
This is the EquationSystems class.
void print_info(std::ostream &os=libMesh::out) const
Prints information about the equation systems, by default to libMesh::out.
void write(std::string_view name, const XdrMODE, const unsigned int write_flags=(WRITE_DATA), bool partition_agnostic=true) const
Write the systems to disk using the XDR data format.
unsigned int n_systems() const
void read(std::string_view name, const XdrMODE, const unsigned int read_flags=(READ_HEADER|READ_DATA), bool partition_agnostic=true)
Read & initialize the systems from disk using the XDR data format.
virtual void init()
Initialize all the systems.
virtual System & add_system(std::string_view system_type, std::string_view name)
Add the system of type system_type named name to the systems array.
const T_sys & get_system(std::string_view name) const
The ErrorVector is a specialization of the StatisticsVector for error data computed on a finite eleme...
This class handles the computation of the L2 and/or H1 error for the Systems in the EquationSystems o...
Real l2_error(std::string_view sys_name, std::string_view unknown_name)
Real h1_error(std::string_view sys_name, std::string_view unknown_name)
void attach_exact_deriv(unsigned int sys_num, FunctionBase< Gradient > *g)
Clone and attach an arbitrary functor which computes the exact gradient of the system sys_num solutio...
void attach_exact_value(unsigned int sys_num, FunctionBase< Number > *f)
Clone and attach an arbitrary functor which computes the exact value of the system sys_num solution a...
void compute_error(std::string_view sys_name, std::string_view unknown_name)
Computes and stores the error in the solution value e = u-u_h, the gradient grad(e) = grad(u) - grad(...
void extra_quadrature_order(const int extraorder)
Increases or decreases the order of the quadrature rule used for numerical integration.
void get_nothing() const
virtual_for_inffe const std::vector< Real > & get_JxW() const
virtual_for_inffe const std::vector< Point > & get_xyz() const
This class forms the foundation from which generic finite elements may be derived.
Definition fe_base.h:86
This class provides all data required for a physics package (e.g.
Definition fem_context.h:64
virtual void pre_fe_reinit(const System &, const Elem *e)
Reinitializes local data vectors/matrices on the current geometric element.
virtual void elem_fe_reinit(const std::vector< Point > *const pts=nullptr)
Reinitializes interior FE objects on the current geometric element.
void get_element_fe(unsigned int var, FEGenericBase< OutputShape > *&fe) const
Accessor for interior finite element object for variable var for the largest dimension in the mesh.
FEMFunctionBase is a base class from which users can derive in order to define "function-like" object...
virtual void solve() override
Invokes the solver associated with the system.
The LibMeshInit class, when constructed, initializes the dependent libraries (e.g.
Definition libmesh.h:92
unsigned int mesh_dimension() const
Definition mesh_base.C:435
void copy_constraint_rows(const MeshBase &other_mesh)
Copy the constraints from the other mesh to this mesh.
Definition mesh_base.C:2506
void print_info(std::ostream &os=libMesh::out, const unsigned int verbosity=0, const bool global=true) const
Prints relevant information about the mesh.
Definition mesh_base.C:1770
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition mesh.h:51
const Parallel::Communicator & comm() const
A Function generated (via FParser) by parsing a mathematical expression.
static std::unique_ptr< SparseMatrix< T > > build(const Parallel::Communicator &comm, const SolverPackage solver_package=libMesh::default_solver_package(), const MatrixBuildType matrix_build_type=MatrixBuildType::AUTOMATIC)
Builds a SparseMatrix<T> using the linear solver package specified by solver_package.
The StoredRange class defines a contiguous, divisible set of objects.
This class defines a norm/seminorm to be applied to a NumericVector which contains coefficients in a ...
Definition system_norm.h:50
void set_type(unsigned int var, const FEMNormType &t)
Sets the type of the norm in variable var, as well as for any unset variables with index less than va...
void set_weight(unsigned int var, Real w)
Sets the weight corresponding to the norm in variable var, as well as for any unset variables with in...
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition system.h:100
const std::string & name() const
Definition system.h:2385
unsigned int add_variable(std::string_view var, const FEType &type, const std::set< subdomain_id_type > *const active_subdomains=nullptr)
Adds the variable var to the list of variables for this system.
Definition system.C:1344
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition system.h:1655
const std::string & variable_name(const unsigned int i) const
Definition system.C:2679
Real calculate_norm(const NumericVector< Number > &v, unsigned int var, FEMNormType norm_type, std::set< unsigned int > *skip_dimensions=nullptr) const
Definition system.C:1511
unsigned int n_vars() const
Definition system.C:2674
const MeshBase & get_mesh() const
Definition system.h:2401
Dummy "splitting object" used to distinguish splitting constructors from copy constructors.
virtual void read(const std::string &name, void *mesh_data=nullptr, bool skip_renumber_nodes_and_elements=false, bool skip_find_neighbors=false, bool skip_detect_interior_parents=false) override
Reads the file specified by name.
dof_id_type n_connected_components(const MeshBase &mesh, Real constraint_tol=0)
Definition mesh_tools.C:864
void parallel_reduce(const Range &range, Body &body, unsigned int n_threads=libMesh::n_threads())
Execute the provided reduction operation in parallel on the specified range.
The libMesh namespace provides an interface to certain functionality in the library.
OStreamProxy err
DIE A HORRIBLE DEATH HERE typedef float ErrorVectorReal
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
void command_line_vector(const std::string &, std::vector< T > &)
Definition libmesh.C:1044
OStreamProxy out
T command_line_next(std::string name, T default_value)
Use GetPot's search()/next() functions to get following arguments from the command line.
Definition libmesh.C:1025
static constexpr Real TOLERANCE
uint8_t dof_id_type
Definition id_types.h:67
bool on_command_line(std::string arg)
Definition libmesh.C:934
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
std::string current_sys_name
Definition projection.C:77
Integrate(Integrate &other, Threads::split)
Definition calculator.C:109
std::unique_ptr< FEMFunctionBase< Number > > _f
Definition calculator.C:157
Number integral() const
Definition calculator.C:152
const System & _sys
Definition calculator.C:155
void operator()(const ConstElemRange &range)
Definition calculator.C:112
Number _integral
Definition calculator.C:159
Integrate(const System &sys, const FEMFunctionBase< Number > &f)
Definition calculator.C:105
void join(const Integrate &other)
Definition calculator.C:147
int main()