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