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