OGS
ProcessLib::CompareJacobiansJacobianAssembler Class Referencefinal

Detailed Description

Assembles the Jacobian matrix using two different Jacobian assemblers and compares the assembled local Jacobian matrices.

If the provided tolerances are exceeded, debugging information is logged in the form of a Python script.

Definition at line 24 of file CompareJacobiansJacobianAssembler.h.

#include <CompareJacobiansJacobianAssembler.h>

Inheritance diagram for ProcessLib::CompareJacobiansJacobianAssembler:
[legend]
Collaboration diagram for ProcessLib::CompareJacobiansJacobianAssembler:
[legend]

Public Member Functions

 CompareJacobiansJacobianAssembler (std::unique_ptr< AbstractJacobianAssembler > &&asm1, std::unique_ptr< AbstractJacobianAssembler > &&asm2, double abs_tol, double rel_tol, bool fail_on_error, std::string const &log_file_path)
void assembleWithJacobian (LocalAssemblerInterface &local_assembler, double const t, double const dt, std::vector< double > const &local_x, std::vector< double > const &local_x_prev, std::vector< double > &local_b_data, std::vector< double > &local_Jac_data) override
std::unique_ptr< AbstractJacobianAssemblercopy () const override
Public Member Functions inherited from ProcessLib::AbstractJacobianAssembler
 AbstractJacobianAssembler (std::vector< double > const &&absolute_epsilons)
 AbstractJacobianAssembler ()
virtual void assembleWithJacobianForStaggeredScheme (LocalAssemblerInterface &, double const, double const, Eigen::VectorXd const &, Eigen::VectorXd const &, int const, std::vector< double > &, std::vector< double > &)
virtual ~AbstractJacobianAssembler ()=default
void checkPerturbationSize (int const max_non_deformation_dofs_per_node) const
void setNonDeformationComponentIDs (std::vector< int > const &non_deformation_component_ids)
void setNonDeformationComponentIDsNoSizeCheck (std::vector< int > const &non_deformation_component_ids)
auto getVariableComponentEpsilonsView () const
auto isPerturbationEnabled () const

Private Attributes

std::unique_ptr< AbstractJacobianAssembler_asm1
std::unique_ptr< AbstractJacobianAssembler_asm2
double const _abs_tol
double const _rel_tol
bool const _fail_on_error
 Whether to abort if the tolerances are exceeded.
std::ofstream _log_file
std::size_t _counter = 0

Additional Inherited Members

Protected Attributes inherited from ProcessLib::AbstractJacobianAssembler
std::vector< int > non_deformation_component_ids_
std::vector< double > const absolute_epsilons_

Constructor & Destructor Documentation

◆ CompareJacobiansJacobianAssembler()

ProcessLib::CompareJacobiansJacobianAssembler::CompareJacobiansJacobianAssembler ( std::unique_ptr< AbstractJacobianAssembler > && asm1,
std::unique_ptr< AbstractJacobianAssembler > && asm2,
double abs_tol,
double rel_tol,
bool fail_on_error,
std::string const & log_file_path )
inline

Definition at line 27 of file CompareJacobiansJacobianAssembler.h.

34 : _asm1(std::move(asm1)),
35 _asm2(std::move(asm2)),
36 _abs_tol(abs_tol),
37 _rel_tol(rel_tol),
38 _fail_on_error(fail_on_error),
39 _log_file(log_file_path)
40 {
41 _log_file.precision(std::numeric_limits<double>::max_digits10);
42 _log_file << "#!/usr/bin/env python\n"
43 "import numpy as np\n"
44 "from numpy import nan\n"
45 << std::endl;
46 }
bool const _fail_on_error
Whether to abort if the tolerances are exceeded.
std::unique_ptr< AbstractJacobianAssembler > _asm2
std::unique_ptr< AbstractJacobianAssembler > _asm1

References _abs_tol, _asm1, _asm2, _fail_on_error, _log_file, and _rel_tol.

Member Function Documentation

◆ assembleWithJacobian()

void ProcessLib::CompareJacobiansJacobianAssembler::assembleWithJacobian ( LocalAssemblerInterface & local_assembler,
double const t,
double const dt,
std::vector< double > const & local_x,
std::vector< double > const & local_x_prev,
std::vector< double > & local_b_data,
std::vector< double > & local_Jac_data )
overridevirtual

Assembles the Jacobian, the matrices \(M\) and \(K\), and the vector \(b\).

Implements ProcessLib::AbstractJacobianAssembler.

Definition at line 120 of file CompareJacobiansJacobianAssembler.cpp.

