MAIA bb96820c
Multiphysics at AIA
Loading...
Searching...
No Matches
dgcartesiangalerkinprojection.cpp
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
8
9#include <array>
10#include "IO/context.h"
11#include "MEMORY/scratch.h"
12#include "UTIL/tensor.h"
13#include "UTIL/timer.h"
14#include "property.h"
15
16using namespace maia::dg::interpolation;
17using namespace maia::dg::projection;
18using namespace std;
19
20
31template <MInt nDim>
32DgGalerkinProjection<nDim>::DgGalerkinProjection(const MInt polyDeg, const MInt noLevels, const MFloat lengthLevel0,
33 const MInt solverId) {
34 TRACE();
35
36 init(polyDeg, noLevels, lengthLevel0, solverId);
37}
38
39
44template <MInt nDim>
46 TRACE();
47
48 m_polyDeg = -1;
49 m_noNodesXD = -1;
50 m_noLevels = -1;
51 m_lengthLevel0 = -1.0;
52 m_solverId = -1;
53 m_wInt.clear(), m_MTD.clear();
54 m_polynomials.clear();
55 m_cellCenters.clear();
56 m_cellLengths.clear();
57}
58
59
71template <MInt nDim>
73 const MInt noLevels,
74 const MFloat lengthLevel0,
75 const MInt solverId) {
76 TRACE();
77
78 // Set member variables
79 m_polyDeg = polyDeg;
80 m_noLevels = noLevels;
81 m_lengthLevel0 = lengthLevel0;
82 m_noNodesXD = ipow(polyDeg + 1, nDim);
83 m_solverId = solverId;
84
85 const MInt noNodes1D = polyDeg + 1;
86 const MInt maxNoCellsOnLevel = IPOW2(noLevels - 1);
87
88 // Allocate space for member variables
89 m_MTD.resize(m_noLevels);
90 m_polynomials.resize(m_noLevels);
91 for(MInt level = 0; level < m_noLevels; level++) {
92 const MInt noCells1D = IPOW2(level);
93 const MInt noCellsOnLevel = ipow(noCells1D, nDim);
94
95 m_MTD[level].resize(noCellsOnLevel, m_noNodesXD);
96 m_polynomials[level].resize(noCells1D, noNodes1D, noNodes1D);
97 }
98
99 m_cellCenters.resize(noLevels, maxNoCellsOnLevel);
100 m_cellLengths.resize(noLevels);
101
102 // Calculate cell center positions and cell lengths for all levels
103 for(MInt l = 0; l < noLevels; l++) {
104 // Store cell length for this level
105 const MInt noCellsOnLevel = IPOW2(l);
106 const MFloat cellLength = 2.0 / noCellsOnLevel;
107 m_cellLengths[l] = cellLength;
108
109 // Store cell center positions
110 for(MInt c = 0; c < noCellsOnLevel; c++) {
111 m_cellCenters(l, c) = -1.0 + 0.5 * cellLength + c * cellLength;
112 }
113 }
114
115 // Calculate the needed interpolation matrices
116 calcInterpolationMatrices();
117}
118
119
147template <MInt nDim>
149 TRACE();
150
151 // Check for SBP Mode
152 const MBool defaultSbpMode = false;
153 const MBool sbpMode = Context::getSolverProperty<MBool>("sbpMode", m_solverId, AT_, &defaultSbpMode);
154
155 // Determine SBP Operator
156 const MString defaultSbpOperator = "";
157 const MString sbpOperator = Context::getSolverProperty<MString>("sbpOperator", m_solverId, AT_, &defaultSbpOperator);
158
159 MInt initNoNodes1D = -1;
160 initNoNodes1D = Context::getSolverProperty<MInt>("initNoNodes", m_solverId, AT_, &initNoNodes1D);
161
162 const MInt noNodes1D = sbpMode ? initNoNodes1D : (m_polyDeg + 1);
163 const MInt maxNoCellsOnLevel = IPOW2(m_noLevels - 1);
164
165 // Determine DG integration method (same as in dgsolver.cpp)
166 MString defaultDgIntegrationMethod = "DG_INTEGRATE_GAUSS";
167 const MInt dgIntegrationMethod = string2enum(
168 Context::getSolverProperty<MString>("dgIntegrationMethod", m_solverId, AT_, &defaultDgIntegrationMethod));
169
170 // Determine DG polynomial type (same as in dgsolver.cpp)
171 MString defaultDgPolynomialType = "DG_POLY_LEGENDRE";
172 const MInt dgPolynomialType =
173 string2enum(Context::getSolverProperty<MString>("dgPolynomialType", m_solverId, AT_, &defaultDgPolynomialType));
174
175 // Convert integers to enums
176 DgPolynomialType polyType = static_cast<DgPolynomialType>(dgPolynomialType);
177 DgIntegrationMethod intMethod = static_cast<DgIntegrationMethod>(dgIntegrationMethod);
178
179 // Create interpolation object
180 DgInterpolation interpolation(m_polyDeg, polyType, noNodes1D, intMethod, sbpMode, sbpOperator);
181
182 // Create convenience pointers
183 const MFloatVector& wInt = interpolation.m_wInt;
184 const MFloatVector& wBary = interpolation.m_wBary;
185 const MFloatVector& nodes = interpolation.m_nodes;
186
187 // Store integration weights in class for later use
188 m_wInt = wInt;
189
190 // Storage for evaluating the lagrange polynomials at a given position
191 ScratchSpace<MFloat> polynomials(m_polyDeg + 1, AT_, "polynomials");
192 // Storage for inverse mass matrix (diagonal)
193 ScratchSpace<MFloat> MT_inverse(m_noNodesXD, AT_, "MT_inverse");
194 // Storage for 1D-vectors
195 ScratchSpace<MFloat> vector1D(m_noLevels, maxNoCellsOnLevel, noNodes1D, AT_, "vector1D");
196
197 std::array<MInt, nDim> index;
198 // Calculate inverted mass matrix with the integration weights
199 IF_CONSTEXPR(nDim == 2) {
200 for(MInt i = 0; i < m_noNodesXD; i++) {
201 indices<nDim>(i, noNodes1D, &index[0]);
202 MT_inverse[i] = 1.0 / (wInt(index[0]) * wInt(index[1]));
203 }
204 }
205 else {
206 for(MInt i = 0; i < m_noNodesXD; i++) {
207 indices<nDim>(i, noNodes1D, &index[0]);
208 MT_inverse[i] = 1.0 / (wInt(index[0]) * wInt(index[1]) * wInt(index[2]));
209 }
210 }
211
212 // Compute 1D-vectors
213 // i.e. on all relative refinement levels: the weight of each donor cell on
214 // this level for each target node of the element (in one dimension)
215 // Loop over all (relative) refinement levels, start with highest level
216 for(MInt level = m_noLevels - 1; level >= 0; level--) {
217 const MInt noCellsOnLevel = IPOW2(level);
218 const MFloat lengthOnLevel = m_cellLengths[level];
219
220 // Loop over all possible donor cells on this level
221 for(MInt l = 0; l < noCellsOnLevel; l++) {
222 // Loop over all target nodes
223 for(MInt j = 0; j < noNodes1D; j++) {
224 MFloat sumXi = 0.0;
225 // On the finest level do the integration by quadrature
226 if(level == m_noLevels - 1) {
227 // Loop over quadrature points
228 for(MInt k = 0; k < noNodes1D; k++) {
229 // Node position on reference element [-1,1]
230 const MFloat xiPos = m_cellCenters(level, l) + 0.5 * lengthOnLevel * nodes[k];
231
232 // Evaluate Lagrange polynomials
233 if(sbpMode) {
234 calcLinearInterpolationBase(xiPos, noNodes1D, &nodes[0], &polynomials[0]);
235 } else {
236 calcLagrangeInterpolatingPolynomials(xiPos, m_polyDeg, &nodes[0], &wBary[0], &polynomials[0]);
237 }
238 // Calculate weight and add to integral
239 const MFloat weight = 0.5 * lengthOnLevel * wInt[k];
240 sumXi += weight * polynomials[j];
241 }
242 } else {
243 // Combine the integrals of the finer level
244 sumXi = vector1D(level + 1, 2 * l, j) + vector1D(level + 1, 2 * l + 1, j);
245 }
246
247 // Store projection information
248 vector1D(level, l, j) = sumXi;
249 }
250 }
251 }
252
253 // TODO labels:DG,toenhance use symmetry to save on memory and computations?
254 MInt cellIndex[MAX_SPACE_DIMENSIONS];
255 // Calculate projection vectors M_TD for all cells on all levels (Cartesian
256 // product of 1D-vectors)
257 for(MInt level = 0; level < m_noLevels; level++) {
258 const MInt noCells1D = IPOW2(level);
259 const MInt noCellsOnLevel = ipow(noCells1D, nDim);
260
261 // Loop over all target nodes
262 for(MInt nodeId = 0; nodeId < m_noNodesXD; nodeId++) {
263 // Compute node index in all dimensons from nodeId
264 indices<nDim>(nodeId, noNodes1D, &index[0]);
265
266 // Loop over all cells on this level
267 for(MInt i = 0; i < noCellsOnLevel; i++) {
268 // Compute cell index in all dimensions
269 indices<nDim>(i, noCells1D, cellIndex);
270
271 // Assemble projection vector M_TD
272 m_MTD[level](i, nodeId) = 1.0;
273 for(MInt d = 0; d < nDim; d++) {
274 m_MTD[level](i, nodeId) *= vector1D(level, cellIndex[d], index[d]);
275 }
276 // Premultiply with inverted mass matrix
277 m_MTD[level](i, nodeId) *= MT_inverse[nodeId];
278 }
279 }
280 }
281
282 // Evaluate polynomials for error calculation
283 for(MInt level = 0; level < m_noLevels; level++) {
284 const MInt noCellsOnLevel = IPOW2(level);
285 const MFloat lengthOnLevel = m_cellLengths[level];
286
287 // Loop over all possible donor cells on this level
288 for(MInt l = 0; l < noCellsOnLevel; l++) {
289 // Loop over quadrature points
290 for(MInt k = 0; k < noNodes1D; k++) {
291 // Node position
292 const MFloat xiPos = m_cellCenters(level, l) + 0.5 * lengthOnLevel * nodes[k];
293
294 // Evaluate Lagrange polynomials
295 calcLagrangeInterpolatingPolynomials(xiPos, m_polyDeg, &nodes[0], &wBary[0], &m_polynomials[level](l, k, 0));
296 }
297 }
298 }
299}
300
301
327template <MInt nDim>
328void DgGalerkinProjection<nDim>::calcProjectionInformation(const MInt noDonorCells, const MInt* donorCellLevels,
329 const MFloat* donorCellCoordinates, const MInt targetLevel,
330 const MFloat* targetCellCenter, std::vector<MInt>& donorIds,
331 std::vector<MInt>& donorLevels) const {
332 TRACE();
333
334 // Calculates the position on the unit element corresponding to the cell with
335 // given center and width.
336 auto positionOnUnitElement = [](MFloat pos, MFloat center, MFloat width) { return 2 * (pos - center) / width; };
337
338 // Define epsilon according to CartesianGrid constructor
339 const MFloat eps = 1.0 / FPOW2(30) * m_lengthLevel0;
340
341 const MFloatTensor donorCellCoords(const_cast<MFloat*>(donorCellCoordinates), noDonorCells, nDim);
342 const MFloat cellLength = m_lengthLevel0 * FFPOW2(targetLevel);
343 MInt intervals[MAX_SPACE_DIMENSIONS];
344
345 IF_CONSTEXPR(nDim == 2) {
346 intervals[2] = 0; // Not set in 2D, prevent uninitialized value valgrind errors
347 }
348
349 // Determine maximum level difference between donor cells and target cell
350 const MInt maxDonorLevel =
351 *(std::max_element(&donorCellLevels[0], &donorCellLevels[0] + noDonorCells)) - targetLevel + 1;
352
353 // Initialize storage for mapped volume, i.e. consider each possible cell on all levels
354 std::vector<std::vector<MInt>> mappedVolume(maxDonorLevel);
355 for(MInt i = 0; i < maxDonorLevel; i++) {
356 const MInt noCellsOnLvl = ipow(IPOW2(i), nDim);
357 mappedVolume[i].resize(noCellsOnLvl);
358 std::fill(mappedVolume[i].begin(), mappedVolume[i].end(), 0);
359 }
360
361 // Loop over all donor cells
362 for(MInt l = 0; l < noDonorCells; l++) {
363 // Relative level of donor cell
364 const MInt donorLevel = donorCellLevels[l] - targetLevel;
365
366 const MInt noCells1D = IPOW2(donorLevel);
367 const MInt noCells1D3 = (nDim == 3) ? noCells1D : 1;
368
369 // Determine position of donor cell relative to target cell
370 for(MInt d = 0; d < nDim; d++) {
371 const MFloat posOnUnitElem = positionOnUnitElement(donorCellCoords(l, d), targetCellCenter[d], cellLength);
372
373 intervals[d] = -1;
374 // Find interval for current coordinate direction
375 for(MInt i = 0; i < noCells1D; i++) {
376 const MFloat lowerBound = m_cellCenters(donorLevel, i) - 0.5 * m_cellLengths[donorLevel];
377 const MFloat upperBound = m_cellCenters(donorLevel, i) + 0.5 * m_cellLengths[donorLevel];
378
379 // Check if the donor cell center is contained in the current cell
380 // interval and if it is 'roughly' the same as the current cell center
381 if(lowerBound < posOnUnitElem && posOnUnitElem < upperBound
382 && approx(posOnUnitElem, m_cellCenters(donorLevel, i), eps)) {
383 intervals[d] = i;
384 break;
385 }
386 }
387
388 if(intervals[d] == -1) {
389 stringstream errorMsg;
390 errorMsg << "Donor cell interval not found! (level = " << donorLevel << "; position = " << std::scientific
391 << posOnUnitElem << ")";
392 TERMM(1, errorMsg.str());
393 }
394 }
395
396 // Compute scalar donor cell index
397 const MInt donorIndex =
398 intervals[0] * noCells1D * noCells1D3 + intervals[1] * noCells1D3 + intervals[2] * (nDim == 3);
399
400 // Store donor cell information
401 donorIds[l] = donorIndex;
402 donorLevels[l] = donorLevel;
403
404 // Keep track of mapped volume, to identify non-mapped/missing cells
405 mappedVolume[donorLevel][donorIndex] = 1;
406
407 // Loop down to highest level and mark all descendent cells as mapped
408 for(MInt lvl = donorLevel + 1; lvl < maxDonorLevel; lvl++) {
409 const MInt noCellsRel1D = IPOW2(lvl - donorLevel);
410 const MInt noCellsRel1D3 = (nDim == 3) ? noCellsRel1D : 1;
411 const MInt sizeFactor = IPOW2(lvl - donorLevel);
412
413 const MInt noCells1D_ = IPOW2(lvl);
414 const MInt noCells1D3_ = (nDim == 3) ? noCells1D_ : 1;
415
416 // Loop over all child positions
417 for(MInt i = 0; i < noCellsRel1D; i++) {
418 for(MInt j = 0; j < noCellsRel1D; j++) {
419 for(MInt k = 0; k < noCellsRel1D3; k++) {
420 // Determine child 1D index on its level and mark as mapped
421 const MInt volumeIndex = (intervals[0] * sizeFactor + i) * noCells1D_ * noCells1D3_
422 + (intervals[1] * sizeFactor + j) * noCells1D3_
423 + (intervals[2] * sizeFactor + k) * (nDim == 3);
424 mappedVolume[lvl][volumeIndex] = 1;
425 }
426 }
427 }
428 }
429
430 // Loop up to relative level zero and mark coarser cells as mapped
431 for(MInt lvl = donorLevel - 1; lvl >= 0; lvl--) {
432 const MInt noCells1D_ = IPOW2(lvl);
433 const MInt noCells1D3_ = (nDim == 3) ? noCells1D_ : 1;
434 const MInt sizeFactor = IPOW2(donorLevel - lvl);
435
436 // Determine 1D index of parent on coarser level and mark as (partially) mapped
437 const MInt volumeIndex = std::floor(intervals[0] / sizeFactor) * noCells1D_ * noCells1D3_
438 + std::floor(intervals[1] / sizeFactor) * noCells1D3_
439 + std::floor(intervals[2] / sizeFactor) * (nDim == 3);
440 mappedVolume[lvl][volumeIndex] = 1;
441 }
442 }
443
444 // Append non-mapped cells/volumes to projection information
445 // Loop over all donor cell levels from coarse to fine
446 for(MInt lvl = 0; lvl < maxDonorLevel; lvl++) {
447 const MInt noCellsLvl1D = IPOW2(lvl);
448 const MInt noCellsLvl1D3 = (nDim == 3) ? noCellsLvl1D : 1;
449
450 // Loop over all cells on this level
451 for(MInt i = 0; i < noCellsLvl1D; i++) {
452 for(MInt j = 0; j < noCellsLvl1D; j++) {
453 for(MInt k = 0; k < noCellsLvl1D3; k++) {
454 // Index of current cell on this level
455 const MInt donorIndex = i * noCellsLvl1D * noCellsLvl1D3 + j * noCellsLvl1D3 + k * (nDim == 3);
456
457 // If the current cell on this level is not marked as mapped ...
458 if(!mappedVolume[lvl][donorIndex]) {
459 // ... append its position and level to the projection information
460 donorIds.push_back(donorIndex);
461 donorLevels.push_back(lvl);
462
463 std::cerr << globalDomainId() << " non-mapped " << targetCellCenter[0] << " " << lvl << " " << donorIndex
464 << std::endl;
465 TERMM(1, "TODO check if this is working correctly");
466
467 // Loop down to highest level and mark all children of this cell as mapped since the
468 // full volume of the coarse cell is already mapped
469 for(MInt childLvl = lvl + 1; childLvl < maxDonorLevel; childLvl++) {
470 const MInt noCellsRel1D = IPOW2(childLvl - lvl);
471 const MInt noCellsRel1D3 = (nDim == 3) ? noCellsRel1D : 1;
472
473 // Loop over all cells on this child level and mark as mapped
474 for(MInt i2 = 0; i2 < noCellsRel1D; i2++) {
475 for(MInt j2 = 0; j2 < noCellsRel1D; j2++) {
476 for(MInt k2 = 0; k2 < noCellsRel1D3; k2++) {
477 const MInt volumeIndex =
478 (i2 + i) * noCellsRel1D * noCellsRel1D3 + (j2 + j) * noCellsRel1D3 + (k2 + k) * (nDim == 3);
479 mappedVolume[childLvl][volumeIndex] = 1;
480 }
481 }
482 }
483 }
484 }
485 }
486 }
487 }
488 }
489
490 // Compute total volume of mapped cells and check (should be 1.0)
491 MFloat sumVolumes = 0.0;
492 for(MUint i = 0; i < donorLevels.size(); i++) {
493 const MFloat volume = 1.0 / (ipow(IPOW2(donorLevels[i]), nDim));
494 sumVolumes += volume;
495 }
496 if(!approx(sumVolumes, 1.0, eps)) {
497 stringstream errMsg;
498 errMsg << "calcProjectionInformation: mapped volume " << std::to_string(sumVolumes) << "(target cell coordinates:";
499 for(MInt d = 0; d < nDim; d++) {
500 errMsg << " " << targetCellCenter[d];
501 }
502 errMsg << ")";
503 TERMM(1, errMsg.str());
504 }
505}
506
507
508template class DgGalerkinProjection<2>;
509template class DgGalerkinProjection<3>;
Performs the Galerkin projection for a given polynomial degree.
void calcProjectionInformation(const MInt noDonorCells, const MInt *donorCellLvls, const MFloat *donorCellCoordinates, const MInt targetLvl, const MFloat *targetElementCenter, std::vector< MInt > &donorIds, std::vector< MInt > &donorLvls) const
Compute the projection information for one target cell.
~DgGalerkinProjection()
Destructor clears all member variables.
void init(MInt polyDeg, const MInt noLevels, const MFloat lengthLevel0, const MInt solverId)
Initialize the member variables and calculate the interpolation matrices.
DgGalerkinProjection()=default
void calcInterpolationMatrices()
Calculate and store the needed interpolation matrices.
Class stores precalculated values for interpolation & integration on the reference interval [-1,...
This class is a ScratchSpace.
Definition: scratch.h:758
MInt string2enum(MString theString)
This global function translates strings in their corresponding enum values (integer values)....
Definition: enums.cpp:20
DgPolynomialType
Definition: enums.h:310
DgIntegrationMethod
Definition: enums.h:313
MBool approx(const T &, const U &, const T)
Definition: functions.h:272
MInt ipow(MInt base, MInt exp)
Integer exponent function for non-negative exponents.
Definition: functions.h:317
MInt globalDomainId()
Return global domain id.
constexpr MLong IPOW2(MInt x)
constexpr MFloat FPOW2(MInt x)
constexpr MFloat FFPOW2(MInt x)
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
bool MBool
Definition: maiatypes.h:58
Holds helper functions for the interpolation.
void calcLinearInterpolationBase(const MFloat x, const MInt noNodes, const MFloat *nodes, MFloat *const polynomials)
Calculates linear interpolation base for point (1D).
void calcLagrangeInterpolatingPolynomials(const MFloat x, const MInt polyDeg, const MFloat *nodes, const MFloat *wBary, MFloat *polynomials)
Calculates the values of the Lagrangian polynomials l_j for a given point x in [-1,...