MAIA bb96820c
Multiphysics at AIA
Loading...
Searching...
No Matches
samplingdata.h
Go to the documentation of this file.
1// Copyright (C) 2024 The m-AIA AUTHORS
2//
3// This file is part of m-AIA (https://git.rwth-aachen.de/aia/m-AIA/m-AIA)
4//
5// SPDX-License-Identifier: LGPL-3.0-only
6
7#ifndef SAMPLINGDATA_H_
8#define SAMPLINGDATA_H_
9
10#include <numeric>
11#include "COMM/mpioverride.h"
12#include "GEOM/geometry.h"
13#include "GEOM/geometry2d.h"
14#include "GEOM/geometry3d.h"
16#include "IO/parallelio.h"
17#include "UTIL/functions.h"
18#include "UTIL/tensor.h"
19#include "globals.h"
20#include "typetraits.h"
21
22// TODO labels:PP,COMM Put all ranks which record data with this feature in a seperate MPI
23// communicator and use that during I/O for optimization
24
25// Predeclaration of auxiliary claas
34template <MInt nDim, class SolverType>
36 public:
38
39 void init();
40 void save(MBool finalTimestep);
42 MBool enabled() const { return m_enabled; }
43
46 virtual MInt loadInputFile(const MString NotUsed(inputFileName),
47 const MInt NotUsed(fileNo),
48 std::vector<MFloat>& NotUsed(coordinates)) {
49 TERMM(1, "ERROR: not implemented in base class");
50 return -1;
51 }
53 virtual MString getBaseName() const {
54 TERMM(1, "ERROR: not implemented in base class");
55 return "";
56 }
57
58 protected:
59 // Methods
60 void init(SamplingDataSeries& timeSeries);
61 void save(SamplingDataSeries& timeSeries, MBool finalTimeStep);
62
64 virtual MBool hasInputDataFile(const MInt fileNo) const {
65 const MString propName = getBaseName() + "DataFileName_" + std::to_string(fileNo);
66 if(Context::propertyExists(propName, solverId())) {
67 const MString fileName = Context::getSolverProperty<MString>(propName, solverId(), AT_);
68 return fileName != "";
69 } else {
70 return false;
71 }
72 }
73
75 virtual MBool generatePoints() const { return false; }
76
78 virtual MString getInputDataFile(const MInt fileNo) const {
92 const MString propName = getBaseName() + "DataFileName_" + std::to_string(fileNo);
93 return Context::getSolverProperty<MString>(propName, solverId(), AT_);
94 }
95
97 virtual MInt noSamplingPoints(const MInt NotUsed(fileNo)) {
98 TERMM(1, "Not implemented in derived class!");
99 return -1;
100 }
101
103 virtual void getSamplingPoint(const MInt NotUsed(fileNo), const MInt NotUsed(pointId),
104 MFloat* const NotUsed(coordinates)) {
105 TERMM(1, "Not implemented in derived class!");
106 }
107
109 void indexXD(const MInt index, const MInt* const nPoints, MInt* indexXD);
110
113
115 virtual void readAdditionalProperties(const MInt NotUsed(fileNo)){};
116
117 // Convenience wrappers to reduce direct solver access
118 MInt solverId() const { return m_solver.solverId(); }
119 MBool isMpiRoot() const { return m_solver.domainId() == 0; }
120 MPI_Comm mpiComm() const { return m_solver.mpiComm(); }
121
122 // Other convenience methods
124 MInt noVars() const { return std::accumulate(m_noSolverSamplingVars.begin(), m_noSolverSamplingVars.end(), 0); }
126 MString getFileBaseName() const { return getBaseName() + "_data_"; }
127
130 const SolverType& solver() const { return m_solver; }
131
132 // Members
133 // Used to disable this entire feature
136
138 std::vector<SamplingDataSeries> m_timeSeries{};
139
141 std::vector<MInt> m_solverSamplingVarIds{};
143 std::vector<MInt> m_noSolverSamplingVars{};
145 std::vector<std::vector<MString>> m_solverSamplingVarNames{};
146
147 private:
148 // Members
149 // Store reference to solver
151};
152
153
163template <MInt nDim, class SolverType>
164class PointData : public SamplingData<nDim, SolverType> {
165 public:
167 // Allow the use of base class functions without the need to use this->function()
174
175 // Constructor that must be called; sets reference to solver
176 PointData(SolverType& solver_) : SamplingData<nDim, SolverType>(solver_) {}
177 virtual ~PointData(){};
178 // Override virtual methods of base class
179 MInt loadInputFile(const MString inputFileName, const MInt NotUsed(fileNo),
180 std::vector<MFloat>& coordinates) override;
181
182 MString getBaseName() const override { return "point"; }
183};
184
185
189template <MInt nDim, class SolverType>
190class SurfaceData : public SamplingData<nDim, SolverType> {
191 public:
193 // Allow the use of base class functions without the need to use this->function()
200
201 // Constructor that must be called; sets reference to solver
202 SurfaceData(SolverType& solver_) : SamplingData<nDim, SolverType>(solver_) {}
203 virtual ~SurfaceData();
204
205 // Store geometric information for surface
206 void saveGeomInfo(const MString inputFileName, const MInt fileNo, MFloat* coordinates);
207
208 // Override virtual methods of base class
209 MInt loadInputFile(const MString inputFileName, const MInt fileNo, std::vector<MFloat>& coordinates) override;
210 MString getBaseName() const override { return "surface"; }
211
212 private:
213 // Geometry object with surface elements
215};
216
217
219template <MInt nDim, class SolverType>
220class VolumeData : public SamplingData<nDim, SolverType> {
221 public:
223 // Allow the use of base class functions without the need to use this->function()
231
232 // Constructor that must be called; sets reference to solver
233 VolumeData(SolverType& solver_) : SamplingData<nDim, SolverType>(solver_) {}
234 virtual ~VolumeData(){};
235 // Override virtual methods of base class
236 MString getBaseName() const override { return "volume"; }
237
238 protected:
240 MBool generatePoints() const override { return true; }
241
243 MBool hasInputDataFile(const MInt fileNo) const override {
244 const MString propName = getBaseName() + "DataShape_" + std::to_string(fileNo);
245 if(Context::propertyExists(propName, solverId())) {
246 const MString shape = Context::getSolverProperty<MString>(propName, solverId(), AT_);
247 return shape != "";
248 } else {
249 return false;
250 }
251 }
252
253 MString getInputDataFile(const MInt NotUsed(fileNo)) const {
254 return ""; // No input file, points are generated
255 }
256
257 void readAdditionalProperties(const MInt fileNo) override;
258 // Sampling point generation
259 MInt noSamplingPoints(const MInt fileNo) override;
260 void getSamplingPoint(const MInt fileNo, const MInt pointId, MFloat* const coordinates) override;
261
262 private:
265 std::vector<MString> m_volumeShape{};
266 std::vector<std::vector<MFloat>> m_volumeParameterFloat{};
267 std::vector<std::vector<MInt>> m_volumeParameterInt{};
268};
269
270
279 public:
280 // C'tor
281 SamplingDataSeries(const MString inputFileName, const MBool generatePoints, const MInt sampleInterval,
282 const MInt writeInterval, const MInt startTimeStep, const MInt endTimeStep, const MInt noFile)
283 : m_inputFileName(inputFileName),
284 m_generatePoints(generatePoints),
285 m_sampleInterval(sampleInterval),
286 m_writeInterval(writeInterval),
287 m_startTimeStep(startTimeStep),
288 m_endTimeStep(endTimeStep),
289 m_fileNo(noFile) {}
290
291 // Member
292 // File name of file containing points
294 // Enable generation of points instead of reading from file
295 const MBool m_generatePoints = false;
296 // Interval in which samplingData should be saved
298 // Interval in which samplingData should be written
300 // Time step after which recording of conservative variables starts
302 // Time step after which recording of conservative variables ends
303 const MInt m_endTimeStep = -1;
304
305 // The following three vectors contain only the data relevant to the rank
306 // Vector containing the point coordinates at which states should be written
307 // out
308 // Contains nDim tuples which represent one coordinate
309 std::vector<MFloat> m_coordinates{};
310 // Vector containing the element/cell ids of the points
311 std::vector<MInt> m_elementIds{};
312 // Sort index: each entry points to the index in the input file
313 std::vector<MInt> m_sortIndex{};
314 // Number of total points at which states should be written out
316 // Numver of points relevant to this rank
318 // Buffer which holds timesteps intbetween writes of point data
319 std::vector<MInt> m_timeStepBuffer{};
320 // Buffer which holds times intbetween writes of point data
321 std::vector<MFloat> m_timeBuffer{};
322 // Buffer which holds sampling data states of variables inbetween writes
323 // Should be accessed as 3D array with the following dimensions:
324 // 1st dim: point index
325 // 2nd dim: sample index
326 // 3rd dim: var index
327 std::vector<MFloat> m_stateBuffer{};
328 // How many times point data was saved to the buffer
330 // Maximum times point data will be buffered
332 // MPI Communicator containing point data ranks
333 MPI_Comm m_mpiComm = MPI_COMM_NULL;
334 // Identifies the filenumber of current object
335 const MInt m_fileNo = -1;
336 // Identifies new root for point data ranks
338};
339
340
341/* \brief Sets I/O properties concerning this feature
342 *
343 * \author Fabian Klemp <f.klemp@aia.rwth-aachen.de>
344 * \date 2016-09-06
345 *
346 */
347template <MInt nDim, class SolverType>
349 TRACE();
350
351 // Get base name of derived class, all properties are defined as "[baseName][PropertyName]"
352 const MString type = getBaseName();
353
354 if(Context::propertyExists(type + "DataEnabled", solverId())) {
362 m_enabled = Context::getSolverProperty<MBool>(type + "DataEnabled", solverId(), AT_);
363 if(!m_enabled) {
364 return;
365 }
366 }
367
368 // Check for all input files
369 if(hasInputDataFile(0)) {
370 m_enabled = true;
371
372 // Check for new default variables
373 MInt sampleIntervalGlobal = 1;
387 sampleIntervalGlobal =
388 Context::getSolverProperty<MInt>(type + "DataSampleInterval", solverId(), AT_, &sampleIntervalGlobal);
389
403 MInt writeIntervalGlobal = -1;
404 const MInt restartInterval = m_solver.restartInterval();
405 if(restartInterval > 0) {
406 writeIntervalGlobal =
407 Context::getSolverProperty<MInt>(type + "DataWriteInterval", solverId(), AT_, &restartInterval);
408 } else {
409 writeIntervalGlobal =
410 Context::getSolverProperty<MInt>(type + "DataWriteInterval", solverId(), AT_, &writeIntervalGlobal);
411 }
412
426 MInt startTimeStepGlobal = 0;
427 startTimeStepGlobal =
428 Context::getSolverProperty<MInt>(type + "DataStartTimeStep", solverId(), AT_, &startTimeStepGlobal);
429
443 MInt endTimeStepGlobal = std::numeric_limits<MInt>::max();
444 endTimeStepGlobal = Context::getSolverProperty<MInt>(type + "DataEndTimeStep", solverId(), AT_, &endTimeStepGlobal);
445
446 m_solverSamplingVarIds.clear();
447 m_noSolverSamplingVars.clear();
448 m_solverSamplingVarNames.clear();
449 // Get sampling variables from solver
450 solver().getSolverSamplingProperties(m_solverSamplingVarIds, m_noSolverSamplingVars, m_solverSamplingVarNames);
451
452 // Allocate additional storage in the solver for the sampling variables
453 solver().initSolverSamplingVariables(m_solverSamplingVarIds, m_noSolverSamplingVars);
454
455 // TODO labels:PP add output about sampling variables
456
457 // Read all Input files, properties and create point data objects
458 MInt noFiles = 0;
459 while(hasInputDataFile(noFiles)) {
460 const MString samplingDataFileName = getInputDataFile(noFiles);
461
462 // Check if points should be generated if no input file name is specified
463 MBool genPoints = false;
464 if(samplingDataFileName == "") {
465 genPoints = generatePoints(); // Default behaviour
477 genPoints = Context::getSolverProperty<MBool>(type + "DataGeneratePoints_" + std::to_string(noFiles),
478 solverId(), AT_, &genPoints);
479 }
480
493 // Check if other property is set by file number
494 const MInt sampleInterval = Context::getSolverProperty<MInt>(
495 type + "DataSampleInterval_" + std::to_string(noFiles), solverId(), AT_, &sampleIntervalGlobal);
496
497 const MInt writeInterval = Context::getSolverProperty<MInt>(type + "DataWriteInterval_" + std::to_string(noFiles),
498 solverId(), AT_, &writeIntervalGlobal);
499
500 if(sampleInterval < 1) {
501 TERMM(1, type + "DataSampleInterval must be a positive integer.");
502 }
503
504 if(writeInterval < 1) {
505 TERMM(1, type
506 + "DataWriteInterval musst be a positive integer. Check if "
507 "all write intervals are set.");
508 }
509
510 if(writeInterval % sampleInterval != 0) {
511 TERMM(1, "samplingDataSampleInterval must be a divisor of "
512 "samplingDataWriteInterval (default value: restartInterval) for "
513 "technical reasons. Also it makes more sense this way.");
514 }
515 if(m_solver.restartInterval() > 0 && m_solver.restartInterval() % writeInterval != 0) {
516 TERMM(1, type
517 + "DataWriteInterval must be a divisor of restartInterval because otherwise "
518 "restarting a simulation without data loss is not possible.");
519 }
520
521 // For property description see samplingDataStartTimeStep
522 const MInt startTimeStep = Context::getSolverProperty<MInt>(type + "DataStartTimeStep_" + std::to_string(noFiles),
523 solverId(), AT_, &startTimeStepGlobal);
524 if(startTimeStep < 0) {
525 TERMM(1,
526 "Point data recording start time step is negative. It must have "
527 "a positive value.");
528 }
529
530 const MInt endTimeStep = Context::getSolverProperty<MInt>(type + "DataEndTimeStep_" + std::to_string(noFiles),
531 solverId(), AT_, &endTimeStepGlobal);
532
533 if(endTimeStep < 0) {
534 TERMM(1, "Point data recording end time step is negative. It must have "
535 "a positive value.");
536 }
537 if(endTimeStep < startTimeStep) {
538 TERMM(1,
539 "End time step of point data recording is smaller than start time"
540 " step of point data recording. Check your property file.");
541 }
542
543 m_interpolatePointData = true;
544 if(Context::propertyExists("interpolatePointData")) {
545 m_interpolatePointData =
546 Context::getSolverProperty<MBool>("interpolatePointData", solverId(), AT_, &m_interpolatePointData);
547 }
548
549 // Read additional properties if required by derived class
550 readAdditionalProperties(noFiles);
551
552 // Create auxiliary object
553 SamplingDataSeries samplingData(samplingDataFileName, genPoints, sampleInterval, writeInterval, startTimeStep,
554 endTimeStep, noFiles);
555 // Store all auxiliary objects to edit them in a loop
556 m_timeSeries.push_back(samplingData);
557 noFiles++;
558 }
559 } else {
560 m_enabled = false;
561 }
562}
563
564
565/* \brief Initializes all auxiliary sampling data objects
566 *
567 * \author Marcus Wiens <m.wiens@aia.rwth-aachen.de>
568 * \date 2016-11-02
569 */
570template <MInt nDim, class SolverType>
572 if(!m_enabled || !solver().isActive()) {
573 return;
574 }
575
576 for(MUint i = 0; i < m_timeSeries.size(); i++) {
577 init(m_timeSeries[i]);
578 }
579}
580
581
582/* \brief Initializes all sampling data related members
583 * Load all points relevant to this rank at which states should be
584 * written out.
585 *
586 * \author Fabian Klemp <f.klemp@aia.rwth-aachen.de>
587 * \date 2016-06-19
588 */
589template <MInt nDim, class SolverType>
591 using namespace maia;
592 using namespace std;
593
594
595 // save points and corresponding data in this container for sorting
596 // tuple contains following data in that order:
597 // coordinates of point, element id of point, line index in input file
598 std::vector<std::tuple<std::array<MFloat, nDim>, MInt, MInt>> localTmpPoints;
599
600 m_log << "Solver #" << solverId() << ": ";
601 // Check for an input file
602 if(timeSeries.m_inputFileName != "") {
603 m_log << "Loading points for " << getBaseName() << " data class...";
604
605 std::vector<MFloat> tmpCoordinates{};
606
607 // read in file on root using the I/O method of the derived class
608 if(isMpiRoot()) {
609 timeSeries.m_noPoints = loadInputFile(timeSeries.m_inputFileName, timeSeries.m_fileNo, tmpCoordinates);
610 }
611
612 MPI_Bcast(&timeSeries.m_noPoints, 1, type_traits<MInt>::mpiType(), 0, mpiComm(), AT_, "timeSeries.m_noPoints");
613
614 if(timeSeries.m_noPoints <= 0) {
615 TERMM(1, "ERROR: no sampling points found for input file '" + timeSeries.m_inputFileName + "'");
616 m_log << "ERROR: no sampling points found for input file '" << timeSeries.m_inputFileName << "'" << std::endl;
617 return;
618 }
619
620 ScratchSpace<MFloat> coordinatesScratch(timeSeries.m_noPoints * nDim, FUN_, "coordinatesScratch");
621 if(isMpiRoot()) {
622 copy(tmpCoordinates.begin(), tmpCoordinates.end(), coordinatesScratch.begin());
623 }
624 MPI_Bcast(&coordinatesScratch[0], timeSeries.m_noPoints * nDim, type_traits<MFloat>::mpiType(), 0, mpiComm(), AT_,
625 "coordinatesScratch[0]");
626
627 localTmpPoints.resize(timeSeries.m_noPoints);
628
629 MFloatTensor tmpCoordinatesTensor(&coordinatesScratch[0], timeSeries.m_noPoints, nDim);
630 std::array<MFloat, nDim> curCoordinates{};
631 MInt curElementId = -1;
632 for(MInt i = 0; i < timeSeries.m_noPoints; i++) {
633 for(MInt j = 0; j < nDim; j++) {
634 curCoordinates[j] = tmpCoordinatesTensor(i, j);
635 }
636 // check if the point is relevant to this rank, each point should be globally unique, i.e.,
637 // appear only once
638 curElementId = solver().getIdAtPoint(&curCoordinates[0], true);
639
640 if(curElementId != -1) {
641 localTmpPoints[timeSeries.m_noLocalPoints] = make_tuple(curCoordinates, curElementId, i);
642 timeSeries.m_noLocalPoints++;
643
644 // Precompute interpolation information if possible/required
645 solver().initInterpolationForCell(curElementId);
646 }
647 }
648
649 // sanity check if each point ended up on exactly one rank:
650 ScratchSpace<MInt> loadedPointsVector(timeSeries.m_noPoints, FUN_, "loadedPointsVector");
651 loadedPointsVector.fill(0);
652 for(MInt i = 0; i < timeSeries.m_noLocalPoints; i++) {
653 loadedPointsVector[get<2>(localTmpPoints[i])]++;
654 }
655
656 if(isMpiRoot()) {
657 MPI_Reduce(MPI_IN_PLACE, &loadedPointsVector[0], loadedPointsVector.size(), type_traits<MInt>::mpiType(), MPI_SUM,
658 0, mpiComm(), AT_, "MPI_IN_PLACE", "loadedPointsVector[0]");
659 for(MUint i = 0; i < loadedPointsVector.size(); i++) {
660 if(loadedPointsVector[i] != 1) {
661 std::ostringstream err;
662 err << "Point at line " << i + 1 << " of file " // start line count at one
663 << timeSeries.m_inputFileName << " was loaded " << loadedPointsVector[i] << " times. Aborting."
664 << std::endl;
665 TERMM(1, err.str());
666 }
667 }
668 } else {
669 MPI_Reduce(&loadedPointsVector[0], nullptr, loadedPointsVector.size(), type_traits<MInt>::mpiType(), MPI_SUM, 0,
670 mpiComm(), AT_, "loadedPointsVector[0]", "nullptr");
671 }
672 } else if(timeSeries.m_generatePoints) {
673 m_log << "Generating points for " << getBaseName() << " data class...";
674 // Generate points based on given properties
675 // Get number of points to generate
676 const MInt noPoints = noSamplingPoints(timeSeries.m_fileNo);
677 timeSeries.m_noPoints = noPoints;
678
679 std::array<MFloat, nDim> coordinates{};
680 MLong checkSum = 0;
681
682 for(MInt i = 0; i < noPoints; i++) {
683 // Get the coordinates for this sampling point id
684 getSamplingPoint(timeSeries.m_fileNo, i, &coordinates[0]);
685
686 // Check if point is relevant for local rank
687 const MInt curElementId = solver().getIdAtPoint(&coordinates[0], true);
688 if(curElementId != -1) {
689 localTmpPoints.push_back(make_tuple(coordinates, curElementId, i));
690 timeSeries.m_noLocalPoints++;
691 checkSum += (i + 1);
692
693 // Precompute interpolation information if possible/required
694 solver().initInterpolationForCell(curElementId);
695 }
696 }
697
698 MInt globalNoPoints = timeSeries.m_noLocalPoints;
699 MPI_Allreduce(MPI_IN_PLACE, &globalNoPoints, 1, type_traits<MInt>::mpiType(), MPI_SUM, mpiComm(), AT_,
700 "MPI_IN_PLACE", "globalNoPoints");
701 if(globalNoPoints != noPoints) {
702 TERMM(1, "Global number of points does not match " + std::to_string(globalNoPoints)
703 + " != " + std::to_string(noPoints));
704 }
705
706 // Use Gauss sum equation to check if all points ended up on exactly one rank
707 // Note: this is a heuristic check but it should be very unlikely to get a false positive; this
708 // will work up to 2 billion sampling points
709 MPI_Allreduce(MPI_IN_PLACE, &checkSum, 1, type_traits<MLong>::mpiType(), MPI_SUM, mpiComm(), AT_, "MPI_IN_PLACE",
710 "checkSum");
711 const MLong noPointsTmp = noPoints;
712 if(2 * checkSum != (noPointsTmp * noPointsTmp + noPointsTmp)) {
713 TERMM(1, "Incorrect check sum for " + std::to_string(noPoints) + " points: " + std::to_string(checkSum));
714 }
715 } else {
716 m_log << "Warning: no sampling data input file specified and generation of points disabled." << std::endl;
717 return;
718 }
719
720 // Create a new MPI Communicator
721 ScratchSpace<MInt> pointDataScratch(globalNoDomains(), FUN_, "pointDataScratch");
722 std::fill_n(&pointDataScratch[0], globalNoDomains(), -1);
723
724 // Get Rank if points are relevant
725 MInt rank = -1;
726 if(timeSeries.m_noLocalPoints > 0) {
727 MPI_Comm_rank(mpiComm(), &rank);
728 }
729
730 // Combine all ranks to get relevant ranks
731 MPI_Allgather(&rank, 1, type_traits<MInt>::mpiType(), &pointDataScratch[0], 1, type_traits<MInt>::mpiType(),
732 mpiComm(), AT_, "rank", "pointDataScratch[0]");
733
734 // Check for relevant ranks and save them to create the new communicator
735 const MInt noPdDomains =
736 count_if(pointDataScratch.begin(), pointDataScratch.end(), [](const MInt a) { return a != -1; });
737 MIntScratchSpace pdDomains(noPdDomains, AT_, "pdDomains");
738 MInt position = 0;
739 for(MUint i = 0; i < pointDataScratch.size(); i++) {
740 if(pointDataScratch[i] != -1) {
741 pdDomains[position] = pointDataScratch[i];
742 position++;
743 }
744 }
745
746 // Create new point data mpi group
747 MPI_Group globalGroup, localGroup;
748 MPI_Comm_group(mpiComm(), &globalGroup, AT_, "globalGroup");
749 MPI_Group_incl(globalGroup, noPdDomains, &pdDomains[0], &localGroup, AT_);
750
751 // Create new communicator and clean up
752 MPI_Comm_create(mpiComm(), localGroup, &timeSeries.m_mpiComm, AT_, "timeSeries.m_mpiComm");
753 MPI_Group_free(&globalGroup, AT_);
754 MPI_Group_free(&localGroup, AT_);
755
756 // Set root for new communicator
757 if(timeSeries.m_noLocalPoints > 0) {
758 MInt subRank = -1;
759 MPI_Comm_rank(timeSeries.m_mpiComm, &subRank);
760 if(subRank == 0) {
761 timeSeries.m_isMpiRoot = true;
762 }
763 }
764
765
766 // sort points by global id
767 stable_sort(localTmpPoints.begin(),
768 localTmpPoints.begin() + timeSeries.m_noLocalPoints,
769 // Read all input files and save all points into one array
770 [this](const std::tuple<std::array<MFloat, nDim>, MInt, MInt>& a,
771 const std::tuple<std::array<MFloat, nDim>, MInt, MInt>& b) {
772 return solver().grid().tree().globalId(solver().getCellIdByIndex(get<1>(a)))
773 < solver().grid().tree().globalId(solver().getCellIdByIndex(get<1>(b)));
774 });
775
776 // split localTmpPoints for it to be easier to handle later on
777 if(timeSeries.m_noLocalPoints > 0) {
778 timeSeries.m_coordinates.resize(timeSeries.m_noLocalPoints * nDim);
779 MFloatTensor coordinatesTensor(&timeSeries.m_coordinates[0], timeSeries.m_noLocalPoints, nDim);
780 timeSeries.m_elementIds.resize(timeSeries.m_noLocalPoints);
781 timeSeries.m_sortIndex.resize(timeSeries.m_noLocalPoints);
782 for(MInt i = 0; i < timeSeries.m_noLocalPoints; i++) {
783 for(MInt j = 0; j < nDim; j++) {
784 coordinatesTensor(i, j) = get<0>(localTmpPoints[i])[j];
785 }
786 timeSeries.m_elementIds[i] = get<1>(localTmpPoints[i]);
787 timeSeries.m_sortIndex[i] = get<2>(localTmpPoints[i]);
788 }
789 }
790
791 // Save sampling point coordinates if they were not loaded from a file
792 if(timeSeries.m_inputFileName == "" && timeSeries.m_noLocalPoints > 0) {
793 saveSamplingPointCoordinates(timeSeries);
794 }
795
796 // Allocate memory to save/buffer point data inbetween writes
797 timeSeries.m_maxNoSample = std::max(1, timeSeries.m_writeInterval / timeSeries.m_sampleInterval);
798 if(timeSeries.m_noLocalPoints > 0) {
799 // Note: +1 for initial data file containing also timestep 0
800 timeSeries.m_stateBuffer.resize(timeSeries.m_noLocalPoints * (timeSeries.m_maxNoSample + 1) * noVars());
801 }
802
803 // Allocate storage for the time step count and the simulation time
804 if(timeSeries.m_isMpiRoot) {
805 timeSeries.m_timeStepBuffer.resize(timeSeries.m_maxNoSample + 1);
806 timeSeries.m_timeBuffer.resize(timeSeries.m_maxNoSample + 1);
807 } else {
808 timeSeries.m_timeStepBuffer.resize(1);
809 timeSeries.m_timeBuffer.resize(1);
810 }
811
812 m_log << " done" << std::endl << "#" << timeSeries.m_fileNo;
813 if(timeSeries.m_inputFileName != "") {
814 m_log << " Loaded " << timeSeries.m_noPoints << " points from " << timeSeries.m_inputFileName << " for "
815 << getBaseName() << " sampling feature successfully." << std::endl;
816 } else if(timeSeries.m_generatePoints) {
817 m_log << " Generated " << timeSeries.m_noPoints << " points for " << getBaseName()
818 << " sampling feature successfully." << std::endl;
819 }
820 m_log << "sampleInterval = " << timeSeries.m_sampleInterval << "; startTimeStep = " << timeSeries.m_startTimeStep
821 << "; endTimeStep = " << timeSeries.m_endTimeStep << "; writeInterval = " << timeSeries.m_writeInterval
822 << std::endl;
823}
824
825
826/* \brief Buffers/Save all auxiliary sampling data objects
827 *
828 * \author Marcus Wiens <m.wiens@aia.rwth-aachen.de>
829 * \date 2016-11-02
830 */
831template <MInt nDim, class SolverType>
833 if(!m_enabled) {
834 return;
835 }
836
837 for(MUint i = 0; i < m_timeSeries.size(); i++) {
838 save(m_timeSeries[i], finalTimeStep);
839 }
840}
841
842
854template <MInt nDim, class SolverType>
856 using namespace std;
857
858 // Check if time step inverval is reached
859 if(solver().getCurrentTimeStep() < timeSeries.m_startTimeStep
860 || solver().getCurrentTimeStep() > timeSeries.m_endTimeStep) {
861 return;
862 }
863
864 // Check for correct timestep interval
865 if(!(solver().getCurrentTimeStep() % timeSeries.m_sampleInterval == 0 || finalTimeStep)) {
866 return;
867 }
868
869 // Compute sampling variables and exchange data for interpolation (if not done already in this time step)
870 solver().calcSamplingVariables(m_solverSamplingVarIds, true);
871
872 // Check if rank belongs to an sampling data MPI communicator
873 // Note: sampling variables are computed by all ranks as the interpolation might require halo cell
874 // values
875 if(timeSeries.m_noLocalPoints < 1 || timeSeries.m_noPoints == 0) {
876 return;
877 }
878
879 // buffer time and timestep
880 if(timeSeries.m_isMpiRoot) {
881 timeSeries.m_timeStepBuffer[timeSeries.m_sampleIndex] = solver().getCurrentTimeStep();
882 timeSeries.m_timeBuffer[timeSeries.m_sampleIndex] = solver().time();
883 }
884
885 // buffer point data states
886 MFloatTensor coordinatesTensorBuf(&timeSeries.m_coordinates[0], timeSeries.m_noLocalPoints, nDim);
887 MFloatTensor stateTensorBuf(&timeSeries.m_stateBuffer[0], timeSeries.m_noLocalPoints, timeSeries.m_maxNoSample + 1,
888 noVars());
889
890 MBool interpolate = m_interpolatePointData;
891 for(MInt i = 0; i < timeSeries.m_noLocalPoints; i++) {
892 MInt offset = 0;
893 for(MUint var = 0; var < m_solverSamplingVarIds.size(); var++) {
894 solver().calcSamplingVarAtPoint(&coordinatesTensorBuf(i, 0), timeSeries.m_elementIds[i],
895 m_solverSamplingVarIds[var], &stateTensorBuf(i, timeSeries.m_sampleIndex, offset),
896 interpolate);
897 offset += m_noSolverSamplingVars[var];
898 }
899 }
900
901 // increment sample index
902 timeSeries.m_sampleIndex++;
903
904 MBool isFirstDataFile = false;
905 // Note: first data file for simulation starting at timestep 0 contains one value more than
906 // all other data files; at a restart the restart time step is not considered here even if it is
907 // equal to m_startTimeStep
908 // if (solver().getCurrentTimeStep() >= timeSeries.m_startTimeStep
909 // && solver().getCurrentTimeStep() <= timeSeries.m_startTimeStep + timeSeries.m_writeInterval) {
910 if(solver().getCurrentTimeStep() >= 0 && solver().getCurrentTimeStep() <= timeSeries.m_writeInterval) {
911 isFirstDataFile = true;
912 }
913
914 const MBool isRestartStep =
915 (solver().restartInterval() > 0 && solver().getCurrentTimeStep() % solver().restartInterval() == 0);
916 const MBool isFirstSample = (isFirstDataFile && timeSeries.m_sampleIndex == 1);
917 const MBool writeAtRestartStep = (isRestartStep && !isFirstSample);
918 const MBool writeFirstDataFile = (timeSeries.m_sampleIndex > timeSeries.m_maxNoSample && isFirstDataFile);
919 const MBool writeDataFile = ((timeSeries.m_sampleIndex + 1 > timeSeries.m_maxNoSample) && !isFirstDataFile);
920
921 // write file if it's time to do so otherwise return
922 if(!(writeDataFile || finalTimeStep || writeAtRestartStep || writeFirstDataFile)) {
923 return;
924 }
925
926 using namespace maia::parallel_io;
927
928 // calculate how many samples have been recorded in total
929 MInt sampleCount = (solver().getCurrentTimeStep() / timeSeries.m_sampleInterval) + 1; // also saving at timeStep 0
930
931 // Correct number of samples if finalTimeStep is not in the writeInterval
932 if(finalTimeStep && !(solver().getCurrentTimeStep() % timeSeries.m_writeInterval == 0)) {
933 sampleCount++;
934 }
935
936 MInt fileNo = 0;
937 if(!finalTimeStep || (solver().getCurrentTimeStep() % timeSeries.m_writeInterval == 0)) {
938 fileNo = (sampleCount - 1) / (timeSeries.m_writeInterval / timeSeries.m_sampleInterval);
939 } else {
940 fileNo = ceil((static_cast<MFloat>(sampleCount - 1)
941 / static_cast<MFloat>(timeSeries.m_writeInterval / timeSeries.m_sampleInterval)));
942 }
943
944 // Output file name
945 std::ostringstream fileName;
946 MString baseFileName = getFileBaseName();
947 fileName << solver().outputDir() << baseFileName << std::to_string(timeSeries.m_fileNo) << "_" << setw(8)
948 << setfill('0') << fileNo << ParallelIo::fileExt();
949
950 ParallelIo file(fileName.str(), PIO_REPLACE, timeSeries.m_mpiComm);
951
952 file.defineArray(PIO_INT, "timeStep", timeSeries.m_sampleIndex);
953 file.defineArray(PIO_FLOAT, "time", timeSeries.m_sampleIndex);
954 file.defineArray(PIO_INT, "sortIndex", timeSeries.m_noPoints);
955
956 file.setAttribute("si[i] = index in input", "description", "sortIndex");
957
958 ParallelIo::size_type dimSizes[] = {timeSeries.m_noPoints, timeSeries.m_sampleIndex, noVars()};
959 file.defineArray(PIO_FLOAT, "pointStates", 3, &dimSizes[0]);
960
961 std::ostringstream descPs;
962 // Please be aware that these attributes get parsed in the corresponding post
963 // processing script. So if you change them please check if it breaks the
964 // script
965 file.setAttribute(descPs.str(), "description", "pointStates");
966 file.setAttribute("point index", "dim_0", "pointStates");
967 file.setAttribute("sample index", "dim_1", "pointStates");
968 file.setAttribute("var index", "dim_2", "pointStates");
969 file.setAttribute(timeSeries.m_inputFileName, "inputFileName", "pointStates");
970
971 MInt varOffset = 0;
972 for(MUint var = 0; var < m_solverSamplingVarIds.size(); var++) {
973 for(MInt i = 0; i < m_noSolverSamplingVars[var]; i++) {
974 file.setAttribute(m_solverSamplingVarNames[var][i], "var_" + std::to_string(varOffset + i), "pointStates");
975 }
976 varOffset += m_noSolverSamplingVars[var];
977 }
978
979 // ensure data is consecutive in memory
980 if(timeSeries.m_sampleIndex < timeSeries.m_maxNoSample + 1) {
981 MFloatTensor stateTensor(&timeSeries.m_stateBuffer[0], timeSeries.m_noLocalPoints, timeSeries.m_maxNoSample + 1,
982 noVars());
983 MFloatMatrix finalStateTensor(&timeSeries.m_stateBuffer[0], timeSeries.m_noLocalPoints, timeSeries.m_sampleIndex,
984 noVars());
985 for(MInt i = 0; i < timeSeries.m_noLocalPoints; i++) {
986 for(MInt j = 0; j < timeSeries.m_sampleIndex; j++) {
987 for(MInt k = 0; k < noVars(); k++) {
988 finalStateTensor(i, j, k) = stateTensor(i, j, k);
989 }
990 }
991 }
992 }
993
994 // root writes timestep and time data
995 if(timeSeries.m_isMpiRoot) {
996 file.setOffset(timeSeries.m_sampleIndex, 0);
997 } else {
998 file.setOffset(0, 0);
999 }
1000 file.writeArray(&timeSeries.m_timeStepBuffer[0], "timeStep");
1001 file.writeArray(&timeSeries.m_timeBuffer[0], "time");
1002
1003 // calculate offset for reverse index and pointStates
1004 ParallelIo::size_type offset, totalCount;
1005 file.calcOffset(timeSeries.m_noLocalPoints, &offset, &totalCount, timeSeries.m_mpiComm);
1006
1007 // write sort index
1008 file.setOffset(timeSeries.m_noLocalPoints, offset);
1009 file.writeArray(&timeSeries.m_sortIndex[0], "sortIndex");
1010
1011 // write state data to file
1012 file.setOffset(timeSeries.m_noLocalPoints, offset, 3);
1013 file.writeArray(&timeSeries.m_stateBuffer[0], "pointStates");
1014
1015 // reset sample index
1016 timeSeries.m_sampleIndex = 0;
1017}
1018
1019
1022template <MInt nDim, class SolverType>
1023void SamplingData<nDim, SolverType>::indexXD(const MInt index, const MInt* const nPoints, MInt* indexXD) {
1024 const MInt noPoints2D = nPoints[0] * nPoints[1];
1025 const MInt maxNoPoints = (nDim == 2) ? noPoints2D : noPoints2D * nPoints[2];
1026 if(index < 0 || index >= maxNoPoints) {
1027 TERMM(1, "invalid index: " + std::to_string(index));
1028 }
1029
1030 // Last index (varies fastest)
1031 indexXD[nDim - 1] = index % nPoints[nDim - 1];
1032 // First/second index in 2D/3D
1033 indexXD[nDim - 2] = ((index - indexXD[nDim - 1]) / nPoints[nDim - 1]) % nPoints[nDim - 2];
1034 IF_CONSTEXPR(nDim == 3) {
1035 // First index in 3D
1036 indexXD[0] = (index - indexXD[1] * nPoints[1] - indexXD[2]) / (nPoints[2] * nPoints[1]);
1037 }
1038}
1039
1040
1043template <MInt nDim, class SolverType>
1045 TRACE();
1046 using namespace maia::parallel_io;
1047
1048 std::ostringstream fileName;
1049 fileName << solver().outputDir() << getFileBaseName() << "coordinates_" << timeSeries.m_fileNo
1050 << ParallelIo::fileExt();
1051 ParallelIo file(fileName.str(), PIO_REPLACE, timeSeries.m_mpiComm);
1052
1053 // Add spatial dimensions
1054 file.setAttribute(nDim, "nDim");
1055
1056 file.defineArray(PIO_INT, "sortIndex", timeSeries.m_noPoints);
1057 file.setAttribute("si[i] = index in input", "description", "sortIndex");
1058
1059 ParallelIo::size_type dimSizes[] = {timeSeries.m_noPoints, nDim};
1060 file.defineArray(PIO_FLOAT, "coordinates", 2, &dimSizes[0]);
1061
1062 ParallelIo::size_type offset, totalCount;
1063 file.calcOffset(timeSeries.m_noLocalPoints, &offset, &totalCount, timeSeries.m_mpiComm);
1064
1065 // Write sort index
1066 file.setOffset(timeSeries.m_noLocalPoints, offset);
1067 file.writeArray(&timeSeries.m_sortIndex[0], "sortIndex");
1068
1069 // Write coordinates
1070 file.setOffset(timeSeries.m_noLocalPoints, offset, 2);
1071 file.writeArray(&timeSeries.m_coordinates[0], "coordinates");
1072}
1073
1074
1076template <MInt nDim, class SolverType>
1078 const MInt NotUsed(fileNo),
1079 std::vector<MFloat>& coordinates) {
1080 TRACE();
1081 if(!isMpiRoot()) {
1082 TERMM(1, "PointData::loadInputFile() should only be called by mpi root process!");
1083 }
1084
1085 return loadPointCoordinatesFromFile(inputFileName, nDim, coordinates);
1086}
1087
1088
1089// Destructor
1090template <MInt nDim, class SolverType>
1092 // Free geometry object if existing
1093 if(m_geometry != nullptr) {
1094 // TODO labels:PP,IO fix deallocation
1095 // delete m_geometry;
1096 }
1097}
1098
1099
1105template <MInt nDim, class SolverType>
1107 const MInt fileNo,
1108 std::vector<MFloat>& coordinates) {
1109 TRACE();
1110 if(!isMpiRoot()) {
1111 TERMM(1, "SurfaceData::loadInputFile() should only be called by mpi root process!");
1112 }
1113
1114 // Create geometry object
1115 auto geometry = new typename GeometryXD<nDim>::type(0, inputFileName, MPI_COMM_SELF);
1116 // Store pointer in class member variable
1117 m_geometry = geometry;
1118
1119 // Determine number of geometry/surface elements
1120 MInt noElements = geometry->GetNoElements();
1121 coordinates.resize(nDim * noElements);
1122
1123 std::array<MFloat, nDim * nDim> vertex;
1124
1125 // Compute element centroids and store in coordinates
1126 for(MInt i = 0; i < noElements; i++) {
1127 geometry->elements[i].getVertices(&vertex[0]);
1128 geometry->elements[i].calcCentroid(&vertex[0], &coordinates[i * nDim]);
1129 }
1130
1131 // Write specific data for all elements into an output file
1132 saveGeomInfo(inputFileName, fileNo, &coordinates[0]);
1133
1134 return noElements;
1135}
1136
1137
1151template <MInt nDim, class SysEqn>
1152void SurfaceData<nDim, SysEqn>::saveGeomInfo(const MString inputFileName, const MInt fileNo, MFloat* coordinates) {
1153 TRACE();
1154 using namespace maia::parallel_io;
1155
1156 const MInt noElements = m_geometry->GetNoElements();
1157 // Define output file
1158 std::ostringstream fileName;
1159 fileName << solver().outputDir() << "surface_geometryInfo_" << fileNo << ParallelIo::fileExt();
1160
1161 ParallelIo file(fileName.str(), PIO_REPLACE, MPI_COMM_SELF);
1162 file.setAttribute(inputFileName, "inputFileName");
1163
1164 // Get vertices of an element and calculate length/surface of element
1165 std::vector<MFloat> vertices(noElements * nDim * nDim);
1166 std::vector<MFloat> geomMeasures(noElements);
1167 std::vector<MFloat> normalVecs(noElements * nDim);
1168
1169 MFloat geomCenter[3]{};
1170
1171 // Check if geometry center is defined by user
1172 if(Context::propertyExists("surfaceDataGeometryCenter_" + std::to_string(fileNo), solverId())) {
1173 for(MInt i = 0; i < nDim; i++) {
1174 geomCenter[i] =
1175 Context::getSolverProperty<MFloat>("surfaceDataGeometryCenter_" + std::to_string(fileNo), solverId(), AT_, i);
1176 }
1177 } else {
1178 // Determine geometry center as average of all element centroids
1179 for(MInt i = 0; i < noElements; i++) {
1180 for(MInt dim = 0; dim < nDim; dim++) {
1181 geomCenter[dim] += coordinates[i * nDim + dim];
1182 }
1183 }
1184 for(MInt dim = 0; dim < nDim; dim++) {
1185 geomCenter[dim] /= static_cast<MFloat>(noElements);
1186 }
1187 }
1188
1189 m_log << "Surface geometry info: geomCenter =";
1190 for(MInt dim = 0; dim < nDim; dim++) {
1191 m_log << " " << geomCenter[dim];
1192 }
1193 m_log << std::endl;
1194
1195 MInt counter = 0;
1196 for(MInt i = 0; i < noElements; i++) {
1197 for(MInt j = 0; j < nDim; j++) {
1198 for(MInt k = 0; k < nDim; k++) {
1199 vertices[counter] = m_geometry->elements[i].m_vertices[j][k];
1200 counter++;
1201 }
1202 }
1203
1204 auto& element = m_geometry->elements[i];
1205 // Calc surface area/length of every element
1206 IF_CONSTEXPR(nDim == 3) {
1207 // Define two vectors for triangular surface in 3D
1208 const MFloat v12_x = element.m_vertices[0][0] - element.m_vertices[1][0];
1209 const MFloat v12_y = element.m_vertices[0][1] - element.m_vertices[1][1];
1210 const MFloat v12_z = element.m_vertices[0][2] - element.m_vertices[1][2];
1211 const MFloat v13_x = element.m_vertices[0][0] - element.m_vertices[2][0];
1212 const MFloat v13_y = element.m_vertices[0][1] - element.m_vertices[2][1];
1213 const MFloat v13_z = element.m_vertices[0][2] - element.m_vertices[2][2];
1214
1215 // Cross product
1216 const MFloat v_x = v12_y * v13_z - v12_z * v13_y;
1217 const MFloat v_y = v12_z * v13_x - v12_x * v13_z;
1218 const MFloat v_z = v12_x * v13_y - v12_y * v13_x;
1219 // Surface area is 1/2 the length of the cross product
1220 geomMeasures[i] = 0.5 * sqrt(pow(v_x, 2) + pow(v_y, 2) + pow(v_z, 2));
1221 }
1222 else {
1223 const MFloat p1_x = element.m_vertices[0][0];
1224 const MFloat p2_x = element.m_vertices[1][0];
1225 const MFloat p1_y = element.m_vertices[0][1];
1226 const MFloat p2_y = element.m_vertices[1][1];
1227 // Element length
1228 geomMeasures[i] = sqrt(pow(p1_x - p2_x, 2) + pow(p1_y - p2_y, 2));
1229 }
1230
1231 std::array<MFloat, nDim * nDim> vertex;
1232 m_geometry->elements[i].getVertices(&vertex[0]);
1233
1234 // Calculate surface normal vector
1235 m_geometry->elements[i].calcNormal(&vertex[0], &normalVecs[i * nDim]);
1236
1237 // Compute dot product of surface normal vector and the vector from the geometric center to the
1238 // surface centroid
1239 MFloat dotProduct = 0.0;
1240 for(MInt k = 0; k < nDim; k++) {
1241 const MFloat vecCenter = coordinates[i * nDim + k] - geomCenter[k];
1242 dotProduct += normalVecs[i * nDim + k] * vecCenter;
1243 }
1244 // If the dot product is positive the two vectors are pointing in the same 'direction', i.e.,
1245 // the angle between the vectors is less than 90deg; thus if not >0 change the sign of the
1246 // normal vector to point outside the geometry
1247 if(!(dotProduct > 0)) {
1248 for(MInt k = 0; k < nDim; k++) {
1249 normalVecs[i * nDim + k] = -normalVecs[i * nDim + k];
1250 }
1251 }
1252 }
1253
1254 const MFloat totalGeomMeasure = std::accumulate(geomMeasures.begin(), geomMeasures.end(), 0.0);
1255 const MString measure = (nDim == 3) ? "area" : "length";
1256 m_log << "Total segment " << measure << ": " << totalGeomMeasure << std::endl;
1257
1258 // TODO labels:PP for DEBUG
1259 std::stringstream dumpFileName;
1260 dumpFileName << solver().outputDir() << "surface_geometryInfo_" << fileNo << ".dump";
1261 // DEBUG output for plotting
1262 FILE* datei;
1263 datei = fopen(dumpFileName.str().c_str(), "w");
1264 /* datei = fopen("geometryInfo.dump", "a+"); */
1265 for(MInt i = 0; i < noElements; i++) {
1266 fprintf(datei, "%d", i);
1267
1268 for(MInt j = 0; j < nDim; j++) {
1269 fprintf(datei, " %.8f", coordinates[i * nDim + j]);
1270 }
1271
1272 for(MInt j = 0; j < nDim; j++) {
1273 fprintf(datei, " %.8f", normalVecs[i * nDim + j]);
1274 }
1275 fprintf(datei, "\n");
1276 }
1277 fclose(datei);
1278
1279 const MInt sizeArray1 = noElements * nDim;
1280 const MInt sizeArray2 = noElements * nDim * nDim;
1281
1282 // Write out geomInfo in output file
1283 file.defineArray(PIO_FLOAT, "vertices", sizeArray2);
1284 file.defineArray(PIO_FLOAT, "centroid", sizeArray1);
1285 file.defineArray(PIO_FLOAT, "normalVector", sizeArray1);
1286
1287 file.defineArray(PIO_FLOAT, "geomMeasure", noElements);
1288 IF_CONSTEXPR(nDim == 3) { file.setAttribute("area", "description", "geomMeasure"); }
1289 else {
1290 file.setAttribute("length", "description", "geomMeasure");
1291 }
1292
1293 file.setOffset(sizeArray2, 0);
1294 file.writeArray(&vertices[0], "vertices");
1295
1296 file.setOffset(sizeArray1, 0);
1297 file.writeArray(&coordinates[0], "centroid");
1298
1299 file.setOffset(sizeArray1, 0);
1300 file.writeArray(&normalVecs[0], "normalVector");
1301
1302 file.setOffset(noElements, 0);
1303 file.writeArray(&geomMeasures[0], "geomMeasure");
1304}
1305
1306
1308template <MInt nDim, class SolverType>
1310 TRACE();
1311
1312 const MString volumeShape =
1313 Context::getSolverProperty<MString>(getBaseName() + "DataShape_" + std::to_string(fileNo), solverId(), AT_);
1314
1315 MInt noInputValuesFloat = -1;
1316 MInt noInputValuesInt = -1;
1317
1318 if(volumeShape == "box") {
1319 // Box defined by two points and number of points in each spatial direction
1320 noInputValuesFloat = 2 * nDim;
1321 noInputValuesInt = nDim;
1322 } else if(volumeShape == "cylinder") {
1323 IF_CONSTEXPR(nDim == 2) { TERMM(1, "Cylinder shape not defined in 2D."); }
1324 // Cylinder defined by first center point, second center axial position and radius
1325 noInputValuesFloat = nDim + 2;
1326 // TODO labels:PP allow to generate non-equidistant points in radial direction to avoid that lots of
1327 // points are clustered in the center of the cylinder
1328 // Number of points in each direction: axial, radial, azimuthal; Cylinder axial direction
1329 // Note: given number of points in radial direction does not include center point
1330 noInputValuesInt = nDim + 1;
1331 } else {
1332 TERMM(1, "Unknown volume data shape: " + volumeShape);
1333 }
1334 m_volumeShape.push_back(volumeShape);
1335
1336 if(noInputValuesFloat < 1 || noInputValuesInt < 1) {
1337 TERMM(1, "Invalid number of input values for volume data shape " + volumeShape + ": "
1338 + std::to_string(noInputValuesFloat) + " " + std::to_string(noInputValuesInt));
1339 }
1340
1341 m_log << "Volume data #" << fileNo << ": " << volumeShape << " with parameters";
1342
1343 // Read float parameters
1344 std::vector<MFloat> paramFloat(noInputValuesFloat);
1345 for(MInt i = 0; i < noInputValuesFloat; i++) {
1346 paramFloat[i] = Context::getSolverProperty<MFloat>(getBaseName() + "DataParameterFloat_" + std::to_string(fileNo),
1347 solverId(), AT_, i);
1348 m_log << " " << paramFloat[i];
1349 }
1350 m_volumeParameterFloat.push_back(paramFloat);
1351
1352 // Read int parameters
1353 std::vector<MInt> paramInt(noInputValuesInt);
1354 for(MInt i = 0; i < noInputValuesInt; i++) {
1355 paramInt[i] = Context::getSolverProperty<MInt>(getBaseName() + "DataParameterInt_" + std::to_string(fileNo),
1356 solverId(), AT_, i);
1357 m_log << " " << paramInt[i];
1358 }
1359 m_volumeParameterInt.push_back(paramInt);
1360
1361 m_log << std::endl;
1362}
1363
1364
1366template <MInt nDim, class SolverType>
1368 TRACE();
1369
1370 MInt noPoints = 0;
1371 if(m_volumeShape[fileNo] == "box") {
1372 noPoints = 1;
1373 for(MInt i = 0; i < nDim; i++) {
1374 noPoints *= m_volumeParameterInt[fileNo][i];
1375 }
1376 } else if(m_volumeShape[fileNo] == "cylinder") {
1377 // nZ * (nPhi * nR + 1) // +1 for center point
1378 noPoints =
1379 m_volumeParameterInt[fileNo][0] * (m_volumeParameterInt[fileNo][1] * m_volumeParameterInt[fileNo][2] + 1);
1380 } else {
1381 TERMM(1, "Unknown volume data shape: " + m_volumeShape[fileNo]);
1382 }
1383
1384 return noPoints;
1385}
1386
1387
1389template <MInt nDim, class SolverType>
1390void VolumeData<nDim, SolverType>::getSamplingPoint(const MInt fileNo, const MInt pointId, MFloat* const coordinates) {
1391 TRACE();
1392
1393 if(pointId < 0 || pointId > noSamplingPoints(fileNo)) {
1394 TERMM(1, "Invalid point id");
1395 }
1396
1397 // Generate coordinates depending on volume shape
1398 std::vector<MInt> indexNd(3);
1399 if(m_volumeShape[fileNo] == "box") {
1400 // Points for box are created with [x,y,z] = [i,j,k] ordering, i.e., k varies the fastest
1401 indexXD(pointId, &m_volumeParameterInt[fileNo][0], &indexNd[0]);
1402
1403 for(MInt i = 0; i < nDim; i++) {
1404 const MFloat minCoord = m_volumeParameterFloat[fileNo][i];
1405 const MFloat maxCoord = m_volumeParameterFloat[fileNo][nDim + i];
1406 const MFloat delta = (maxCoord - minCoord) / static_cast<MFloat>(m_volumeParameterInt[fileNo][i] - 1);
1407 coordinates[i] = minCoord + indexNd[i] * delta;
1408 }
1409 } else if(m_volumeShape[fileNo] == "cylinder") {
1410 /* TERMM(1, "TODO: test volume data sampling with cylinder shape generation"); */
1411 // ordering: z, r, phi
1412 // Note: center point (r=0) only appears once
1413 const MInt nPointsPerCirc = m_volumeParameterInt[fileNo][1] * m_volumeParameterInt[fileNo][2] + 1;
1414 indexNd[0] = floor(pointId / nPointsPerCirc); // z
1415
1416 const MInt tmpIndex = pointId - indexNd[0] * nPointsPerCirc;
1417 indexNd[1] = ceil(static_cast<MFloat>(tmpIndex) / static_cast<MFloat>(m_volumeParameterInt[fileNo][2]));
1418 indexNd[2] = std::max((tmpIndex - 1) % m_volumeParameterInt[fileNo][2], 0);
1419
1420 const MInt dir = m_volumeParameterInt[fileNo][3];
1421 const MInt dir2 = (dir == 0) ? 1 : 0;
1422 const MInt dir3 = 3 - dir - dir2;
1423
1424 const MFloat* startPos = &m_volumeParameterFloat[fileNo][0];
1425 const MFloat nz = static_cast<MFloat>(m_volumeParameterInt[fileNo][0]);
1426 const MFloat dist_z = static_cast<MFloat>(indexNd[0]) * (startPos[3] - startPos[dir]) / (nz - 1);
1427
1428 const MFloat r = m_volumeParameterFloat[fileNo][4];
1429 const MFloat nr = m_volumeParameterInt[fileNo][1];
1430 const MFloat dist_r = static_cast<MFloat>(indexNd[1]) * r / nr;
1431
1432 const MFloat nphi = m_volumeParameterInt[fileNo][2];
1433 const MFloat phi = static_cast<MFloat>(indexNd[2]) * 2.0 * PI / nphi;
1434
1435 coordinates[dir] = startPos[dir] + dist_z;
1436 coordinates[dir2] = startPos[dir2] + dist_r * sin(phi);
1437 coordinates[dir3] = startPos[dir3] + dist_r * cos(phi);
1438 } else {
1439 TERMM(1, "Unknown volume data shape: " + m_volumeShape[fileNo]);
1440 }
1441}
1442
1443#endif // SAMPLINGDATA_H_
static MBool propertyExists(const MString &name, MInt solver=m_noSolvers)
This function checks if a property exists in general.
Definition: context.cpp:494
MFloat ** m_vertices
This class is responsible for the point data feature. It records the state of all sampling variables ...
Definition: samplingdata.h:164
MInt loadInputFile(const MString inputFileName, const MInt NotUsed(fileNo), std::vector< MFloat > &coordinates) override
Loads an input file with points for the point data feature.
PointData(SolverType &solver_)
Definition: samplingdata.h:176
virtual ~PointData()
Definition: samplingdata.h:177
MString getBaseName() const override
Return base name of derived class, e.g. used for naming properties and output file names.
Definition: samplingdata.h:182
This base class is responsible for the sampling data feature that provides the required general metho...
Definition: samplingdata.h:35
void init(SamplingDataSeries &timeSeries)
Definition: samplingdata.h:590
std::vector< SamplingDataSeries > m_timeSeries
Holds all properties and buffers for each input file.
Definition: samplingdata.h:138
virtual MBool generatePoints() const
Default behaviour for point generation which can be overwritten in derived class.
Definition: samplingdata.h:75
MBool isMpiRoot() const
Definition: samplingdata.h:119
virtual MString getBaseName() const
Return base name of derived class, e.g. used for naming properties and output file names.
Definition: samplingdata.h:53
std::vector< MInt > m_solverSamplingVarIds
List of sampling variables.
Definition: samplingdata.h:141
SolverType & m_solver
Definition: samplingdata.h:150
virtual MBool hasInputDataFile(const MInt fileNo) const
Check if an input file exists, might be overwritten in derived class if points are generated.
Definition: samplingdata.h:64
virtual MInt loadInputFile(const MString NotUsed(inputFileName), const MInt NotUsed(fileNo), std::vector< MFloat > &NotUsed(coordinates))
Definition: samplingdata.h:46
MPI_Comm mpiComm() const
Definition: samplingdata.h:120
const SolverType & solver() const
Definition: samplingdata.h:130
MBool m_interpolatePointData
Definition: samplingdata.h:135
MInt solverId() const
Definition: samplingdata.h:118
void setInputOutputProperties()
virtual void getSamplingPoint(const MInt NotUsed(fileNo), const MInt NotUsed(pointId), MFloat *const NotUsed(coordinates))
Return the coordinates of the sampling point with the given id when generating points.
Definition: samplingdata.h:103
void save(SamplingDataSeries &timeSeries, MBool finalTimeStep)
Saves sampling data Saves all states of conservative variables at specified points....
Definition: samplingdata.h:855
SolverType & solver()
Return reference to solver.
Definition: samplingdata.h:129
virtual MInt noSamplingPoints(const MInt NotUsed(fileNo))
Return the number of sampling points which are generated by the derived class (no input file)
Definition: samplingdata.h:97
MString getFileBaseName() const
Return base name of files for used sampling feature.
Definition: samplingdata.h:126
std::vector< MInt > m_noSolverSamplingVars
Number of variables for each sampling variable.
Definition: samplingdata.h:143
MBool enabled() const
Definition: samplingdata.h:42
void save(MBool finalTimestep)
Definition: samplingdata.h:832
MInt noVars() const
Return total number of sampling variables.
Definition: samplingdata.h:124
void saveSamplingPointCoordinates(SamplingDataSeries &timeSeries)
Save point coordinates of time series.
SamplingData(SolverType &solver)
Definition: samplingdata.h:37
std::vector< std::vector< MString > > m_solverSamplingVarNames
List of variable names for each sampling variable.
Definition: samplingdata.h:145
void indexXD(const MInt index, const MInt *const nPoints, MInt *indexXD)
Convert 1D index into 2D/3D index.
virtual void readAdditionalProperties(const MInt NotUsed(fileNo))
Read additional properties which are required by the derived class.
Definition: samplingdata.h:115
This auxiliary class contains buffers and properties for each input file.
Definition: samplingdata.h:278
std::vector< MInt > m_elementIds
Definition: samplingdata.h:311
std::vector< MFloat > m_coordinates
Definition: samplingdata.h:309
const MInt m_writeInterval
Definition: samplingdata.h:299
const MInt m_fileNo
Definition: samplingdata.h:335
std::vector< MInt > m_timeStepBuffer
Definition: samplingdata.h:319
std::vector< MFloat > m_stateBuffer
Definition: samplingdata.h:327
const MInt m_startTimeStep
Definition: samplingdata.h:301
std::vector< MInt > m_sortIndex
Definition: samplingdata.h:313
const MInt m_sampleInterval
Definition: samplingdata.h:297
SamplingDataSeries(const MString inputFileName, const MBool generatePoints, const MInt sampleInterval, const MInt writeInterval, const MInt startTimeStep, const MInt endTimeStep, const MInt noFile)
Definition: samplingdata.h:281
const MInt m_endTimeStep
Definition: samplingdata.h:303
const MString m_inputFileName
Definition: samplingdata.h:293
const MBool m_generatePoints
Definition: samplingdata.h:295
std::vector< MFloat > m_timeBuffer
Definition: samplingdata.h:321
This class is a ScratchSpace.
Definition: scratch.h:758
size_type size() const
Definition: scratch.h:302
void fill(T val)
fill the scratch with a given value
Definition: scratch.h:311
iterator end()
Definition: scratch.h:281
iterator begin()
Definition: scratch.h:273
Surface data sampling class. Records all sampling variables on all surface elements and outputs addit...
Definition: samplingdata.h:190
MInt loadInputFile(const MString inputFileName, const MInt fileNo, std::vector< MFloat > &coordinates) override
Load an input file for the surface data sampling feature.
SurfaceData(SolverType &solver_)
Definition: samplingdata.h:202
GeometryXD< nDim >::type * m_geometry
Definition: samplingdata.h:214
virtual ~SurfaceData()
MString getBaseName() const override
Return base name of derived class, e.g. used for naming properties and output file names.
Definition: samplingdata.h:210
void saveGeomInfo(const MString inputFileName, const MInt fileNo, MFloat *coordinates)
Store geometric information for all elements of a surface.
Class to handle sampling of volume data.
Definition: samplingdata.h:220
MString getBaseName() const override
Return base name of derived class, e.g. used for naming properties and output file names.
Definition: samplingdata.h:236
std::vector< std::vector< MInt > > m_volumeParameterInt
Definition: samplingdata.h:267
MString getInputDataFile(const MInt NotUsed(fileNo)) const
Definition: samplingdata.h:253
void readAdditionalProperties(const MInt fileNo) override
Read additional properties for volume sampling.
MBool generatePoints() const override
Always generate points.
Definition: samplingdata.h:240
void getSamplingPoint(const MInt fileNo, const MInt pointId, MFloat *const coordinates) override
Return sampling point coordinates.
MInt solverId() const
Definition: samplingdata.h:118
virtual ~VolumeData()
Definition: samplingdata.h:234
std::vector< std::vector< MFloat > > m_volumeParameterFloat
Definition: samplingdata.h:266
MInt noSamplingPoints(const MInt fileNo) override
Return number of volume sampling points.
MBool hasInputDataFile(const MInt fileNo) const override
Points are generated not read from file, just check for property 'volumeDataShape_*'.
Definition: samplingdata.h:243
VolumeData(SolverType &solver_)
Definition: samplingdata.h:233
std::vector< MString > m_volumeShape
Definition: samplingdata.h:265
SolverType
Definition: enums.h:22
MInt loadPointCoordinatesFromFile(const MString inputFileName, const MInt nDim, std::vector< MFloat > &coordinates)
Loads point coordinates from an input file.
Definition: functions.cpp:182
MInt globalNoDomains()
Return global number of domains.
InfoOutFile m_log
int32_t MInt
Definition: maiatypes.h:62
uint32_t MUint
Definition: maiatypes.h:63
std::basic_string< char > MString
Definition: maiatypes.h:55
double MFloat
Definition: maiatypes.h:52
int64_t MLong
Definition: maiatypes.h:64
bool MBool
Definition: maiatypes.h:58
int MPI_Comm_create(MPI_Comm comm, MPI_Group group, MPI_Comm *newcomm, const MString &name, const MString &varname)
same as MPI_Comm_create, but updates the number of MPI communicators
int MPI_Group_incl(MPI_Group group, int n, const int ranks[], MPI_Group *newgroup, const MString &name)
same as MPI_Group_incl
int MPI_Comm_group(MPI_Comm comm, MPI_Group *group, const MString &name, const MString &varname)
same as MPI_Comm_group
int MPI_Reduce(const void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, int root, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Reduce
int MPI_Allreduce(const void *sendbuf, void *recvbuf, int count, MPI_Datatype datatype, MPI_Op op, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Allreduce
int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm, const MString &name, const MString &varname)
same as MPI_Bcast
int MPI_Allgather(const void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, MPI_Comm comm, const MString &name, const MString &sndvarname, const MString &rcvvarname)
same as MPI_Allgather
int MPI_Group_free(MPI_Group *group, const MString &name)
same as MPI_Group_free
Namespace for auxiliary functions/classes.
PARALLELIO_DEFAULT_BACKEND ParallelIo
Definition: parallelio.h:292
Definition: contexttypes.h:19