OGS
ProcessLib/Output/Output.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 "Output.h"
5
6#include <cassert>
7#include <exception>
8#include <fstream>
9#include <range/v3/algorithm/transform.hpp>
10#include <range/v3/range/conversion.hpp>
11#include <vector>
12
15#include "BaseLib/FileTools.h"
16#include "BaseLib/Logging.h"
17#include "BaseLib/RunTime.h"
18#include "MeshLib/Mesh.h"
21#include "ProcessLib/Process.h"
22
23namespace ProcessLib
24{
26 MeshLib::Mesh& sub_mesh,
27 std::string const& property_name)
28{
29 if (bulk_mesh == sub_mesh)
30 {
31 return;
32 }
33
34 if (!bulk_mesh.getProperties().existsPropertyVector<double>(
35 std::string_view(property_name)))
36 {
37 return;
38 }
39 auto const& bulk_mesh_property =
40 *bulk_mesh.getProperties().getPropertyVector<double>(
41 std::string_view(property_name));
42 auto const mesh_item_type = bulk_mesh_property.getMeshItemType();
43
44 std::string_view mesh_item_type_string =
45 [mesh_item_type, &property_name]() -> std::string_view
46 {
47 switch (mesh_item_type)
48 {
51 break;
54 break;
56 WARN(
57 "Property '{}' is assigned to edges. But mappings from the "
58 "bulk edges to submesh edges isn't implemented. Mesh item "
59 "type 'Edge' is not supported, only 'Node' and 'Cell' are "
60 "implemented at the moment.",
61 property_name);
62 return "";
63 break;
65 WARN(
66 "Property '{}' is assigned to faces. But mappings from the "
67 "bulk faces to submesh faces isn't implemented. Mesh item "
68 "type 'Face' is not supported, only 'Node' and 'Cell' are "
69 "implemented at the moment.",
70 property_name);
71 return "";
72 break;
74 WARN(
75 "Property '{}' is assigned to integration points. But "
76 "mappings from the bulk integration points to submesh "
77 "integration points isn't implemented. Mesh item type "
78 "'IntegrationPoint' is not supported, only 'Node' and "
79 "'Cell' are implemented at the moment.",
80 property_name);
81 return "";
82 break;
83 default:
84 OGS_FATAL("Unknown mesh item type '{}'.",
85 static_cast<int>(mesh_item_type));
86 return "";
87 }
88 }();
89
90 if (mesh_item_type_string.empty())
91 {
92 return;
93 }
94 if (!sub_mesh.getProperties().existsPropertyVector<std::size_t>(
95 mesh_item_type_string, mesh_item_type, 1))
96 {
97 WARN(
98 "The property {} is required for output on the mesh {}, but it "
99 "doesn't exist.",
100 mesh_item_type_string, sub_mesh.getName());
101 return;
102 }
103
104 auto const& bulk_ids =
105 *sub_mesh.getProperties().getPropertyVector<std::size_t>(
106 mesh_item_type_string);
107
108 auto const number_of_components =
109 bulk_mesh_property.getNumberOfGlobalComponents();
110 auto& sub_mesh_property = *MeshLib::getOrCreateMeshProperty<double>(
111 sub_mesh, property_name, mesh_item_type, number_of_components);
112
113 for (std::size_t sub_mesh_node_id = 0;
114 sub_mesh_node_id < bulk_ids.getNumberOfTuples();
115 ++sub_mesh_node_id)
116 {
117 auto const& bulk_id = bulk_ids[sub_mesh_node_id];
118 for (std::remove_cv_t<decltype(number_of_components)> c = 0;
119 c < number_of_components;
120 ++c)
121 {
122 sub_mesh_property.getComponent(sub_mesh_node_id, c) =
123 bulk_mesh_property.getComponent(bulk_id, c);
124 }
125 }
126}
127
128bool Output::isOutputProcess(const int process_id, const Process& process) const
129{
130 auto const is_last_process =
131 process_id == static_cast<int>(_output_processes.size()) - 1;
132
133 return process.isMonolithicSchemeUsed()
134 // For the staggered scheme for the coupling, only the last
135 // process, which gives the latest solution within a coupling
136 // loop, is allowed to make output.
137 || is_last_process;
138}
139
140Output::Output(std::unique_ptr<OutputFormat>&& output_format,
141 bool const output_nonlinear_iteration_results,
142 OutputDataSpecification&& output_data_specification,
143 std::vector<std::string>&& mesh_names_for_output,
144 std::vector<std::unique_ptr<MeshLib::Mesh>> const& meshes)
145 : _output_format(std::move(output_format)),
146 _output_nonlinear_iteration_results(output_nonlinear_iteration_results),
147 _output_data_specification(std::move(output_data_specification)),
148 _mesh_names_for_output(std::move(mesh_names_for_output)),
149 _meshes(meshes)
150{
151}
152
154{
155 _output_processes.push_back(process);
156 if (_mesh_names_for_output.empty())
157 {
158 _mesh_names_for_output.push_back(process.getMesh().getName());
159 }
160}
161
163 std::string const& property_name,
164 MeshLib::MeshItemType const mesh_item_type)
165{
167 mesh_item_type);
168}
169
171 const int timestep, const double t, const int iteration,
172 bool const converged,
173 std::vector<std::reference_wrapper<const MeshLib::Mesh>> const& meshes)
174 const
175{
176 // copy
177 auto output_variable_names = _output_data_specification.output_variables;
178 if (output_variable_names.empty())
179 {
180 // special case: no output properties specified => output all properties
181 for (auto const& mesh : meshes)
182 {
183 for (auto [name, property] : mesh.get().getProperties())
184 {
185 output_variable_names.insert(name);
186 }
187 }
188 }
189 else
190 {
191 for (auto const& mesh : meshes)
192 {
193 for (auto [name, property] : mesh.get().getProperties())
194 {
195 // special case: always output OGS_VERSION
196 if (name == "OGS_VERSION")
197 {
198 output_variable_names.insert(name);
199 continue;
200 }
201 }
202 }
203 }
204 _output_format->outputMeshes(timestep, t, iteration, converged, meshes,
205 output_variable_names);
206}
207
209 std::string const& submesh_output_name, Process const& process,
210 const int process_id, NumLib::Time const& t,
211 std::vector<GlobalVector*> const& xs) const
212{
213 auto& submesh = MeshLib::findMeshByName(_meshes.get(), submesh_output_name);
214
215 DBUG("Found {:d} nodes for output at mesh '{:s}'.",
216 submesh.getNumberOfNodes(), submesh.getName());
217
218 bool const output_secondary_variables = false;
219
220 // TODO Under the assumption that xs.size() and submesh do not change during
221 // the simulation, process output data should not be recreated every time,
222 // but should rather be computed only once and stored for later reuse.
223 auto const process_output_data =
224 createProcessOutputData(process, xs.size(), submesh);
225
226 addProcessDataToMesh(t, xs, process_id, process_output_data,
227 output_secondary_variables,
229
230 auto const& bulk_mesh = process.getMesh();
231 auto const& property_names =
233
234 // TODO Once all processes have been refactored to use the new residuum
235 // assembly logic, the functionality of this lambda should be refactored.
236 // Currently (Jan '23) there is a difference between the logic using this
237 // lambda and doNotProjectFromBulkMeshToSubmeshes(): The latter is
238 // considered regardless of submesh dimension.
239 auto is_residuum_field = [](std::string const& name) -> bool
240 {
241 using namespace std::literals::string_view_literals;
242 static constexpr std::string_view endings[] = {
243 "FlowRate"sv, "heat_flux"sv, "MaterialForces"sv, "NodalForces"sv,
244 "NodalForcesJump"sv};
245 auto ends_with = [&](std::string_view const& ending)
246 { return name.ends_with(ending); };
247 return std::find_if(std::begin(endings), std::end(endings),
248 ends_with) != std::end(endings);
249 };
250
251 for (auto const& name : property_names)
252 {
254 {name, MeshLib::MeshItemType::Node}))
255 {
256 // the projection is disabled regardless of mesh and submesh
257 // dimension
258 continue;
259 }
260
261 if (bulk_mesh.getDimension() == submesh.getDimension())
262 {
263 // omit the 'simple' transfer of the properties in the if condition
264 // on submeshes with equal dimension to the bulk mesh
265 // for those data extra assembly is required
266 if (is_residuum_field(name))
267 {
268 continue;
269 }
270 addBulkMeshPropertyToSubMesh(bulk_mesh, submesh, name);
271 }
272 else
273 {
274 // For residuum based properties it is assumed that the lower
275 // dimensional mesh is a boundary mesh!
276 addBulkMeshPropertyToSubMesh(bulk_mesh, submesh, name);
277 }
278 }
279 return submesh;
280}
281
282void Output::doOutputAlways(Process const& process,
283 const int process_id,
284 int const timestep,
285 const NumLib::Time& t,
286 int const iteration,
287 bool const converged,
288 std::vector<GlobalVector*> const& xs) const
289{
290 BaseLib::RunTime time_output;
291 time_output.start();
292
293 bool const output_secondary_variables = true;
294 auto const process_output_data =
295 createProcessOutputData(process, xs.size(), process.getMesh());
296
297 // Need to add variables of process to mesh even if no output takes place.
298 addProcessDataToMesh(t, xs, process_id, process_output_data,
299 output_secondary_variables,
301
302 if (!isOutputProcess(process_id, process))
303 {
304 return;
305 }
306
307 std::vector<std::reference_wrapper<const MeshLib::Mesh>> output_meshes;
308 for (auto const& mesh_output_name : _mesh_names_for_output)
309 {
310 if (process.getMesh().getName() == mesh_output_name)
311 {
312 // process related output
313 output_meshes.emplace_back(process.getMesh());
314 }
315 else
316 {
317 // mesh related output
318 auto const& submesh =
319 prepareSubmesh(mesh_output_name, process, process_id, t, xs);
320 output_meshes.emplace_back(submesh);
321 }
322 }
323
324 outputMeshes(timestep, t(), iteration, converged, std::move(output_meshes));
325
326 INFO("[time] Output of timestep {:d} took {:g} s.", timestep,
327 time_output.elapsed());
328}
329
330void Output::doOutput(Process const& process,
331 const int process_id,
332 int const timestep,
333 const NumLib::Time& t,
334 int const iteration,
335 bool const converged,
336 std::vector<GlobalVector*> const& xs) const
337{
338 if (isOutputStep(timestep, t))
339 {
340 doOutputAlways(process, process_id, timestep, t, iteration, converged,
341 xs);
342 }
343#ifdef OGS_USE_INSITU
344 // Note: last time step may be output twice: here and in
345 // doOutputLastTimestep() which throws a warning.
346 InSituLib::CoProcess(process.getMesh(), t, timestep, false,
347 _output_format->directory);
348#endif
349}
350
352 const int process_id,
353 int const timestep,
354 const NumLib::Time& t,
355 int const iteration,
356 bool const converged,
357 std::vector<GlobalVector*> const& xs) const
358{
359 if (!isOutputStep(timestep, t))
360 {
361 doOutputAlways(process, process_id, timestep, t, iteration, converged,
362 xs);
363 }
364#ifdef OGS_USE_INSITU
365 InSituLib::CoProcess(process.getMesh(), t, timestep, true,
366 _output_format->directory);
367#endif
368}
369
371 Process const& process, const int process_id, int const timestep,
372 const NumLib::Time& t, int const iteration, bool const converged,
373 std::vector<GlobalVector*> const& xs) const
374{
376 {
377 return;
378 }
379
380 BaseLib::RunTime time_output;
381 time_output.start();
382
383 bool const output_secondary_variable = true;
384 auto const process_output_data =
385 createProcessOutputData(process, xs.size(), process.getMesh());
386
387 addProcessDataToMesh(t, xs, process_id, process_output_data,
388 output_secondary_variable, _output_data_specification);
389
390 if (!isOutputProcess(process_id, process))
391 {
392 return;
393 }
394
395 std::string const output_file_name = _output_format->constructFilename(
396 process.getMesh().getName(), timestep, t(), iteration, converged);
397
398 std::string const output_file_path =
399 BaseLib::joinPaths(_output_format->directory, output_file_name);
400
401 DBUG("output iteration results to {:s}", output_file_path);
402
403 if (dynamic_cast<OutputVTKFormat*>(_output_format.get()))
404 {
406 output_file_path, process.getMesh(),
407 _output_data_specification.output_variables,
408 _output_format->compression,
409 dynamic_cast<OutputVTKFormat*>(_output_format.get())->data_mode);
410 }
411 else
412 {
413 DBUG("non-linear iterations can only written in Vtk/VTU format.");
414 }
415 INFO("[time] Output took {:g} s.", time_output.elapsed());
416}
417
418bool Output::isOutputStep(int const timestep, NumLib::Time const& t) const
419{
420 return _output_data_specification.isOutputStep(timestep, t);
421}
422
423std::vector<std::string> Output::getFileNamesForOutput() const
424{
425 auto construct_filename = ranges::views::transform(
426 [&](auto const& output_name)
427 {
428 return _output_format->constructFilename(output_name, 0, 0, 0,
429 true);
430 });
431
432 return _mesh_names_for_output | construct_filename |
433 ranges::to<std::vector>;
434}
435
437 std::vector<Output> const& outputs)
438{
439 std::vector<double> fixed_times;
440 for (auto const& output : outputs)
441 {
442 auto const& output_fixed_times = output.getFixedOutputTimes();
443 fixed_times.insert(fixed_times.end(), output_fixed_times.begin(),
444 output_fixed_times.end());
445 }
446 BaseLib::makeVectorUnique(fixed_times);
447 return fixed_times;
448}
449
450std::ostream& operator<<(std::ostream& os, Output const& output)
451{
452 os << "Output::_output_data_specification:\t"
454 os << "Output::_output_format:\t" << *(output._output_format);
455 return os;
456}
457
458} // namespace ProcessLib
#define OGS_FATAL(...)
Definition Error.h:19
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:28
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
Count the running time.
Definition RunTime.h:18
double elapsed() const
Get the elapsed time in seconds.
Definition RunTime.h:31
void start()
Start the timer.
Definition RunTime.h:21
Properties & getProperties()
Definition Mesh.h:127
const std::string getName() const
Get name of the mesh.
Definition Mesh.h:95
std::vector< std::string > getPropertyVectorNames() const
bool existsPropertyVector(std::string_view name) const
PropertyVector< T > const * getPropertyVector(std::string_view name) const
MeshItemType getMeshItemType() const
int getNumberOfGlobalComponents() const
std::set< std::pair< std::string, MeshLib::MeshItemType > > _do_not_project_from_bulk_mesh_to_submeshes
std::reference_wrapper< std::vector< std::unique_ptr< MeshLib::Mesh > > const > _meshes
void doOutputAlways(Process const &process, const int process_id, int const timestep, const NumLib::Time &t, int const iteration, bool const converged, std::vector< GlobalVector * > const &xs) const
void doOutput(Process const &process, const int process_id, int const timestep, const NumLib::Time &t, int const iteration, bool const converged, std::vector< GlobalVector * > const &xs) const
void doOutputNonlinearIteration(Process const &process, const int process_id, int const timestep, const NumLib::Time &t, const int iteration, bool const converged, std::vector< GlobalVector * > const &xs) const
std::vector< std::string > _mesh_names_for_output
void doOutputLastTimestep(Process const &process, const int process_id, int const timestep, const NumLib::Time &t, int const iteration, bool const converged, std::vector< GlobalVector * > const &xs) const
void outputMeshes(int const timestep, const double t, int const iteration, bool const converged, std::vector< std::reference_wrapper< const MeshLib::Mesh > > const &meshes) const
void doNotProjectFromBulkMeshToSubmeshes(std::string const &property_name, MeshLib::MeshItemType const mesh_item_type)
OutputDataSpecification _output_data_specification
std::vector< std::reference_wrapper< Process const > > _output_processes
MeshLib::Mesh const & prepareSubmesh(std::string const &submesh_output_name, Process const &process, const int process_id, NumLib::Time const &t, std::vector< GlobalVector * > const &xs) const
Output(std::unique_ptr< OutputFormat > &&output_format, bool const output_nonlinear_iteration_results, OutputDataSpecification &&output_data_specification, std::vector< std::string > &&mesh_names_for_output, std::vector< std::unique_ptr< MeshLib::Mesh > > const &meshes)
bool isOutputProcess(int const process_id, Process const &process) const
std::unique_ptr< OutputFormat > _output_format
void addProcess(ProcessLib::Process const &process)
TODO doc. Opens a PVD file for each process.
std::vector< std::string > getFileNamesForOutput() const
bool isOutputStep(int const timestep, NumLib::Time const &t) const
Tells if output will be written at the specified timestep/time.
MeshLib::Mesh & getMesh() const
Definition Process.h:146
virtual bool isMonolithicSchemeUsed() const
Definition Process.h:95
std::string joinPaths(std::string const &pathA, std::string const &pathB)
void makeVectorUnique(std::vector< T > &v)
Definition Algorithm.h:173
void CoProcess(MeshLib::Mesh const &mesh, double const time, unsigned int const timeStep, bool const lastTimeStep, std::string output_directory)
Definition Adaptor.cpp:63
PropertyVector< T > * getOrCreateMeshProperty(Mesh &mesh, std::string const &property_name, MeshItemType const item_type, int const number_of_components)
Mesh & findMeshByName(std::vector< std::unique_ptr< Mesh > > const &meshes, std::string_view const name)
Definition Mesh.cpp:354
constexpr std::string_view getBulkIDString(MeshItemType mesh_item_type)
void addProcessDataToMesh(NumLib::Time const &t, std::vector< GlobalVector * > const &xs, int const process_id, ProcessOutputData const &process_output_data, bool const output_secondary_variables, OutputDataSpecification const &process_output)
ProcessOutputData createProcessOutputData(Process const &process, std::size_t const n_processes, MeshLib::Mesh &output_mesh)
Extracts data necessary for output from the given process.
std::ostream & operator<<(std::ostream &os, Output const &output)
void outputMeshVtk(std::string const &file_name, MeshLib::Mesh const &mesh, std::set< std::string > const &output_variables, bool const compress_output, int const data_mode)
std::vector< double > calculateUniqueFixedTimesForAllOutputs(std::vector< Output > const &outputs)
void addBulkMeshPropertyToSubMesh(MeshLib::Mesh const &bulk_mesh, MeshLib::Mesh &sub_mesh, std::string const &property_name)
Holds information about which variables to write to output files.