124{
125 ++_counter;
126
127 auto const num_dof = local_x.size();
128
129 // First assembly -- the one whose results will be added to the global
130 // equation system finally.
131 _asm1->assembleWithJacobian(local_assembler, t, dt, local_x, local_x_prev,
132 local_b_data, local_Jac_data);
133
134 auto const local_b1 = MathLib::toVector(local_b_data);
135
136 std::vector<double> local_b_data2;
137 std::vector<double> local_Jac_data2;
138
139 // Second assembly -- used for checking only.
140 _asm2->assembleWithJacobian(local_assembler, t, dt, local_x, local_x_prev,
141 local_b_data2, local_Jac_data2);
142
143 auto const local_b2 = MathLib::toVector(local_b_data2);
144
145 auto const local_Jac1 = MathLib::toMatrix(local_Jac_data, num_dof, num_dof);
146 auto const local_Jac2 =
147 MathLib::toMatrix(local_Jac_data2, num_dof, num_dof);
148
149 auto const abs_diff = (local_Jac2 - local_Jac1).array().eval();
150 auto const rel_diff =
151 (abs_diff == 0.0)
152 .select(abs_diff,
153 2. * abs_diff /
154 (local_Jac1.cwiseAbs() + local_Jac2.cwiseAbs()).array())
155 .eval();
156
157 auto const abs_diff_mask =
158 (abs_diff.abs() <= _abs_tol)
159 .select(decltype(abs_diff)::Zero(abs_diff.rows(), abs_diff.cols()),
160 decltype(abs_diff)::Ones(abs_diff.rows(), abs_diff.cols()))
161 .eval();
162 auto const rel_diff_mask =
163 (rel_diff.abs() <= _rel_tol)
164 .select(decltype(rel_diff)::Zero(rel_diff.rows(), rel_diff.cols()),
165 decltype(rel_diff)::Ones(rel_diff.rows(), rel_diff.cols()))
166 .eval();
167
168 auto const abs_diff_OK = !abs_diff_mask.any();
169 auto const rel_diff_OK = !rel_diff_mask.any();
170
171 std::ostringstream msg_tolerance;
172 bool tol_exceeded = true;
173 bool fatal_error = false;
174
175 if (abs_diff_OK)
176 {
177 tol_exceeded = false;
178 }
179 else
180 {
181 msg_tolerance << "absolute tolerance of " << _abs_tol << " exceeded";
182 }
183
184 if (rel_diff_OK)
185 {
186 tol_exceeded = false;
187 }
188 else
189 {
190 if (!msg_tolerance.str().empty())
191 {
192 msg_tolerance << " and ";
193 }
194
195 msg_tolerance << "relative tolerance of " << _rel_tol << " exceeded";
196 }
197
198 // basic consistency check if something went terribly wrong
199 auto check_equality = [&fatal_error](auto mat_or_vec1, auto mat_or_vec2)
200 {
201 if (mat_or_vec1.size() == 0 || mat_or_vec2.size() == 0)
202 {
203 return;
204 }
205 if (mat_or_vec1.rows() != mat_or_vec2.rows() ||
206 mat_or_vec1.cols() != mat_or_vec2.cols())
207 {
208 fatal_error = true;
209 }
210 else if (((mat_or_vec1 - mat_or_vec2).array().cwiseAbs() >
211 std::numeric_limits<double>::epsilon())
212 .any())
213 {
214 fatal_error = true;
215 }
216 };
217
218 check_equality(local_b1, local_b2);
219
220 Eigen::VectorXd res1 = Eigen::VectorXd::Zero(num_dof);
221 auto const x = MathLib::toVector(local_x);
222 auto const x_dot = ((x - MathLib::toVector(local_x_prev)) / dt).eval();
223 if (local_b1.size() != 0)
224 {
225 res1.noalias() -= local_b1;
226 }
227
228 Eigen::VectorXd res2 = Eigen::VectorXd::Zero(num_dof);
229 if (local_b2.size() != 0)
230 {
231 res2.noalias() -= local_b2;
232 }
233
234 check_equality(res1, res2);
235
236 if (tol_exceeded)
237 {
238 WARN("Compare Jacobians: {:s}", msg_tolerance.str());
239 }
240
241 bool const output = tol_exceeded || fatal_error;
242
243 if (output)
244 {
245 _log_file << "\n### counter: " << std::to_string(_counter)
246 << " (begin)\n";
247 }
248
249 if (fatal_error)
250 {
251 _log_file << '\n'
252 << "#######################################################\n"
253 << "# FATAL ERROR: " << msg_fatal << '\n'
254 << "# You cannot expect any meaningful insights "
255 "from the Jacobian data printed below!\n"
256 << "# The reason for the mentioned differences "
257 "might be\n"
258 << "# (a) that the assembly routine has side "
259 "effects or\n"
260 << "# (b) that the assembly routines for b "
261 "themselves differ.\n"
262 << "#######################################################\n"
263 << '\n';
264 }
265
266 if (tol_exceeded)
267 {
268 _log_file << "# " << msg_tolerance.str() << "\n\n";
269 }
270
271 if (output)
272 {
273 dump_py(_log_file, "counter", _counter);
274 dump_py(_log_file, "num_dof", num_dof);
275 dump_py(_log_file, "abs_tol", _abs_tol);
276 dump_py(_log_file, "rel_tol", _rel_tol);
277
278 _log_file << '\n';
279
280 dump_py(_log_file, "local_x", local_x);
281 dump_py(_log_file, "local_x_prev", local_x_prev);
282 dump_py(_log_file, "dt", dt);
283
284 _log_file << '\n';
285
286 dump_py(_log_file, "Jacobian_1", local_Jac1);
287 dump_py(_log_file, "Jacobian_2", local_Jac2);
288
289 _log_file << '\n';
290
291 _log_file << "# Jacobian_2 - Jacobian_1\n";
292 dump_py(_log_file, "abs_diff", abs_diff);
293 _log_file << "# Componentwise: 2 * abs_diff / (|Jacobian_1| + "
294 "|Jacobian_2|)\n";
295 dump_py(_log_file, "rel_diff", rel_diff);
296
297 _log_file << '\n';
298
299 _log_file << "# Masks: 0 ... tolerance met, 1 ... tolerance exceeded\n";
300 dump_py(_log_file, "abs_diff_mask", abs_diff_mask);
301 dump_py(_log_file, "rel_diff_mask", rel_diff_mask);
302
303 _log_file << '\n';
304
305 dump_py(_log_file, "b_1", local_b_data);
306 dump_py(_log_file, "b_2", local_b_data2);
307 if (fatal_error && local_b1.size() == local_b2.size())
308 {
309 dump_py(_log_file, "delta_b", local_b2 - local_b1);
310 _log_file << '\n';
311 }
312
313 dump_py(_log_file, "res_1", res1);
314 dump_py(_log_file, "res_2", res2);
315 if (fatal_error)
316 {
317 dump_py(_log_file, "delta_res", res2 - res1);
318 }
319
320 _log_file << '\n';
321
322 _log_file << "### counter: " << std::to_string(_counter) << " (end)\n";
323 }
324
325 if (fatal_error)
326 {
327 _log_file << std::flush;
328 OGS_FATAL("{:s}", msg_fatal);
329 }
330
331 if (tol_exceeded && _fail_on_error)
332 {
333 _log_file << std::flush;
334 OGS_FATAL(
335 "OGS failed, because the two Jacobian implementations returned "
336 "different results.");
337 }
338}
#define OGS_FATAL(...)
Definition Error.h:19
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:34
Eigen::Map< const Vector > toVector(std::vector< double > const &data, Eigen::VectorXd::Index size)
Creates an Eigen mapped vector from the given data vector.
Eigen::Map< const Matrix > toMatrix(std::vector< double > const &data, Eigen::MatrixXd::Index rows, Eigen::MatrixXd::Index cols)
auto eval(Function &f, Tuples &... ts) -> typename detail::GetFunctionReturnType< decltype(&Function::eval)>::type
Definition Apply.h:268
void dump_py(std::ostream &fh, std::string const &var, double const val)
Dumps a double value as a Python script snippet.
const std::string msg_fatal
Will be printed if some consistency error is detected.

