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 if (_output_data_specification.output_variables.empty())
177 {
178 // special case: no output properties specified => output all properties
179 for (auto const& mesh : meshes)
180 {
181 for (auto [name, property] : mesh.get().getProperties())
182 {
183 property->is_for_output = true;
184 }
185 }
186 }
187 else
188 {
189 for (auto const& mesh : meshes)
190 {
191 for (auto [name, property] : mesh.get().getProperties())
192 {
193 // special case: always output OGS_VERSION
194 if (name == "OGS_VERSION")
195 {
196 property->is_for_output = true;
197 continue;
198 }
199
200 property->is_for_output =
201 _output_data_specification.output_variables.contains(name);
202 }
203 }
204 }
205 _output_format->outputMeshes(timestep, t, iteration, converged, meshes,
206 _output_data_specification.output_variables);
207}
208
210 std::string const& submesh_output_name, Process const& process,
211 const int process_id, NumLib::Time const& t,
212 std::vector<GlobalVector*> const& xs) const
213{
214 auto& submesh = MeshLib::findMeshByName(_meshes.get(), submesh_output_name);
215
216 DBUG("Found {:d} nodes for output at mesh '{:s}'.",
217 submesh.getNumberOfNodes(), submesh.getName());
218
219 bool const output_secondary_variables = false;
220
221 // TODO Under the assumption that xs.size() and submesh do not change during
222 // the simulation, process output data should not be recreated every time,
223 // but should rather be computed only once and stored for later reuse.
224 auto const process_output_data =
225 createProcessOutputData(process, xs.size(), submesh);
226
227 addProcessDataToMesh(t, xs, process_id, process_output_data,
228 output_secondary_variables,
230
231 auto const& bulk_mesh = process.getMesh();
232 auto const& property_names =
234
235 // TODO Once all processes have been refactored to use the new residuum
236 // assembly logic, the functionality of this lambda should be refactored.
237 // Currently (Jan '23) there is a difference between the logic using this
238 // lambda and doNotProjectFromBulkMeshToSubmeshes(): The latter is
239 // considered regardless of submesh dimension.
240 auto is_residuum_field = [](std::string const& name) -> bool
241 {
242 using namespace std::literals::string_view_literals;
243 static constexpr std::string_view endings[] = {
244 "FlowRate"sv, "heat_flux"sv, "MaterialForces"sv, "NodalForces"sv,
245 "NodalForcesJump"sv};
246 auto ends_with = [&](std::string_view const& ending)
247 { return name.ends_with(ending); };
248 return std::find_if(std::begin(endings), std::end(endings),
249 ends_with) != std::end(endings);
250 };
251
252 for (auto const& name : property_names)
253 {
255 {name, MeshLib::MeshItemType::Node}))
256 {
257 // the projection is disabled regardless of mesh and submesh
258 // dimension
259 continue;
260 }
261
262 if (bulk_mesh.getDimension() == submesh.getDimension())
263 {
264 // omit the 'simple' transfer of the properties in the if condition
265 // on submeshes with equal dimension to the bulk mesh
266 // for those data extra assembly is required
267 if (is_residuum_field(name))
268 {
269 continue;
270 }
271 addBulkMeshPropertyToSubMesh(bulk_mesh, submesh, name);
272 }
273 else
274 {
275 // For residuum based properties it is assumed that the lower
276 // dimensional mesh is a boundary mesh!
277 addBulkMeshPropertyToSubMesh(bulk_mesh, submesh, name);
278 }
279 }
280 return submesh;
281}
282
283void Output::doOutputAlways(Process const& process,
284 const int process_id,
285 int const timestep,
286 const NumLib::Time& t,
287 int const iteration,
288 bool const converged,
289 std::vector<GlobalVector*> const& xs) const
290{
291 BaseLib::RunTime time_output;
292 time_output.start();
293
294 bool const output_secondary_variables = true;
295 auto const process_output_data =
296 createProcessOutputData(process, xs.size(), process.getMesh());
297
298 // Need to add variables of process to mesh even if no output takes place.
299 addProcessDataToMesh(t, xs, process_id, process_output_data,
300 output_secondary_variables,
302
303 if (!isOutputProcess(process_id, process))
304 {
305 return;
306 }
307
308 std::vector<std::reference_wrapper<const MeshLib::Mesh>> output_meshes;
309 for (auto const& mesh_output_name : _mesh_names_for_output)
310 {
311 if (process.getMesh().getName() == mesh_output_name)
312 {
313 // process related output
314 output_meshes.emplace_back(process.getMesh());
315 }
316 else
317 {
318 // mesh related output
319 auto const& submesh =
320 prepareSubmesh(mesh_output_name, process, process_id, t, xs);
321 output_meshes.emplace_back(submesh);
322 }
323 }
324
325 outputMeshes(timestep, t(), iteration, converged, std::move(output_meshes));
326
327 INFO("[time] Output of timestep {:d} took {:g} s.", timestep,
328 time_output.elapsed());
329}
330
331void Output::doOutput(Process const& process,
332 const int process_id,
333 int const timestep,
334 const NumLib::Time& t,
335 int const iteration,
336 bool const converged,
337 std::vector<GlobalVector*> const& xs) const
338{
339 if (isOutputStep(timestep, t))
340 {
341 doOutputAlways(process, process_id, timestep, t, iteration, converged,
342 xs);
343 }
344#ifdef OGS_USE_INSITU
345 // Note: last time step may be output twice: here and in
346 // doOutputLastTimestep() which throws a warning.
347 InSituLib::CoProcess(process.getMesh(), t, timestep, false,
348 _output_format->directory);
349#endif
350}
351
353 const int process_id,
354 int const timestep,
355 const NumLib::Time& t,
356 int const iteration,
357 bool const converged,
358 std::vector<GlobalVector*> const& xs) const
359{
360 if (!isOutputStep(timestep, t))
361 {
362 doOutputAlways(process, process_id, timestep, t, iteration, converged,
363 xs);
364 }
365#ifdef OGS_USE_INSITU
366 InSituLib::CoProcess(process.getMesh(), t, timestep, true,
367 _output_format->directory);
368#endif
369}
370
372 Process const& process, const int process_id, int const timestep,
373 const NumLib::Time& t, int const iteration, bool const converged,
374 std::vector<GlobalVector*> const& xs) const
375{
377 {
378 return;
379 }
380
381 BaseLib::RunTime time_output;
382 time_output.start();
383
384 bool const output_secondary_variable = true;
385 auto const process_output_data =
386 createProcessOutputData(process, xs.size(), process.getMesh());
387
388 addProcessDataToMesh(t, xs, process_id, process_output_data,
389 output_secondary_variable, _output_data_specification);
390
391 if (!isOutputProcess(process_id, process))
392 {
393 return;
394 }
395
396 std::string const output_file_name = _output_format->constructFilename(
397 process.getMesh().getName(), timestep, t(), iteration, converged);
398
399 std::string const output_file_path =
400 BaseLib::joinPaths(_output_format->directory, output_file_name);
401
402 DBUG("output iteration results to {:s}", output_file_path);
403
404 if (dynamic_cast<OutputVTKFormat*>(_output_format.get()))
405 {
407 output_file_path, process.getMesh(), _output_format->compression,
408 dynamic_cast<OutputVTKFormat*>(_output_format.get())->data_mode);
409 }
410 else
411 {
412 DBUG("non-linear iterations can only written in Vtk/VTU format.");
413 }
414 INFO("[time] Output took {:g} s.", time_output.elapsed());
415}
416
417bool Output::isOutputStep(int const timestep, NumLib::Time const& t) const
418{
419 return _output_data_specification.isOutputStep(timestep, t);
420}
421
422std::vector<std::string> Output::getFileNamesForOutput() const
423{
424 auto construct_filename = ranges::views::transform(
425 [&](auto const& output_name) {
426 return _output_format->constructFilename(output_name, 0, 0, 0,
427 true);
428 });
429
430 return _mesh_names_for_output | construct_filename |
431 ranges::to<std::vector>;
432}
433
435 std::vector<Output> const& outputs)
436{
437 std::vector<double> fixed_times;
438 for (auto const& output : outputs)
439 {
440 auto const& output_fixed_times = output.getFixedOutputTimes();
441 fixed_times.insert(fixed_times.end(), output_fixed_times.begin(),
442 output_fixed_times.end());
443 }
444 BaseLib::makeVectorUnique(fixed_times);
445 return fixed_times;
446}
447
448std::ostream& operator<<(std::ostream& os, Output const& output)
449{
450 os << "Output::_output_data_specification:\t"
452 os << "Output::_output_format:\t" << *(output._output_format);
453 return os;
454}
455
456} // 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:125
const std::string getName() const
Get name of the mesh.
Definition Mesh.h:94
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, 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.