OGS
MatrixOutput.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#include "MatrixOutput.h"
5
6#include <spdlog/fmt/ostr.h>
7
8#include <iomanip>
9#include <optional>
10#include <unordered_set>
11
12#include "BaseLib/Error.h"
13#include "BaseLib/Logging.h"
16
17namespace
18{
19std::string getSeparatorAfterFilenamePrefix(std::string const& filenamePrefix)
20{
21 return filenamePrefix.empty() || filenamePrefix.ends_with('/') ||
22 filenamePrefix.ends_with('\\')
23 ? ""
24 : "_";
25}
26
27#ifndef USE_PETSC
28static void outputGlobalMatrix(GlobalMatrix const& mat, std::ostream& os)
29{
30 os << "(" << mat.getNumberOfRows() << " x " << mat.getNumberOfColumns()
31 << ")\n";
32 mat.write(os);
33}
34
35static void outputGlobalVector(GlobalVector const& vec, std::ostream& os)
36{
37 os << "(" << vec.size() << ")\n";
38 os << vec.getRawVector() << '\n';
39}
40
41std::ofstream openGlobalMatrixOutputFile(std::string const& filenamePrefix,
42 std::size_t const counter,
43 double const t, int const process_id,
44 std::string const& which_matrix,
45 std::string const& extension)
46{
47 auto const filename = fmt::format(
48 "{}{}ogs_global_matrix_cnt_{:03}_t_{:g}_pcs_{}_{}.{}", filenamePrefix,
49 getSeparatorAfterFilenamePrefix(filenamePrefix), counter, t, process_id,
50 which_matrix, extension);
51
52 std::ofstream fh{filename};
53
54 if (!fh)
55 {
56 OGS_FATAL("Could not open file `{}' for global matrix debug output",
57 filename);
58 }
59
60 fh << std::setprecision(std::numeric_limits<double>::max_digits10);
61 return fh;
62}
63#endif
64
65static std::optional<std::string> getEnvironmentVariable(
66 std::string const& env_var)
67{
68 char const* const prefix = std::getenv(env_var.c_str());
69 return prefix ? std::make_optional(prefix) : std::nullopt;
70}
71
72std::string localMatrixOutputFilename(std::string const& filenamePrefix)
73{
74 return fmt::format("{}{}ogs_local_matrix.log", filenamePrefix,
75 getSeparatorAfterFilenamePrefix(filenamePrefix));
76}
77
78Eigen::Map<const Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic,
79 Eigen::RowMajor>>
80toSquareMatrixRowMajor(std::vector<double> entries)
81{
82 auto const num_r_c =
83 static_cast<Eigen::Index>(std::round(std::sqrt(entries.size())));
84
85 return {entries.data(), num_r_c, num_r_c};
86}
87} // namespace
88
90{
91namespace detail
92{
93std::unordered_set<std::size_t> parseSetOfSizeT(std::string const& str,
94 std::string const& warn_msg)
95{
96 std::istringstream sstr{str};
97 std::unordered_set<std::size_t> result;
98 std::ptrdiff_t value;
99
100 while (sstr >> value)
101 {
102 [[likely]] if (value >= 0)
103 {
104 result.insert(value);
105 }
106 else
107 {
108 if (!warn_msg.empty())
109 {
110 WARN("{}", warn_msg);
111 }
112
113 return result;
114 }
115 }
116
117 if (!sstr.eof() && !warn_msg.empty()) // The stream is not read until
118 // the end, must be an error.
119 {
120 WARN("{}", warn_msg);
121 }
122
123 return result;
124}
125
126std::function<bool(std::size_t)> createLocalMatrixOutputElementPredicate(
127 std::string const& element_ids_str)
128{
129 if (element_ids_str.empty())
130 {
131 return {};
132 }
133
134 if (element_ids_str == "*")
135 {
136 return [](std::size_t) { return true; };
137 }
138
139 auto element_ids = parseSetOfSizeT(
140 element_ids_str,
141 "Error parsing list of element ids for local matrix debug "
142 "output. We'll try to proceed anyway, as best as we can.");
143
144 if (element_ids.empty())
145 {
146 return {};
147 }
148
149 return [element_ids = std::move(element_ids)](std::size_t element_id)
150 { return element_ids.contains(element_id); };
151}
152} // namespace detail
153
155{
156 auto opt_prefix = getEnvironmentVariable("OGS_GLOBAL_MAT_OUT_PREFIX");
157
158 if (opt_prefix.has_value())
159 {
160#ifndef USE_PETSC
161 do_output_ = true;
162 filenamePrefix_ = std::move(*opt_prefix);
163#else
164 // TODO implement. PETScMatrix's viewer() method could be used for that.
165 WARN(
166 "You requested global matrix output (OGS_GLOBAL_MAT_OUT_PREFIX is "
167 "set), which is not yet implemented for PETSc matrices.");
168#endif
169 }
170}
171
172void GlobalMatrixOutput::operator()(double const t, int const process_id,
173 GlobalMatrix const& M,
174 GlobalMatrix const& K,
175 GlobalVector const& b)
176{
177 if (!do_output_)
178 {
179 return;
180 }
181
182#ifndef USE_PETSC
183 ++counter_;
184
185 {
186 auto fh = openGlobalMatrixOutputFile(filenamePrefix_, counter_, t,
187 process_id, "M", "mat");
188
189 fh << "M ";
190 outputGlobalMatrix(M, fh);
191 }
192
193 {
194 auto fh = openGlobalMatrixOutputFile(filenamePrefix_, counter_, t,
195 process_id, "K", "mat");
196
197 fh << "K ";
198 outputGlobalMatrix(K, fh);
199 }
200
201 {
202 auto fh = openGlobalMatrixOutputFile(filenamePrefix_, counter_, t,
203 process_id, "b", "vec");
204
205 fh << "b ";
206 outputGlobalVector(b, fh);
207 }
208#else
209 // do nothing, warning message already printed in the constructor
210 (void)t;
211 (void)process_id;
212 (void)M;
213 (void)K;
214 (void)b;
215#endif
216}
217
218void GlobalMatrixOutput::operator()(double const t, int const process_id,
219 GlobalVector const& b,
220 GlobalMatrix const& Jac)
221{
222 if (!do_output_)
223 {
224 return;
225 }
226
227#ifndef USE_PETSC
228 ++counter_;
229
230 {
231 auto fh = openGlobalMatrixOutputFile(filenamePrefix_, counter_, t,
232 process_id, "b", "vec");
233
234 fh << "b ";
235 outputGlobalVector(b, fh);
236 }
237
238 {
239 auto fh = openGlobalMatrixOutputFile(filenamePrefix_, counter_, t,
240 process_id, "Jac", "mat");
241
242 fh << "Jac ";
243 outputGlobalMatrix(Jac, fh);
244 }
245#else
246 // do nothing, warning message already printed in the constructor
247 (void)t;
248 (void)process_id;
249 (void)b;
250 (void)Jac;
251#endif
252}
253
255{
256 auto const opt_prefix = getEnvironmentVariable("OGS_LOCAL_MAT_OUT_PREFIX");
257 auto const opt_elements =
258 getEnvironmentVariable("OGS_LOCAL_MAT_OUT_ELEMENTS");
259
260 if (!opt_prefix)
261 {
262 if (opt_elements)
263 {
264 WARN(
265 "Environment variable OGS_LOCAL_MAT_OUT_ELEMENTS is set, but "
266 "OGS_LOCAL_MAT_OUT_PREFIX is not. Local matrix debug output "
267 "will be disabled.");
268 }
269
270 return;
271 }
272 if (!opt_elements)
273 {
274 WARN(
275 "Environment variable OGS_LOCAL_MAT_OUT_PREFIX is set, but "
276 "OGS_LOCAL_MAT_OUT_ELEMENTS is not. Local matrix debug output "
277 "will be disabled.");
278 return;
279 }
280
283
285 {
286 WARN(
287 "Environment variable OGS_LOCAL_MAT_OUT_ELEMENTS not set "
288 "properly. Local matrix debug output will be disabled.");
289 return;
290 }
291
292 auto const outputFilename = localMatrixOutputFilename(*opt_prefix);
293 outputFile_.open(outputFilename);
294
295 if (!outputFile_)
296 {
297 OGS_FATAL(
298 "File `{}' for local matrix debug output could not be "
299 "opened.",
300 outputFilename);
301 }
302
303 DBUG("Successfully opened local matrix debug output file {}.",
304 outputFilename);
305}
306
307void LocalMatrixOutput::operator()(double const t, int const process_id,
308 std::size_t const element_id,
309 std::vector<double> const& local_M_data,
310 std::vector<double> const& local_K_data,
311 std::vector<double> const& local_b_data)
312{
313 [[likely]] if (!isOutputRequested(element_id))
314 {
315 return;
316 }
317
318 std::lock_guard lock_guard{mutex_};
319
320 auto& fh = outputFile_;
321
322 DBUG("Writing to local matrix debug output file...");
323
324 fmt::print(fh, "## t = {:.{}g}, process id = {}, element id = {}\n\n", t,
325 std::numeric_limits<double>::max_digits10, process_id,
326 element_id);
327
328 if (!local_M_data.empty())
329 {
330 DBUG("... M");
331 fmt::print(fh, "# M\n{}\n\n", toSquareMatrixRowMajor(local_M_data));
332 }
333
334 if (!local_K_data.empty())
335 {
336 DBUG("... K");
337 fmt::print(fh, "# K\n{}\n\n", toSquareMatrixRowMajor(local_K_data));
338 }
339
340 if (!local_b_data.empty())
341 {
342 DBUG("... b");
343 fmt::print(fh, "# b\n{}\n\n", MathLib::toVector(local_b_data));
344 }
345}
346
347void LocalMatrixOutput::operator()(double const t, int const process_id,
348 std::size_t const element_id,
349 std::vector<double> const& local_b_data,
350 std::vector<double> const& local_Jac_data)
351{
352 [[likely]] if (!isOutputRequested(element_id))
353 {
354 return;
355 }
356
357 std::lock_guard lock_guard{mutex_};
358
359 auto& fh = outputFile_;
360
361 DBUG("Writing to local matrix debug output file...");
362
363 fmt::print(fh, "## t = {:.{}g}, process id = {}, element id = {}\n\n", t,
364 std::numeric_limits<double>::max_digits10, process_id,
365 element_id);
366
367 if (!local_b_data.empty())
368 {
369 DBUG("... b");
370 fmt::print(fh, "# b\n{}\n\n", MathLib::toVector(local_b_data));
371 }
372
373 if (!local_Jac_data.empty())
374 {
375 DBUG("... Jac");
376 fmt::print(fh, "# Jac\n{}\n\n\n",
377 toSquareMatrixRowMajor(local_Jac_data));
378 }
379}
380
381bool LocalMatrixOutput::isOutputRequested(std::size_t const element_id) const
382{
384}
385
387} // namespace ProcessLib::Assembly
#define OGS_FATAL(...)
Definition Error.h:19
MathLib::EigenMatrix GlobalMatrix
MathLib::EigenVector GlobalVector
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:22
void WARN(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:34
IndexType getNumberOfColumns() const
return the number of columns
Definition EigenMatrix.h:49
void write(const std::string &filename) const
printout this matrix for debugging
IndexType getNumberOfRows() const
return the number of rows
Definition EigenMatrix.h:46
IndexType size() const
return a vector length
Definition EigenVector.h:36
RawVectorType & getRawVector()
return a raw Eigen vector object
Eigen::Map< const Vector > toVector(std::vector< double > const &data, Eigen::VectorXd::Index size)
Creates an Eigen mapped vector from the given data vector.
std::unordered_set< std::size_t > parseSetOfSizeT(std::string const &str, std::string const &warn_msg)
std::function< bool(std::size_t)> createLocalMatrixOutputElementPredicate(std::string const &element_ids_str)
std::string localMatrixOutputFilename(std::string const &filenamePrefix)
Eigen::Map< const Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > toSquareMatrixRowMajor(std::vector< double > entries)
static std::optional< std::string > getEnvironmentVariable(std::string const &env_var)
std::string getSeparatorAfterFilenamePrefix(std::string const &filenamePrefix)
void operator()(double const t, int const process_id, GlobalMatrix const &M, GlobalMatrix const &K, GlobalVector const &b)
bool isOutputRequested(std::size_t const element_id) const
std::function< bool(std::size_t)> output_element_predicate_
void operator()(double const t, int const process_id, std::size_t const element_id, std::vector< double > const &local_M_data, std::vector< double > const &local_K_data, std::vector< double > const &local_b_data)