References _abs_tol, _asm1, _asm2, _counter, _fail_on_error, _log_file, _rel_tol, OGS_FATAL, MathLib::toMatrix(), MathLib::toVector(), and WARN().

◆ copy()

std::unique_ptr< AbstractJacobianAssembler > ProcessLib::CompareJacobiansJacobianAssembler::copy ( ) const
overridevirtual

Implements ProcessLib::AbstractJacobianAssembler.

Definition at line 341 of file CompareJacobiansJacobianAssembler.cpp.

342{
343 OGS_FATAL(
344 "CompareJacobiansJacobianAssembler should not be copied. This class "
345 "logs to a file, which would most certainly break after copying "
346 "(concurrent file access) with the current implementation.");
347}

References OGS_FATAL.

Member Data Documentation

◆ _abs_tol

double const ProcessLib::CompareJacobiansJacobianAssembler::_abs_tol
private

◆ _asm1

std::unique_ptr<AbstractJacobianAssembler> ProcessLib::CompareJacobiansJacobianAssembler::_asm1
private

◆ _asm2

std::unique_ptr<AbstractJacobianAssembler> ProcessLib::CompareJacobiansJacobianAssembler::_asm2
private

◆ _counter

std::size_t ProcessLib::CompareJacobiansJacobianAssembler::_counter = 0
private

Counter used for identifying blocks in the _log_file. It is incremented upon each call of the assembly routine, i.e., for each element in each iteration etc.

Definition at line 75 of file CompareJacobiansJacobianAssembler.h.

Referenced by assembleWithJacobian().

◆ _fail_on_error

bool const ProcessLib::CompareJacobiansJacobianAssembler::_fail_on_error
private

Whether to abort if the tolerances are exceeded.

Definition at line 66 of file CompareJacobiansJacobianAssembler.h.

Referenced by CompareJacobiansJacobianAssembler(), and assembleWithJacobian().

◆ _log_file

std::ofstream ProcessLib::CompareJacobiansJacobianAssembler::_log_file
private

Path where a Python script will be placed, which contains information about exceeded tolerances and assembled local matrices.

Definition at line 70 of file CompareJacobiansJacobianAssembler.h.

Referenced by CompareJacobiansJacobianAssembler(), and assembleWithJacobian().

◆ _rel_tol

double const ProcessLib::CompareJacobiansJacobianAssembler::_rel_tol
private

The documentation for this class was generated from the following files: