MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_GeneralGeometricPFactory_def.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// MueLu: A package for multigrid based preconditioning
4//
5// Copyright 2012 NTESS and the MueLu contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
10#ifndef MUELU_GENERALGEOMETRICPFACTORY_DEF_HPP
11#define MUELU_GENERALGEOMETRICPFACTORY_DEF_HPP
12
13#include <stdlib.h>
14#include <iomanip>
15
16// #include <Teuchos_LAPACK.hpp>
17#include <Teuchos_SerialDenseMatrix.hpp>
18#include <Teuchos_SerialDenseVector.hpp>
19#include <Teuchos_SerialDenseSolver.hpp>
20
21#include <Xpetra_CrsMatrixWrap.hpp>
22#include <Xpetra_ImportFactory.hpp>
23#include <Xpetra_Matrix.hpp>
24#include <Xpetra_MapFactory.hpp>
25#include <Xpetra_MultiVectorFactory.hpp>
26#include <Xpetra_VectorFactory.hpp>
27
28#include <Xpetra_IO.hpp>
29
31
32#include "MueLu_Monitor.hpp"
33
34namespace MueLu {
35
36template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
38 RCP<ParameterList> validParamList = rcp(new ParameterList());
39
40 // Coarsen can come in two forms, either a single char that will be interpreted as an integer
41 // which is used as the coarsening rate in every spatial dimentions, or it can be a longer
42 // string that will then be interpreted as an array of integers.
43 // By default coarsen is set as "{2}", hence a coarsening rate of 2 in every spatial dimension
44 // is the default setting!
45 validParamList->set<std::string>("Coarsen", "{2}",
46 "Coarsening rate in all spatial dimensions");
47 validParamList->set<int>("order", 1,
48 "Order of the interpolation scheme used");
49 validParamList->set<RCP<const FactoryBase> >("A", Teuchos::null,
50 "Generating factory of the matrix A");
51 validParamList->set<RCP<const FactoryBase> >("Nullspace", Teuchos::null,
52 "Generating factory of the nullspace");
53 validParamList->set<RCP<const FactoryBase> >("Coordinates", Teuchos::null,
54 "Generating factory for coorindates");
55 validParamList->set<RCP<const FactoryBase> >("gNodesPerDim", Teuchos::null,
56 "Number of nodes per spatial dimmension provided by CoordinatesTransferFactory.");
57 validParamList->set<RCP<const FactoryBase> >("lNodesPerDim", Teuchos::null,
58 "Number of nodes per spatial dimmension provided by CoordinatesTransferFactory.");
59 validParamList->set<std::string>("meshLayout", "Global Lexicographic",
60 "Type of mesh ordering");
61 validParamList->set<RCP<const FactoryBase> >("meshData", Teuchos::null,
62 "Mesh ordering associated data");
63
64 return validParamList;
65}
66
67template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
69 DeclareInput(Level& fineLevel, Level& /* coarseLevel */) const {
70 Input(fineLevel, "A");
71 Input(fineLevel, "Nullspace");
72 Input(fineLevel, "Coordinates");
73 // Request the global number of nodes per dimensions
74 if (fineLevel.GetLevelID() == 0) {
75 if (fineLevel.IsAvailable("gNodesPerDim", NoFactory::get())) {
76 fineLevel.DeclareInput("gNodesPerDim", NoFactory::get(), this);
77 } else {
78 TEUCHOS_TEST_FOR_EXCEPTION(fineLevel.IsAvailable("gNodesPerDim", NoFactory::get()),
80 "gNodesPerDim was not provided by the user on level0!");
81 }
82 } else {
83 Input(fineLevel, "gNodesPerDim");
84 }
85
86 // Request the local number of nodes per dimensions
87 if (fineLevel.GetLevelID() == 0) {
88 if (fineLevel.IsAvailable("lNodesPerDim", NoFactory::get())) {
89 fineLevel.DeclareInput("lNodesPerDim", NoFactory::get(), this);
90 } else {
91 TEUCHOS_TEST_FOR_EXCEPTION(fineLevel.IsAvailable("lNodesPerDim", NoFactory::get()),
93 "lNodesPerDim was not provided by the user on level0!");
94 }
95 } else {
96 Input(fineLevel, "lNodesPerDim");
97 }
98}
99
100template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
102 Level& coarseLevel) const {
103 return BuildP(fineLevel, coarseLevel);
104}
105
106template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
108 Level& coarseLevel) const {
109 FactoryMonitor m(*this, "Build", coarseLevel);
110
111 // Obtain general variables
112 using xdMV = Xpetra::MultiVector<typename Teuchos::ScalarTraits<Scalar>::coordinateType, LO, GO, NO>;
113 RCP<Matrix> A = Get<RCP<Matrix> >(fineLevel, "A");
114 RCP<MultiVector> fineNullspace = Get<RCP<MultiVector> >(fineLevel, "Nullspace");
115 RCP<xdMV> fineCoords = Get<RCP<xdMV> >(fineLevel, "Coordinates");
116 RCP<xdMV> coarseCoords;
117
118 // Get user-provided coarsening rate parameter (constant over all levels)
119 const ParameterList& pL = GetParameterList();
120
121 // collect general input data
122 const LO blkSize = A->GetFixedBlockSize();
123 RCP<const Map> rowMap = A->getRowMap();
124 RCP<GeometricData> myGeometry = rcp(new GeometricData{});
125
126 // Load the mesh layout type and the associated mesh data
127 myGeometry->meshLayout = pL.get<std::string>("meshLayout");
128 if (fineLevel.GetLevelID() == 0) {
129 if (myGeometry->meshLayout == "Local Lexicographic") {
130 Array<GO> meshData;
131 meshData = fineLevel.Get<Array<GO> >("meshData", NoFactory::get());
132 TEUCHOS_TEST_FOR_EXCEPTION(meshData.empty() == true, Exceptions::RuntimeError,
133 "The meshData array is empty, somehow the input for geometric"
134 " multigrid are not captured correctly.");
135 myGeometry->meshData.resize(rowMap->getComm()->getSize());
136 for (int i = 0; i < rowMap->getComm()->getSize(); ++i) {
137 myGeometry->meshData[i].resize(10);
138 for (int j = 0; j < 10; ++j) {
139 myGeometry->meshData[i][j] = meshData[10 * i + j];
140 }
141 }
142 }
143 }
144
145 TEUCHOS_TEST_FOR_EXCEPTION(fineCoords == Teuchos::null, Exceptions::RuntimeError,
146 "Coordinates cannot be accessed from fine level!");
147 myGeometry->numDimensions = fineCoords->getNumVectors();
148
149 // Get the number of points in each direction
150 if (fineLevel.GetLevelID() == 0) {
151 myGeometry->gFineNodesPerDir = fineLevel.Get<Array<GO> >("gNodesPerDim", NoFactory::get());
152 myGeometry->lFineNodesPerDir = fineLevel.Get<Array<LO> >("lNodesPerDim", NoFactory::get());
153 } else {
154 // Loading global number of nodes per diretions
155 myGeometry->gFineNodesPerDir = Get<Array<GO> >(fineLevel, "gNodesPerDim");
156
157 // Loading local number of nodes per diretions
158 myGeometry->lFineNodesPerDir = Get<Array<LO> >(fineLevel, "lNodesPerDim");
159 }
160 myGeometry->gNumFineNodes10 = myGeometry->gFineNodesPerDir[1] * myGeometry->gFineNodesPerDir[0];
161 myGeometry->gNumFineNodes = myGeometry->gFineNodesPerDir[2] * myGeometry->gNumFineNodes10;
162 myGeometry->lNumFineNodes10 = myGeometry->lFineNodesPerDir[1] * myGeometry->lFineNodesPerDir[0];
163 myGeometry->lNumFineNodes = myGeometry->lFineNodesPerDir[2] * myGeometry->lNumFineNodes10;
164
165 TEUCHOS_TEST_FOR_EXCEPTION(fineCoords->getLocalLength() != static_cast<size_t>(myGeometry->lNumFineNodes),
167 "The local number of elements in Coordinates is not equal to the"
168 " number of nodes given by: lNodesPerDim!");
169 TEUCHOS_TEST_FOR_EXCEPTION(fineCoords->getGlobalLength() != static_cast<size_t>(myGeometry->gNumFineNodes),
171 "The global number of elements in Coordinates is not equal to the"
172 " number of nodes given by: gNodesPerDim!");
173
174 // Get the coarsening rate
175 std::string coarsenRate = pL.get<std::string>("Coarsen");
176 Teuchos::Array<LO> crates;
177 try {
178 crates = Teuchos::fromStringToArray<LO>(coarsenRate);
179 } catch (const Teuchos::InvalidArrayStringRepresentation& e) {
180 GetOStream(Errors, -1) << " *** Coarsen must be a string convertible into an array! *** "
181 << std::endl;
182 throw e;
183 }
184 TEUCHOS_TEST_FOR_EXCEPTION((crates.size() > 1) && (crates.size() < myGeometry->numDimensions),
186 "Coarsen must have at least as many components as the number of"
187 " spatial dimensions in the problem.");
188
189 for (LO i = 0; i < 3; ++i) {
190 if (i < myGeometry->numDimensions) {
191 if (crates.size() == 1) {
192 myGeometry->coarseRate[i] = crates[0];
193 } else if (crates.size() == myGeometry->numDimensions) {
194 myGeometry->coarseRate[i] = crates[i];
195 }
196 } else {
197 myGeometry->coarseRate[i] = 1;
198 }
199 }
200
201 int interpolationOrder = pL.get<int>("order");
202 TEUCHOS_TEST_FOR_EXCEPTION((interpolationOrder < 0) || (interpolationOrder > 1),
204 "The interpolation order can only be set to 0 or 1.");
205
206 // Get the axis permutation from Global axis to Local axis
207 Array<LO> mapDirG2L(3), mapDirL2G(3);
208 for (LO i = 0; i < myGeometry->numDimensions; ++i) {
209 mapDirG2L[i] = i;
210 }
211 for (LO i = 0; i < myGeometry->numDimensions; ++i) {
212 TEUCHOS_TEST_FOR_EXCEPTION(mapDirG2L[i] > myGeometry->numDimensions,
214 "axis permutation values must all be less than"
215 " the number of spatial dimensions.");
216 mapDirL2G[mapDirG2L[i]] = i;
217 }
218 RCP<const Map> fineCoordsMap = fineCoords->getMap();
219
220 // This struct stores PIDs, LIDs and GIDs on the fine mesh and GIDs on the coarse mesh.
221 RCP<NodesIDs> ghostedCoarseNodes = rcp(new NodesIDs{});
222 Array<Array<GO> > lCoarseNodesGIDs(2);
223 if ((fineLevel.GetLevelID() == 0) && (myGeometry->meshLayout != "Global Lexicographic")) {
224 MeshLayoutInterface(interpolationOrder, blkSize, fineCoordsMap, myGeometry,
225 ghostedCoarseNodes, lCoarseNodesGIDs);
226 } else {
227 // This function expects perfect global lexicographic ordering of nodes and will not process
228 // data correctly otherwise. These restrictions allow for the simplest and most efficient
229 // processing of the levels (hopefully at least).
230 GetCoarsePoints(interpolationOrder, blkSize, fineCoordsMap, myGeometry, ghostedCoarseNodes,
231 lCoarseNodesGIDs);
232 }
233
234 // All that is left to do is loop over NCpts and:
235 // - extract coarse points coordiate for coarseCoords
236 // - get coordinates for current stencil computation
237 // - compute current stencil
238 // - compute row and column indices for stencil entries
239 RCP<const Map> stridedDomainMapP;
240 RCP<Matrix> P;
241 // Fancy formula for the number of non-zero terms
242 // All coarse points are injected, other points are using polynomial interpolation
243 // and have contribution from (interpolationOrder + 1)^numDimensions
244 // Noticebly this leads to 1 when the order is zero, hence fancy MatMatMatMult can be used.
245 GO lnnzP = Teuchos::as<LO>(std::pow(interpolationOrder + 1, myGeometry->numDimensions)) * (myGeometry->lNumFineNodes - myGeometry->lNumCoarseNodes) + myGeometry->lNumCoarseNodes;
246 lnnzP = lnnzP * blkSize;
247 GO gnnzP = Teuchos::as<LO>(std::pow(interpolationOrder + 1, myGeometry->numDimensions)) * (myGeometry->gNumFineNodes - myGeometry->gNumCoarseNodes) + myGeometry->gNumCoarseNodes;
248 gnnzP = gnnzP * blkSize;
249
250 GetOStream(Runtime1) << "P size = " << blkSize * myGeometry->gNumFineNodes
251 << " x " << blkSize * myGeometry->gNumCoarseNodes << std::endl;
252 GetOStream(Runtime1) << "P Fine grid : " << myGeometry->gFineNodesPerDir[0] << " -- "
253 << myGeometry->gFineNodesPerDir[1] << " -- "
254 << myGeometry->gFineNodesPerDir[2] << std::endl;
255 GetOStream(Runtime1) << "P Coarse grid : " << myGeometry->gCoarseNodesPerDir[0] << " -- "
256 << myGeometry->gCoarseNodesPerDir[1] << " -- "
257 << myGeometry->gCoarseNodesPerDir[2] << std::endl;
258 GetOStream(Runtime1) << "P nnz estimate: " << gnnzP << std::endl;
259
260 MakeGeneralGeometricP(myGeometry, fineCoords, lnnzP, blkSize, stridedDomainMapP,
261 A, P, coarseCoords, ghostedCoarseNodes, lCoarseNodesGIDs,
262 interpolationOrder);
263
264 // set StridingInformation of P
265 if (A->IsView("stridedMaps") == true) {
266 P->CreateView("stridedMaps", A->getRowMap("stridedMaps"), stridedDomainMapP);
267 } else {
268 P->CreateView("stridedMaps", P->getRangeMap(), stridedDomainMapP);
269 }
270
271 // store the transfer operator and node coordinates on coarse level
272 Set(coarseLevel, "P", P);
273 Set(coarseLevel, "coarseCoordinates", coarseCoords);
274 Set<Array<GO> >(coarseLevel, "gCoarseNodesPerDim", myGeometry->gCoarseNodesPerDir);
275 Set<Array<LO> >(coarseLevel, "lCoarseNodesPerDim", myGeometry->lCoarseNodesPerDir);
276
277 // rst: null space might get scaled here ... do we care. We could just inject at the cpoints,
278 // but I don't feel that this is needed.
279 RCP<MultiVector> coarseNullspace = MultiVectorFactory::Build(P->getDomainMap(),
280 fineNullspace->getNumVectors());
281 P->apply(*fineNullspace, *coarseNullspace, Teuchos::TRANS, Teuchos::ScalarTraits<SC>::one(),
282 Teuchos::ScalarTraits<SC>::zero());
283 Set(coarseLevel, "Nullspace", coarseNullspace);
284}
285
286template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
288 MeshLayoutInterface(const int /* interpolationOrder */, const LO /* blkSize */, RCP<const Map> fineCoordsMap,
289 RCP<GeometricData> myGeo, RCP<NodesIDs> ghostedCoarseNodes,
290 Array<Array<GO> >& lCoarseNodesGIDs) const {
291 // The goal here is to produce maps that globally labels the mesh lexicographically.
292 // These maps will replace the current maps of A, the coordinate vector and the nullspace.
293 // Ideally if the user provides the necessary maps then nothing needs to be done, otherwise
294 // it could be advantageous to allow the user to register a re-labeling function. Ultimately
295 // for common ordering schemes, some re-labeling can be directly implemented here.
296
297 int numRanks = fineCoordsMap->getComm()->getSize();
298 int myRank = fineCoordsMap->getComm()->getRank();
299
300 myGeo->myBlock = myGeo->meshData[myRank][2];
301 myGeo->startIndices[0] = myGeo->meshData[myRank][3];
302 myGeo->startIndices[1] = myGeo->meshData[myRank][5];
303 myGeo->startIndices[2] = myGeo->meshData[myRank][7];
304 myGeo->startIndices[3] = myGeo->meshData[myRank][4];
305 myGeo->startIndices[4] = myGeo->meshData[myRank][6];
306 myGeo->startIndices[5] = myGeo->meshData[myRank][8];
307 std::sort(myGeo->meshData.begin(), myGeo->meshData.end(),
308 [](const std::vector<GO>& a, const std::vector<GO>& b) -> bool {
309 // The below function sorts ranks by blockID, kmin, jmin and imin
310 if (a[2] < b[2]) {
311 return true;
312 } else if (a[2] == b[2]) {
313 if (a[7] < b[7]) {
314 return true;
315 } else if (a[7] == b[7]) {
316 if (a[5] < b[5]) {
317 return true;
318 } else if (a[5] == b[5]) {
319 if (a[3] < b[3]) {
320 return true;
321 }
322 }
323 }
324 }
325 return false;
326 });
327
328 myGeo->numBlocks = myGeo->meshData[numRanks - 1][2] + 1;
329 // Find the range of the current block
330 auto myBlockStart = std::lower_bound(myGeo->meshData.begin(), myGeo->meshData.end(),
331 myGeo->myBlock - 1,
332 [](const std::vector<GO>& vec, const GO val) -> bool {
333 return (vec[2] < val) ? true : false;
334 });
335 auto myBlockEnd = std::upper_bound(myGeo->meshData.begin(), myGeo->meshData.end(),
336 myGeo->myBlock,
337 [](const GO val, const std::vector<GO>& vec) -> bool {
338 return (val < vec[2]) ? true : false;
339 });
340 // Assuming that i,j,k and ranges are split in pi, pj and pk processors
341 // we search for these numbers as they will allow us to find quickly the PID of processors
342 // owning ghost nodes.
343 auto myKEnd = std::upper_bound(myBlockStart, myBlockEnd, (*myBlockStart)[3],
344 [](const GO val, const std::vector<GO>& vec) -> bool {
345 return (val < vec[7]) ? true : false;
346 });
347 auto myJEnd = std::upper_bound(myBlockStart, myKEnd, (*myBlockStart)[3],
348 [](const GO val, const std::vector<GO>& vec) -> bool {
349 return (val < vec[5]) ? true : false;
350 });
351 LO pi = std::distance(myBlockStart, myJEnd);
352 LO pj = std::distance(myBlockStart, myKEnd) / pi;
353 LO pk = std::distance(myBlockStart, myBlockEnd) / (pj * pi);
354
355 // We also look for the index of the local rank in the current block.
356 LO myRankIndex = std::distance(myGeo->meshData.begin(),
357 std::find_if(myBlockStart, myBlockEnd,
358 [myRank](const std::vector<GO>& vec) -> bool {
359 return (vec[0] == myRank) ? true : false;
360 }));
361
362 for (int dim = 0; dim < 3; ++dim) {
363 if (dim < myGeo->numDimensions) {
364 myGeo->offsets[dim] = Teuchos::as<LO>(myGeo->startIndices[dim]) % myGeo->coarseRate[dim];
365 myGeo->offsets[dim + 3] = Teuchos::as<LO>(myGeo->startIndices[dim]) % myGeo->coarseRate[dim];
366 }
367 }
368
369 // Check if the partition contains nodes on a boundary, if so that boundary (face, line or
370 // point) will not require ghost nodes.
371 for (int dim = 0; dim < 3; ++dim) {
372 if (dim < myGeo->numDimensions && (myGeo->startIndices[dim] % myGeo->coarseRate[dim] != 0 ||
373 myGeo->startIndices[dim] == myGeo->gFineNodesPerDir[dim] - 1)) {
374 myGeo->ghostInterface[2 * dim] = true;
375 }
376 if (dim < myGeo->numDimensions && myGeo->startIndices[dim + 3] != myGeo->gFineNodesPerDir[dim] - 1 && (myGeo->lFineNodesPerDir[dim] == 1 || myGeo->startIndices[dim + 3] % myGeo->coarseRate[dim] != 0)) {
377 myGeo->ghostInterface[2 * dim + 1] = true;
378 }
379 }
380
381 // Here one element can represent either the degenerate case of one node or the more general
382 // case of two nodes, i.e. x---x is a 1D element with two nodes and x is a 1D element with one
383 // node. This helps generating a 3D space from tensorial products...
384 // A good way to handle this would be to generalize the algorithm to take into account the
385 // discretization order used in each direction, at least in the FEM sense, since a 0 degree
386 // discretization will have a unique node per element. This way 1D discretization can be viewed
387 // as a 3D problem with one 0 degree element in the y direction and one 0 degre element in the z
388 // direction.
389 // !!! Operations below are aftecting both local and global values that have two different !!!
390 // orientations. Orientations can be interchanged using mapDirG2L and mapDirL2G. coarseRate,
391 // endRate and offsets are in the global basis, as well as all the variables starting with a g,
392 // !!! while the variables starting with an l are in the local basis. !!!
393 for (int i = 0; i < 3; ++i) {
394 if (i < myGeo->numDimensions) {
395 // This array is passed to the RAPFactory and eventually becomes gFineNodePerDir on the next
396 // level.
397 myGeo->gCoarseNodesPerDir[i] = (myGeo->gFineNodesPerDir[i] - 1) / myGeo->coarseRate[i];
398 myGeo->endRate[i] = Teuchos::as<LO>((myGeo->gFineNodesPerDir[i] - 1) % myGeo->coarseRate[i]);
399 if (myGeo->endRate[i] == 0) {
400 myGeo->endRate[i] = myGeo->coarseRate[i];
401 ++myGeo->gCoarseNodesPerDir[i];
402 } else {
403 myGeo->gCoarseNodesPerDir[i] += 2;
404 }
405 } else {
406 myGeo->endRate[i] = 1;
407 myGeo->gCoarseNodesPerDir[i] = 1;
408 }
409 }
410
411 myGeo->gNumCoarseNodes = myGeo->gCoarseNodesPerDir[0] * myGeo->gCoarseNodesPerDir[1] * myGeo->gCoarseNodesPerDir[2];
412
413 for (LO i = 0; i < 3; ++i) {
414 if (i < myGeo->numDimensions) {
415 // Check whether the partition includes the "end" of the mesh which means that endRate will
416 // apply. Also make sure that endRate is not 0 which means that the mesh does not require a
417 // particular treatment at the boundaries.
418 if ((myGeo->startIndices[i] + myGeo->lFineNodesPerDir[i]) == myGeo->gFineNodesPerDir[i]) {
419 myGeo->lCoarseNodesPerDir[i] = (myGeo->lFineNodesPerDir[i] - myGeo->endRate[i] + myGeo->offsets[i] - 1) / myGeo->coarseRate[i] + 1;
420 if (myGeo->offsets[i] == 0) {
421 ++myGeo->lCoarseNodesPerDir[i];
422 }
423 } else {
424 myGeo->lCoarseNodesPerDir[i] = (myGeo->lFineNodesPerDir[i] + myGeo->offsets[i] - 1) / myGeo->coarseRate[i];
425 if (myGeo->offsets[i] == 0) {
426 ++myGeo->lCoarseNodesPerDir[i];
427 }
428 }
429 } else {
430 myGeo->lCoarseNodesPerDir[i] = 1;
431 }
432 // This would happen if the rank does not own any nodes but in that case a subcommunicator
433 // should be used so this should really not be a concern.
434 if (myGeo->lFineNodesPerDir[i] < 1) {
435 myGeo->lCoarseNodesPerDir[i] = 0;
436 }
437 }
438
439 // Assuming linear interpolation, each fine point has contribution from 8 coarse points
440 // and each coarse point value gets injected.
441 // For systems of PDEs we assume that all dofs have the same P operator.
442 myGeo->lNumCoarseNodes = myGeo->lCoarseNodesPerDir[0] * myGeo->lCoarseNodesPerDir[1] * myGeo->lCoarseNodesPerDir[2];
443
444 // For each direction, determine how many points (including ghosts) are required.
445 for (int dim = 0; dim < 3; ++dim) {
446 // The first branch of this if-statement will be used if the rank contains only one layer
447 // of nodes in direction i, that layer must also coincide with the boundary of the mesh
448 // and coarseRate[i] == endRate[i]...
449 if (myGeo->startIndices[dim] == myGeo->gFineNodesPerDir[dim] - 1 &&
450 myGeo->startIndices[dim] % myGeo->coarseRate[dim] == 0) {
451 myGeo->startGhostedCoarseNode[dim] = myGeo->startIndices[dim] / myGeo->coarseRate[dim] - 1;
452 } else {
453 myGeo->startGhostedCoarseNode[dim] = myGeo->startIndices[dim] / myGeo->coarseRate[dim];
454 }
455 myGeo->ghostedCoarseNodesPerDir[dim] = myGeo->lCoarseNodesPerDir[dim];
456 // Check whether face *low needs ghost nodes
457 if (myGeo->ghostInterface[2 * dim]) {
458 myGeo->ghostedCoarseNodesPerDir[dim] += 1;
459 }
460 // Check whether face *hi needs ghost nodes
461 if (myGeo->ghostInterface[2 * dim + 1]) {
462 myGeo->ghostedCoarseNodesPerDir[dim] += 1;
463 }
464 }
465 myGeo->lNumGhostedNodes = myGeo->ghostedCoarseNodesPerDir[2] * myGeo->ghostedCoarseNodesPerDir[1] * myGeo->ghostedCoarseNodesPerDir[0];
466 myGeo->lNumGhostNodes = myGeo->lNumGhostedNodes - myGeo->lNumCoarseNodes;
467 ghostedCoarseNodes->PIDs.resize(myGeo->lNumGhostedNodes);
468 ghostedCoarseNodes->LIDs.resize(myGeo->lNumGhostedNodes);
469 ghostedCoarseNodes->GIDs.resize(myGeo->lNumGhostedNodes);
470 ghostedCoarseNodes->coarseGIDs.resize(myGeo->lNumGhostedNodes);
471 ghostedCoarseNodes->colInds.resize(myGeo->lNumGhostedNodes);
472 lCoarseNodesGIDs[0].resize(myGeo->lNumCoarseNodes);
473 lCoarseNodesGIDs[1].resize(myGeo->lNumCoarseNodes);
474
475 // Now the tricky part starts, the coarse nodes / ghosted coarse nodes need to be imported.
476 // This requires finding what their GID on the fine mesh is. They need to be ordered
477 // lexicographically to allow for fast sweeps through the mesh.
478
479 // We loop over all ghosted coarse nodes by increasing global lexicographic order
480 Array<LO> coarseNodeCoarseIndices(3), coarseNodeFineIndices(3);
481 LO currentIndex = -1, countCoarseNodes = 0;
482 for (int k = 0; k < myGeo->ghostedCoarseNodesPerDir[2]; ++k) {
483 for (int j = 0; j < myGeo->ghostedCoarseNodesPerDir[1]; ++j) {
484 for (int i = 0; i < myGeo->ghostedCoarseNodesPerDir[0]; ++i) {
485 currentIndex = k * myGeo->ghostedCoarseNodesPerDir[1] * myGeo->ghostedCoarseNodesPerDir[0] + j * myGeo->ghostedCoarseNodesPerDir[0] + i;
486 coarseNodeCoarseIndices[0] = myGeo->startGhostedCoarseNode[0] + i;
487 coarseNodeFineIndices[0] = coarseNodeCoarseIndices[0] * myGeo->coarseRate[0];
488 if (coarseNodeFineIndices[0] > myGeo->gFineNodesPerDir[0] - 1) {
489 coarseNodeFineIndices[0] = myGeo->gFineNodesPerDir[0] - 1;
490 }
491 coarseNodeCoarseIndices[1] = myGeo->startGhostedCoarseNode[1] + j;
492 coarseNodeFineIndices[1] = coarseNodeCoarseIndices[1] * myGeo->coarseRate[1];
493 if (coarseNodeFineIndices[1] > myGeo->gFineNodesPerDir[1] - 1) {
494 coarseNodeFineIndices[1] = myGeo->gFineNodesPerDir[1] - 1;
495 }
496 coarseNodeCoarseIndices[2] = myGeo->startGhostedCoarseNode[2] + k;
497 coarseNodeFineIndices[2] = coarseNodeCoarseIndices[2] * myGeo->coarseRate[2];
498 if (coarseNodeFineIndices[2] > myGeo->gFineNodesPerDir[2] - 1) {
499 coarseNodeFineIndices[2] = myGeo->gFineNodesPerDir[2] - 1;
500 }
501 GO myGID = -1, myCoarseGID = -1;
502 LO myLID = -1, myPID = -1;
503 GetGIDLocalLexicographic(i, j, k, coarseNodeFineIndices, myGeo, myRankIndex, pi, pj, pk,
504 myBlockStart, myBlockEnd, myGID, myPID, myLID);
505 myCoarseGID = coarseNodeCoarseIndices[0] + coarseNodeCoarseIndices[1] * myGeo->gCoarseNodesPerDir[0] + coarseNodeCoarseIndices[2] * myGeo->gCoarseNodesPerDir[1] * myGeo->gCoarseNodesPerDir[0];
506 ghostedCoarseNodes->PIDs[currentIndex] = myPID;
507 ghostedCoarseNodes->LIDs[currentIndex] = myLID;
508 ghostedCoarseNodes->GIDs[currentIndex] = myGID;
509 ghostedCoarseNodes->coarseGIDs[currentIndex] = myCoarseGID;
510 if (myPID == myRank) {
511 lCoarseNodesGIDs[0][countCoarseNodes] = myCoarseGID;
512 lCoarseNodesGIDs[1][countCoarseNodes] = myGID;
513 ++countCoarseNodes;
514 }
515 }
516 }
517 }
518
519} // End MeshLayoutInterface
520
521template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
523 GetCoarsePoints(const int /* interpolationOrder */, const LO /* blkSize */, RCP<const Map> fineCoordsMap,
524 RCP<GeometricData> myGeo, RCP<NodesIDs> ghostedCoarseNodes,
525 Array<Array<GO> >& lCoarseNodesGIDs) const {
526 // Assuming perfect global lexicographic ordering of the mesh, produce two arrays:
527 // 1) lGhostNodesIDs that stores PID, LID, GID and coarseGID associated with the coarse nodes
528 // need to compute the local part of the prolongator.
529 // 2) lCoarseNodesGIDs that stores the GIDs associated with the local nodes needed to create
530 // the map of the MultiVector of coarse node coordinates.
531
532 {
533 GO tmp = 0;
534 myGeo->startIndices[2] = fineCoordsMap->getMinGlobalIndex() / (myGeo->gFineNodesPerDir[1] * myGeo->gFineNodesPerDir[0]);
535 tmp = fineCoordsMap->getMinGlobalIndex() % (myGeo->gFineNodesPerDir[1] * myGeo->gFineNodesPerDir[0]);
536 myGeo->startIndices[1] = tmp / myGeo->gFineNodesPerDir[0];
537 myGeo->startIndices[0] = tmp % myGeo->gFineNodesPerDir[0];
538 } // End of scope for tmp
539 for (int dim = 0; dim < 3; ++dim) {
540 myGeo->startIndices[dim + 3] = myGeo->startIndices[dim] + myGeo->lFineNodesPerDir[dim] - 1;
541 }
542
543 for (int dim = 0; dim < 3; ++dim) {
544 if (dim < myGeo->numDimensions) {
545 myGeo->offsets[dim] = Teuchos::as<LO>(myGeo->startIndices[dim]) % myGeo->coarseRate[dim];
546 myGeo->offsets[dim + 3] = Teuchos::as<LO>(myGeo->startIndices[dim]) % myGeo->coarseRate[dim];
547 }
548 }
549
550 // Check if the partition contains nodes on a boundary, if so that boundary (face, line or
551 // point) will not require ghost nodes, unless there is only one node in that direction.
552 for (int dim = 0; dim < 3; ++dim) {
553 if (dim < myGeo->numDimensions && (myGeo->startIndices[dim] % myGeo->coarseRate[dim] != 0 ||
554 myGeo->startIndices[dim] == myGeo->gFineNodesPerDir[dim] - 1)) {
555 myGeo->ghostInterface[2 * dim] = true;
556 }
557 if (dim < myGeo->numDimensions && myGeo->startIndices[dim + 3] != myGeo->gFineNodesPerDir[dim] - 1 && (myGeo->lFineNodesPerDir[dim] == 1 || myGeo->startIndices[dim + 3] % myGeo->coarseRate[dim] != 0)) {
558 myGeo->ghostInterface[2 * dim + 1] = true;
559 }
560 }
561
562 // Here one element can represent either the degenerate case of one node or the more general
563 // case of two nodes, i.e. x---x is a 1D element with two nodes and x is a 1D element with one
564 // node. This helps generating a 3D space from tensorial products...
565 // A good way to handle this would be to generalize the algorithm to take into account the
566 // discretization order used in each direction, at least in the FEM sense, since a 0 degree
567 // discretization will have a unique node per element. This way 1D discretization can be viewed
568 // as a 3D problem with one 0 degree element in the y direction and one 0 degre element in the z
569 // direction.
570 // !!! Operations below are aftecting both local and global values that have two different !!!
571 // orientations. Orientations can be interchanged using mapDirG2L and mapDirL2G. coarseRate,
572 // endRate and offsets are in the global basis, as well as all the variables starting with a g,
573 // !!! while the variables starting with an l are in the local basis. !!!
574 for (int i = 0; i < 3; ++i) {
575 if (i < myGeo->numDimensions) {
576 // This array is passed to the RAPFactory and eventually becomes gFineNodePerDir on the next
577 // level.
578 myGeo->gCoarseNodesPerDir[i] = (myGeo->gFineNodesPerDir[i] - 1) / myGeo->coarseRate[i];
579 myGeo->endRate[i] = Teuchos::as<LO>((myGeo->gFineNodesPerDir[i] - 1) % myGeo->coarseRate[i]);
580 if (myGeo->endRate[i] == 0) {
581 myGeo->endRate[i] = myGeo->coarseRate[i];
582 ++myGeo->gCoarseNodesPerDir[i];
583 } else {
584 myGeo->gCoarseNodesPerDir[i] += 2;
585 }
586 } else {
587 myGeo->endRate[i] = 1;
588 myGeo->gCoarseNodesPerDir[i] = 1;
589 }
590 }
591
592 myGeo->gNumCoarseNodes = myGeo->gCoarseNodesPerDir[0] * myGeo->gCoarseNodesPerDir[1] * myGeo->gCoarseNodesPerDir[2];
593
594 for (LO i = 0; i < 3; ++i) {
595 if (i < myGeo->numDimensions) {
596 // Check whether the partition includes the "end" of the mesh which means that endRate will
597 // apply. Also make sure that endRate is not 0 which means that the mesh does not require a
598 // particular treatment at the boundaries.
599 if ((myGeo->startIndices[i] + myGeo->lFineNodesPerDir[i]) == myGeo->gFineNodesPerDir[i]) {
600 myGeo->lCoarseNodesPerDir[i] = (myGeo->lFineNodesPerDir[i] - myGeo->endRate[i] + myGeo->offsets[i] - 1) / myGeo->coarseRate[i] + 1;
601 if (myGeo->offsets[i] == 0) {
602 ++myGeo->lCoarseNodesPerDir[i];
603 }
604 } else {
605 myGeo->lCoarseNodesPerDir[i] = (myGeo->lFineNodesPerDir[i] + myGeo->offsets[i] - 1) / myGeo->coarseRate[i];
606 if (myGeo->offsets[i] == 0) {
607 ++myGeo->lCoarseNodesPerDir[i];
608 }
609 }
610 } else {
611 myGeo->lCoarseNodesPerDir[i] = 1;
612 }
613 // This would happen if the rank does not own any nodes but in that case a subcommunicator
614 // should be used so this should really not be a concern.
615 if (myGeo->lFineNodesPerDir[i] < 1) {
616 myGeo->lCoarseNodesPerDir[i] = 0;
617 }
618 }
619
620 // Assuming linear interpolation, each fine point has contribution from 8 coarse points
621 // and each coarse point value gets injected.
622 // For systems of PDEs we assume that all dofs have the same P operator.
623 myGeo->lNumCoarseNodes = myGeo->lCoarseNodesPerDir[0] * myGeo->lCoarseNodesPerDir[1] * myGeo->lCoarseNodesPerDir[2];
624
625 // For each direction, determine how many points (including ghosts) are required.
626 bool ghostedDir[6] = {false};
627 for (int dim = 0; dim < 3; ++dim) {
628 // The first branch of this if-statement will be used if the rank contains only one layer
629 // of nodes in direction i, that layer must also coincide with the boundary of the mesh
630 // and coarseRate[i] == endRate[i]...
631 if (myGeo->startIndices[dim] == myGeo->gFineNodesPerDir[dim] - 1 &&
632 myGeo->startIndices[dim] % myGeo->coarseRate[dim] == 0) {
633 myGeo->startGhostedCoarseNode[dim] = myGeo->startIndices[dim] / myGeo->coarseRate[dim] - 1;
634 } else {
635 myGeo->startGhostedCoarseNode[dim] = myGeo->startIndices[dim] / myGeo->coarseRate[dim];
636 }
637 myGeo->ghostedCoarseNodesPerDir[dim] = myGeo->lCoarseNodesPerDir[dim];
638 // Check whether face *low needs ghost nodes
639 if (myGeo->ghostInterface[2 * dim]) {
640 myGeo->ghostedCoarseNodesPerDir[dim] += 1;
641 ghostedDir[2 * dim] = true;
642 }
643 // Check whether face *hi needs ghost nodes
644 if (myGeo->ghostInterface[2 * dim + 1]) {
645 myGeo->ghostedCoarseNodesPerDir[dim] += 1;
646 ghostedDir[2 * dim + 1] = true;
647 }
648 }
649 myGeo->lNumGhostedNodes = myGeo->ghostedCoarseNodesPerDir[2] * myGeo->ghostedCoarseNodesPerDir[1] * myGeo->ghostedCoarseNodesPerDir[0];
650 myGeo->lNumGhostNodes = myGeo->lNumGhostedNodes - myGeo->lNumCoarseNodes;
651 ghostedCoarseNodes->PIDs.resize(myGeo->lNumGhostedNodes);
652 ghostedCoarseNodes->LIDs.resize(myGeo->lNumGhostedNodes);
653 ghostedCoarseNodes->GIDs.resize(myGeo->lNumGhostedNodes);
654 ghostedCoarseNodes->coarseGIDs.resize(myGeo->lNumGhostedNodes);
655 ghostedCoarseNodes->colInds.resize(myGeo->lNumGhostedNodes);
656 lCoarseNodesGIDs[0].resize(myGeo->lNumCoarseNodes);
657 lCoarseNodesGIDs[1].resize(myGeo->lNumCoarseNodes);
658
659 // Now the tricky part starts, the coarse nodes / ghosted coarse nodes need to be imported.
660 // This requires finding what their GID on the fine mesh is. They need to be ordered
661 // lexicographically to allow for fast sweeps through the mesh.
662
663 // We loop over all ghosted coarse nodes by increasing global lexicographic order
664 Array<LO> coarseNodeCoarseIndices(3), coarseNodeFineIndices(3), ijk(3);
665 LO currentIndex = -1, countCoarseNodes = 0;
666 for (ijk[2] = 0; ijk[2] < myGeo->ghostedCoarseNodesPerDir[2]; ++ijk[2]) {
667 for (ijk[1] = 0; ijk[1] < myGeo->ghostedCoarseNodesPerDir[1]; ++ijk[1]) {
668 for (ijk[0] = 0; ijk[0] < myGeo->ghostedCoarseNodesPerDir[0]; ++ijk[0]) {
669 currentIndex = ijk[2] * myGeo->ghostedCoarseNodesPerDir[1] * myGeo->ghostedCoarseNodesPerDir[0] + ijk[1] * myGeo->ghostedCoarseNodesPerDir[0] + ijk[0];
670 coarseNodeCoarseIndices[0] = myGeo->startGhostedCoarseNode[0] + ijk[0];
671 coarseNodeFineIndices[0] = coarseNodeCoarseIndices[0] * myGeo->coarseRate[0];
672 if (coarseNodeFineIndices[0] > myGeo->gFineNodesPerDir[0] - 1) {
673 coarseNodeFineIndices[0] = myGeo->gFineNodesPerDir[0] - 1;
674 }
675 coarseNodeCoarseIndices[1] = myGeo->startGhostedCoarseNode[1] + ijk[1];
676 coarseNodeFineIndices[1] = coarseNodeCoarseIndices[1] * myGeo->coarseRate[1];
677 if (coarseNodeFineIndices[1] > myGeo->gFineNodesPerDir[1] - 1) {
678 coarseNodeFineIndices[1] = myGeo->gFineNodesPerDir[1] - 1;
679 }
680 coarseNodeCoarseIndices[2] = myGeo->startGhostedCoarseNode[2] + ijk[2];
681 coarseNodeFineIndices[2] = coarseNodeCoarseIndices[2] * myGeo->coarseRate[2];
682 if (coarseNodeFineIndices[2] > myGeo->gFineNodesPerDir[2] - 1) {
683 coarseNodeFineIndices[2] = myGeo->gFineNodesPerDir[2] - 1;
684 }
685 GO myGID = 0, myCoarseGID = -1;
686 GO factor[3] = {};
687 factor[2] = myGeo->gNumFineNodes10;
688 factor[1] = myGeo->gFineNodesPerDir[0];
689 factor[0] = 1;
690 for (int dim = 0; dim < 3; ++dim) {
691 if (dim < myGeo->numDimensions) {
692 if (myGeo->startIndices[dim] - myGeo->offsets[dim] + ijk[dim] * myGeo->coarseRate[dim] < myGeo->gFineNodesPerDir[dim] - 1) {
693 myGID += (myGeo->startIndices[dim] - myGeo->offsets[dim] + ijk[dim] * myGeo->coarseRate[dim]) * factor[dim];
694 } else {
695 myGID += (myGeo->startIndices[dim] - myGeo->offsets[dim] + (ijk[dim] - 1) * myGeo->coarseRate[dim] + myGeo->endRate[dim]) * factor[dim];
696 }
697 }
698 }
699 myCoarseGID = coarseNodeCoarseIndices[0] + coarseNodeCoarseIndices[1] * myGeo->gCoarseNodesPerDir[0] + coarseNodeCoarseIndices[2] * myGeo->gCoarseNodesPerDir[1] * myGeo->gCoarseNodesPerDir[0];
700 ghostedCoarseNodes->GIDs[currentIndex] = myGID;
701 ghostedCoarseNodes->coarseGIDs[currentIndex] = myCoarseGID;
702 if ((!ghostedDir[0] || ijk[0] != 0) && (!ghostedDir[2] || ijk[1] != 0) && (!ghostedDir[4] || ijk[2] != 0) && (!ghostedDir[1] || ijk[0] != myGeo->ghostedCoarseNodesPerDir[0] - 1) && (!ghostedDir[3] || ijk[1] != myGeo->ghostedCoarseNodesPerDir[1] - 1) && (!ghostedDir[5] || ijk[2] != myGeo->ghostedCoarseNodesPerDir[2] - 1)) {
703 lCoarseNodesGIDs[0][countCoarseNodes] = myCoarseGID;
704 lCoarseNodesGIDs[1][countCoarseNodes] = myGID;
705 ++countCoarseNodes;
706 }
707 }
708 }
709 }
710 Array<int> ghostsPIDs(myGeo->lNumGhostedNodes);
711 Array<LO> ghostsLIDs(myGeo->lNumGhostedNodes);
712 fineCoordsMap->getRemoteIndexList(ghostedCoarseNodes->GIDs(),
713 ghostedCoarseNodes->PIDs(),
714 ghostedCoarseNodes->LIDs());
715} // End GetCoarsePoint
716
717template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
719 MakeGeneralGeometricP(RCP<GeometricData> myGeo,
720 const RCP<Xpetra::MultiVector<typename Teuchos::ScalarTraits<Scalar>::coordinateType, LO, GO, Node> >& fineCoords,
721 const LO nnzP, const LO dofsPerNode,
722 RCP<const Map>& stridedDomainMapP, RCP<Matrix>& Amat, RCP<Matrix>& P,
723 RCP<Xpetra::MultiVector<typename Teuchos::ScalarTraits<Scalar>::coordinateType, LO, GO, Node> >& coarseCoords,
724 RCP<NodesIDs> ghostedCoarseNodes, Array<Array<GO> > coarseNodesGIDs,
725 int interpolationOrder) const {
726 /* On termination, return the number of local prolongator columns owned by
727 * this processor.
728 *
729 * Input
730 * =====
731 * nNodes Number of fine level Blk Rows owned by this processor
732 * coarseRate Rate of coarsening in each spatial direction.
733 * endRate Rate of coarsening in each spatial direction for the last
734 * nodes in the mesh where an adaptive coarsening rate is
735 * required.
736 * nTerms Number of nonzero entries in the prolongation matrix.
737 * dofsPerNode Number of degrees-of-freedom per mesh node.
738 *
739 * Output
740 * =====
741 * So far nothing...
742 */
743
744 using xdMV = Xpetra::MultiVector<typename Teuchos::ScalarTraits<Scalar>::coordinateType, LO, GO, NO>;
745 Xpetra::global_size_t OTI = Teuchos::OrdinalTraits<Xpetra::global_size_t>::invalid();
746
747 LO myRank = Amat->getRowMap()->getComm()->getRank();
748 GO numGloCols = dofsPerNode * myGeo->gNumCoarseNodes;
749
750 // Build maps necessary to create and fill complete the prolongator
751 // note: rowMapP == rangeMapP and colMapP != domainMapP
752 RCP<const Map> rowMapP = Amat->getDomainMap();
753
754 RCP<const Map> domainMapP;
755
756 RCP<const Map> colMapP;
757
758 // At this point we need to create the column map which is a delicate operation.
759 // The entries in that map need to be ordered as follows:
760 // 1) first owned entries ordered by LID
761 // 2) second order the remaining entries by PID
762 // 3) entries with the same remote PID are ordered by GID.
763 // One piece of good news: myGeo->lNumCoarseNodes is the number of ownedNodes and
764 // myGeo->lNumGhostNodes is the number of remote nodes. The sorting can be limited to remote
765 // nodes as the owned ones are alreaded ordered by LID!
766
767 {
768 std::vector<NodeID> colMapOrdering(myGeo->lNumGhostedNodes);
769 for (LO ind = 0; ind < myGeo->lNumGhostedNodes; ++ind) {
770 colMapOrdering[ind].GID = ghostedCoarseNodes->GIDs[ind];
771 if (ghostedCoarseNodes->PIDs[ind] == myRank) {
772 colMapOrdering[ind].PID = -1;
773 } else {
774 colMapOrdering[ind].PID = ghostedCoarseNodes->PIDs[ind];
775 }
776 colMapOrdering[ind].LID = ghostedCoarseNodes->LIDs[ind];
777 colMapOrdering[ind].lexiInd = ind;
778 }
779 std::sort(colMapOrdering.begin(), colMapOrdering.end(),
780 [](NodeID a, NodeID b) -> bool {
781 return ((a.PID < b.PID) || ((a.PID == b.PID) && (a.GID < b.GID)));
782 });
783
784 Array<GO> colGIDs(dofsPerNode * myGeo->lNumGhostedNodes);
785 for (LO ind = 0; ind < myGeo->lNumGhostedNodes; ++ind) {
786 // Save the permutation calculated to go from Lexicographic indexing to column map indexing
787 ghostedCoarseNodes->colInds[colMapOrdering[ind].lexiInd] = ind;
788 for (LO dof = 0; dof < dofsPerNode; ++dof) {
789 colGIDs[dofsPerNode * ind + dof] = dofsPerNode * colMapOrdering[ind].GID + dof;
790 }
791 }
792 domainMapP = Xpetra::MapFactory<LO, GO, NO>::Build(rowMapP->lib(),
793 numGloCols,
794 colGIDs.view(0, dofsPerNode *
795 myGeo->lNumCoarseNodes),
796 rowMapP->getIndexBase(),
797 rowMapP->getComm());
798 colMapP = Xpetra::MapFactory<LO, GO, NO>::Build(rowMapP->lib(),
799 OTI,
800 colGIDs.view(0, colGIDs.size()),
801 rowMapP->getIndexBase(),
802 rowMapP->getComm());
803 } // End of scope for colMapOrdering and colGIDs
804
805 std::vector<size_t> strideInfo(1);
806 strideInfo[0] = dofsPerNode;
807 stridedDomainMapP = Xpetra::StridedMapFactory<LO, GO, NO>::Build(domainMapP, strideInfo);
808
809 // Build the map for the coarse level coordinates, create the associated MultiVector and fill it
810 // with an import from the fine coordinates MultiVector. As data is local this should not create
811 // communications during the importer creation.
812 RCP<const Map> coarseCoordsMap = MapFactory::Build(fineCoords->getMap()->lib(),
813 myGeo->gNumCoarseNodes,
814 coarseNodesGIDs[0](),
815 fineCoords->getMap()->getIndexBase(),
816 fineCoords->getMap()->getComm());
817 RCP<const Map> coarseCoordsFineMap = MapFactory::Build(fineCoords->getMap()->lib(),
818 myGeo->gNumCoarseNodes,
819 coarseNodesGIDs[1](),
820 fineCoords->getMap()->getIndexBase(),
821 fineCoords->getMap()->getComm());
822
823 RCP<const Import> coarseImporter = ImportFactory::Build(fineCoords->getMap(),
824 coarseCoordsFineMap);
825 coarseCoords = Xpetra::MultiVectorFactory<typename Teuchos::ScalarTraits<Scalar>::coordinateType, LO, GO, NO>::Build(coarseCoordsFineMap,
826 myGeo->numDimensions);
827 coarseCoords->doImport(*fineCoords, *coarseImporter, Xpetra::INSERT);
828 coarseCoords->replaceMap(coarseCoordsMap);
829
830 // Do the actual import using the fineCoords->getMap()
831 RCP<const Map> ghostMap = Xpetra::MapFactory<LO, GO, NO>::Build(fineCoords->getMap()->lib(),
832 OTI,
833 ghostedCoarseNodes->GIDs(),
834 fineCoords->getMap()->getIndexBase(),
835 fineCoords->getMap()->getComm());
836 RCP<const Import> ghostImporter = ImportFactory::Build(fineCoords->getMap(), ghostMap);
837 RCP<xdMV> ghostCoords = Xpetra::MultiVectorFactory<typename Teuchos::ScalarTraits<Scalar>::coordinateType, LO, GO, NO>::Build(ghostMap,
838 myGeo->numDimensions);
839 ghostCoords->doImport(*fineCoords, *ghostImporter, Xpetra::INSERT);
840
841 P = rcp(new CrsMatrixWrap(rowMapP, colMapP, 0));
842 RCP<CrsMatrix> PCrs = toCrsMatrix(P);
843
844 ArrayRCP<size_t> iaP;
845 ArrayRCP<LO> jaP;
846 ArrayRCP<SC> valP;
847
848 PCrs->allocateAllValues(nnzP, iaP, jaP, valP);
849
850 ArrayView<size_t> ia = iaP();
851 ArrayView<LO> ja = jaP();
852 ArrayView<SC> val = valP();
853 ia[0] = 0;
854
855 Array<ArrayRCP<typename Teuchos::ScalarTraits<Scalar>::coordinateType> > ghostedCoords(3);
856 {
857 ArrayRCP<typename Teuchos::ScalarTraits<Scalar>::coordinateType> tmp(ghostCoords->getLocalLength(), 0.0);
858 for (int dim = 0; dim < 3; ++dim) {
859 if (dim < myGeo->numDimensions) {
860 ghostedCoords[dim] = ghostCoords->getDataNonConst(dim);
861 } else {
862 ghostedCoords[dim] = tmp;
863 }
864 }
865 }
866
867 // Declaration and assignment of fineCoords which holds the coordinates of the fine nodes in 3D.
868 // To do so we pull the nD coordinates from fineCoords and pad the rest with zero vectors...
869 RCP<Xpetra::Vector<typename Teuchos::ScalarTraits<Scalar>::coordinateType, LO, GO, NO> > zeros = Xpetra::VectorFactory<typename Teuchos::ScalarTraits<Scalar>::coordinateType, LO, GO, NO>::Build(fineCoords->getMap(), true);
870 ArrayRCP<ArrayRCP<typename Teuchos::ScalarTraits<Scalar>::coordinateType> > lFineCoords(3);
871 for (int dim = 0; dim < 3; ++dim) {
872 if (dim < myGeo->numDimensions) {
873 lFineCoords[dim] = fineCoords->getDataNonConst(dim);
874 } else {
875 lFineCoords[dim] = zeros->getDataNonConst(0);
876 }
877 }
878
879 GO tStencil = 0;
880 for (int currentIndex = 0; currentIndex < myGeo->lNumFineNodes; ++currentIndex) {
881 Array<GO> ghostedIndices(3), firstInterpolationIndices(3);
882 Array<LO> interpolationPIDs(8), interpolationLIDs(8), interpolationGIDs(8);
883 Array<Array<typename Teuchos::ScalarTraits<Scalar>::coordinateType> > interpolationCoords(9);
884 interpolationCoords[0].resize(3);
885 GO firstInterpolationNodeIndex;
886 int nStencil = 0;
887 for (int dim = 0; dim < 3; ++dim) {
888 interpolationCoords[0][dim] = lFineCoords[dim][currentIndex];
889 }
890
891 // Compute the ghosted (i,j,k) of the current node, that assumes (I,J,K)=(0,0,0) to be
892 // associated with the first node in ghostCoords
893 { // Scope for tmp
894 ghostedIndices[2] = currentIndex / (myGeo->lFineNodesPerDir[1] * myGeo->lFineNodesPerDir[0]);
895 LO tmp = currentIndex % (myGeo->lFineNodesPerDir[1] * myGeo->lFineNodesPerDir[0]);
896 ghostedIndices[1] = tmp / myGeo->lFineNodesPerDir[0];
897 ghostedIndices[0] = tmp % myGeo->lFineNodesPerDir[0];
898 for (int dim = 0; dim < 3; ++dim) {
899 ghostedIndices[dim] += myGeo->offsets[dim];
900 }
901 // A special case appears when the mesh is really coarse: it is possible for a rank to
902 // have a single coarse node in a given direction. If this happens on the highest i, j or k
903 // we need to "grab" a coarse node with a lower i, j, or k which leads us to add to the
904 // value of ghostedIndices
905 }
906 // No we find the indices of the coarse nodes used for interpolation simply by integer
907 // division.
908 for (int dim = 0; dim < 3; ++dim) {
909 firstInterpolationIndices[dim] = ghostedIndices[dim] / myGeo->coarseRate[dim];
910 // If you are on the edge of the local domain go back one coarse node, unless there is only
911 // one node on the local domain...
912 if (firstInterpolationIndices[dim] == myGeo->ghostedCoarseNodesPerDir[dim] - 1 && myGeo->ghostedCoarseNodesPerDir[dim] > 1) {
913 firstInterpolationIndices[dim] -= 1;
914 }
915 }
916 firstInterpolationNodeIndex =
917 firstInterpolationIndices[2] * myGeo->ghostedCoarseNodesPerDir[1] * myGeo->ghostedCoarseNodesPerDir[0] + firstInterpolationIndices[1] * myGeo->ghostedCoarseNodesPerDir[0] + firstInterpolationIndices[0];
918
919 // We extract the coordinates and PIDs associated with each coarse node used during
920 // inteprolation in order to fill the prolongator correctly
921 {
922 LO ind = -1;
923 for (int k = 0; k < 2; ++k) {
924 for (int j = 0; j < 2; ++j) {
925 for (int i = 0; i < 2; ++i) {
926 ind = k * 4 + j * 2 + i;
927 interpolationLIDs[ind] = firstInterpolationNodeIndex + k * myGeo->ghostedCoarseNodesPerDir[1] * myGeo->ghostedCoarseNodesPerDir[0] + j * myGeo->ghostedCoarseNodesPerDir[0] + i;
928 if (ghostedCoarseNodes->PIDs[interpolationLIDs[ind]] == rowMapP->getComm()->getRank()) {
929 interpolationPIDs[ind] = -1;
930 } else {
931 interpolationPIDs[ind] = ghostedCoarseNodes->PIDs[interpolationLIDs[ind]];
932 }
933 interpolationGIDs[ind] = ghostedCoarseNodes->coarseGIDs[interpolationLIDs[ind]];
934
935 interpolationCoords[ind + 1].resize(3);
936 for (int dim = 0; dim < 3; ++dim) {
937 interpolationCoords[ind + 1][dim] = ghostedCoords[dim][interpolationLIDs[ind]];
938 }
939 }
940 }
941 }
942 } // End of ind scope
943
944 // Compute the actual geometric interpolation stencil
945 // LO stencilLength = static_cast<LO>(std::pow(interpolationOrder + 1, 3));
946 std::vector<double> stencil(8);
947 Array<GO> firstCoarseNodeFineIndices(3);
948 int rate[3] = {};
949 for (int dim = 0; dim < 3; ++dim) {
950 firstCoarseNodeFineIndices[dim] = firstInterpolationIndices[dim] * myGeo->coarseRate[dim];
951 if ((myGeo->startIndices[dim + 3] == myGeo->gFineNodesPerDir[dim] - 1) && (ghostedIndices[dim] >=
952 (myGeo->ghostedCoarseNodesPerDir[dim] - 2) * myGeo->coarseRate[dim])) {
953 rate[dim] = myGeo->endRate[dim];
954 } else {
955 rate[dim] = myGeo->coarseRate[dim];
956 }
957 }
958 Array<GO> trueGhostedIndices(3);
959 // This handles the case of a rank having a single node that also happens to be the last node
960 // in any direction. It might be more efficient to re-write the algo so that this is
961 // incorporated in the definition of ghostedIndices directly...
962 for (int dim = 0; dim < 3; ++dim) {
963 if (myGeo->startIndices[dim] == myGeo->gFineNodesPerDir[dim] - 1) {
964 trueGhostedIndices[dim] = ghostedIndices[dim] + rate[dim];
965 } else {
966 trueGhostedIndices[dim] = ghostedIndices[dim];
967 }
968 }
969 ComputeStencil(myGeo->numDimensions, trueGhostedIndices, firstCoarseNodeFineIndices, rate,
970 interpolationCoords, interpolationOrder, stencil);
971
972 // Finally check whether the fine node is on a coarse: node, edge or face
973 // and select accordingly the non-zero values from the stencil and the
974 // corresponding column indices
975 Array<LO> nzIndStencil(8), permutation(8);
976 for (LO k = 0; k < 8; ++k) {
977 permutation[k] = k;
978 }
979 if (interpolationOrder == 0) {
980 nStencil = 1;
981 for (LO k = 0; k < 8; ++k) {
982 nzIndStencil[k] = static_cast<LO>(stencil[0]);
983 }
984 stencil[0] = 0.0;
985 stencil[nzIndStencil[0]] = 1.0;
986 } else if (interpolationOrder == 1) {
987 Array<GO> currentNodeGlobalFineIndices(3);
988 for (int dim = 0; dim < 3; ++dim) {
989 currentNodeGlobalFineIndices[dim] = ghostedIndices[dim] - myGeo->offsets[dim] + myGeo->startIndices[dim];
990 }
991 if (((ghostedIndices[0] % myGeo->coarseRate[0] == 0) || currentNodeGlobalFineIndices[0] == myGeo->gFineNodesPerDir[0] - 1) && ((ghostedIndices[1] % myGeo->coarseRate[1] == 0) || currentNodeGlobalFineIndices[1] == myGeo->gFineNodesPerDir[1] - 1) && ((ghostedIndices[2] % myGeo->coarseRate[2] == 0) || currentNodeGlobalFineIndices[2] == myGeo->gFineNodesPerDir[2] - 1)) {
992 if ((currentNodeGlobalFineIndices[0] == myGeo->gFineNodesPerDir[0] - 1) ||
993 (ghostedIndices[0] / myGeo->coarseRate[0] == myGeo->ghostedCoarseNodesPerDir[0] - 1)) {
994 nzIndStencil[0] += 1;
995 }
996 if (((currentNodeGlobalFineIndices[1] == myGeo->gFineNodesPerDir[1] - 1) ||
997 (ghostedIndices[1] / myGeo->coarseRate[1] == myGeo->ghostedCoarseNodesPerDir[1] - 1)) &&
998 (myGeo->numDimensions > 1)) {
999 nzIndStencil[0] += 2;
1000 }
1001 if (((currentNodeGlobalFineIndices[2] == myGeo->gFineNodesPerDir[2] - 1) ||
1002 (ghostedIndices[2] / myGeo->coarseRate[2] == myGeo->ghostedCoarseNodesPerDir[2] - 1)) &&
1003 myGeo->numDimensions > 2) {
1004 nzIndStencil[0] += 4;
1005 }
1006 nStencil = 1;
1007 for (LO k = 0; k < 8; ++k) {
1008 nzIndStencil[k] = nzIndStencil[0];
1009 }
1010 } else {
1011 nStencil = 8;
1012 for (LO k = 0; k < 8; ++k) {
1013 nzIndStencil[k] = k;
1014 }
1015 }
1016 }
1017
1018 // Here the values are filled in the Crs matrix arrays
1019 // This is basically the only place these variables are modified
1020 // Hopefully this makes handling system of PDEs easy!
1021
1022 // Loop on dofsPerNode and process each row for the current Node
1023
1024 // Sort nodes by PIDs using stable sort to keep sublist ordered by LIDs and GIDs
1025 sh_sort2(interpolationPIDs.begin(), interpolationPIDs.end(),
1026 permutation.begin(), permutation.end());
1027
1028 GO colInd;
1029 for (LO j = 0; j < dofsPerNode; ++j) {
1030 ia[currentIndex * dofsPerNode + j + 1] = ia[currentIndex * dofsPerNode + j] + nStencil;
1031 for (LO k = 0; k < nStencil; ++k) {
1032 colInd = ghostedCoarseNodes->colInds[interpolationLIDs[nzIndStencil[permutation[k]]]];
1033 ja[ia[currentIndex * dofsPerNode + j] + k] = colInd * dofsPerNode + j;
1034 val[ia[currentIndex * dofsPerNode + j] + k] = stencil[nzIndStencil[permutation[k]]];
1035 }
1036 // Add the stencil for each degree of freedom.
1037 tStencil += nStencil;
1038 }
1039 } // End loop over fine nodes
1040
1041 if (rowMapP->lib() == Xpetra::UseTpetra) {
1042 // - Cannot resize for Epetra, as it checks for same pointers
1043 // - Need to resize for Tpetra, as it check ().size() == ia[numRows]
1044 // NOTE: these invalidate ja and val views
1045 jaP.resize(tStencil);
1046 valP.resize(tStencil);
1047 }
1048
1049 // Set the values of the prolongation operators into the CrsMatrix P and call FillComplete
1050 PCrs->setAllValues(iaP, jaP, valP);
1051 PCrs->expertStaticFillComplete(domainMapP, rowMapP);
1052} // End MakeGeneralGeometricP
1053
1054// template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1055// void BlackBoxPFactory<Scalar, LocalOrdinal, GlobalOrdinal, Node>::GetGeometricData(
1056// RCP<Xpetra::MultiVector<typename Teuchos::ScalarTraits<Scalar>::coordinateType,LO,GO,NO> >& coordinates, const Array<LO> coarseRate,
1057// const Array<GO> gFineNodesPerDir, const Array<LO> lFineNodesPerDir, const LO BlkSize,
1058// Array<GO>& gIndices, Array<LO>& myOffset, Array<bool>& ghostInterface, Array<LO>& endRate,
1059// Array<GO>& gCoarseNodesPerDir, Array<LO>& lCoarseNodesPerDir, Array<GO>& ghostGIDs,
1060// Array<GO>& coarseNodesGIDs, Array<GO>& colGIDs, GO& gNumCoarseNodes, LO& lNumCoarseNodes,
1061// ArrayRCP<Array<double> > coarseNodes) const {
1062
1063// RCP<const Map> coordinatesMap = coordinates->getMap();
1064// LO numDimensions = coordinates->getNumVectors();
1065
1066// // Using the coarsening rate and the fine level data,
1067// // compute coarse level data
1068
1069// // Phase 1 //
1070// // ------------------------------------------------------------------ //
1071// // We first start by finding small informations on the mesh such as //
1072// // the number of coarse nodes (local and global) and the number of //
1073// // ghost nodes / the end rate of coarsening. //
1074// // ------------------------------------------------------------------ //
1075// GO minGlobalIndex = coordinatesMap->getMinGlobalIndex();
1076// {
1077// GO tmp;
1078// gIndices[2] = minGlobalIndex / (gFineNodesPerDir[1]*gFineNodesPerDir[0]);
1079// tmp = minGlobalIndex % (gFineNodesPerDir[1]*gFineNodesPerDir[0]);
1080// gIndices[1] = tmp / gFineNodesPerDir[0];
1081// gIndices[0] = tmp % gFineNodesPerDir[0];
1082
1083// myOffset[2] = gIndices[2] % coarseRate[2];
1084// myOffset[1] = gIndices[1] % coarseRate[1];
1085// myOffset[0] = gIndices[0] % coarseRate[0];
1086// }
1087
1088// // Check whether ghost nodes are needed in each direction
1089// for(LO i=0; i < numDimensions; ++i) {
1090// if((gIndices[i] != 0) && (gIndices[i] % coarseRate[i] > 0)) {
1091// ghostInterface[2*i] = true;
1092// }
1093// if(((gIndices[i] + lFineNodesPerDir[i]) != gFineNodesPerDir[i]) && ((gIndices[i] + lFineNodesPerDir[i] - 1) % coarseRate[i] > 0)) {
1094// ghostInterface[2*i + 1] = true;
1095// }
1096// }
1097
1098// for(LO i = 0; i < 3; ++i) {
1099// if(i < numDimensions) {
1100// lCoarseNodesPerDir[i] = (lFineNodesPerDir[i] + myOffset[i] - 1) / coarseRate[i];
1101// if(myOffset[i] == 0) { ++lCoarseNodesPerDir[i]; }
1102// gCoarseNodesPerDir[i] = (gFineNodesPerDir[i] - 1) / coarseRate[i];
1103// endRate[i] = (gFineNodesPerDir[i] - 1) % coarseRate[i];
1104// if(endRate[i] == 0) {
1105// ++gCoarseNodesPerDir[i];
1106// endRate[i] = coarseRate[i];
1107// }
1108// } else {
1109// // Most quantities need to be set to 1 for extra dimensions
1110// // this is rather logical, an x-y plane is like a single layer
1111// // of nodes in the z direction...
1112// gCoarseNodesPerDir[i] = 1;
1113// lCoarseNodesPerDir[i] = 1;
1114// endRate[i] = 1;
1115// }
1116// }
1117
1118// gNumCoarseNodes = gCoarseNodesPerDir[0]*gCoarseNodesPerDir[1]*gCoarseNodesPerDir[2];
1119// lNumCoarseNodes = lCoarseNodesPerDir[0]*lCoarseNodesPerDir[1]*lCoarseNodesPerDir[2];
1120
1121// // For each direction, determine how many ghost points are required.
1122// LO lNumGhostNodes = 0;
1123// {
1124// const int complementaryIndices[3][2] = {{1,2}, {0,2}, {0,1}};
1125// LO tmp = 0;
1126// for(LO i = 0; i < 3; ++i) {
1127// // Check whether a face in direction i needs ghost nodes
1128// if(ghostInterface[2*i] || ghostInterface[2*i+1]) {
1129// if(i == 0) {tmp = lCoarseNodesPerDir[1]*lCoarseNodesPerDir[2];}
1130// if(i == 1) {tmp = lCoarseNodesPerDir[0]*lCoarseNodesPerDir[2];}
1131// if(i == 2) {tmp = lCoarseNodesPerDir[0]*lCoarseNodesPerDir[1];}
1132// }
1133// // If both faces in direction i need nodes, double the number of ghost nodes
1134// if(ghostInterface[2*i] && ghostInterface[2*i+1]) {tmp = 2*tmp;}
1135// lNumGhostNodes += tmp;
1136
1137// // The corners and edges need to be checked in 2D / 3D to add more ghosts nodes
1138// for(LO j = 0; j < 2; ++j) {
1139// for(LO k = 0; k < 2; ++k) {
1140// // Check if two adjoining faces need ghost nodes and then add their common edge
1141// if(ghostInterface[2*complementaryIndices[i][0]+j] && ghostInterface[2*complementaryIndices[i][1]+k]) {
1142// lNumGhostNodes += lCoarseNodesPerDir[i];
1143// // Add corners if three adjoining faces need ghost nodes,
1144// // but add them only once! Hence when i == 0.
1145// if(ghostInterface[2*i] && (i == 0)) { lNumGhostNodes += 1; }
1146// if(ghostInterface[2*i+1] && (i == 0)) { lNumGhostNodes += 1; }
1147// }
1148// }
1149// }
1150// tmp = 0;
1151// }
1152// } // end of scope for tmp and complementaryIndices
1153
1154// // Phase 2 //
1155// // ------------------------------------------------------------------ //
1156// // Build a list of GIDs to import the required ghost nodes. //
1157// // The ordering of the ghosts nodes will be as natural as possible, //
1158// // i.e. it should follow the GID ordering of the mesh. //
1159// // ------------------------------------------------------------------ //
1160
1161// // Saddly we have to more or less redo what was just done to figure out the value of lNumGhostNodes,
1162// // there might be some optimization possibility here...
1163// ghostGIDs.resize(lNumGhostNodes);
1164// LO countGhosts = 0;
1165// // Get the GID of the first point on the current partition.
1166// GO startingGID = minGlobalIndex;
1167// Array<GO> startingIndices(3);
1168// // We still want ghost nodes even if have with a 0 offset,
1169// // except when we are on a boundary
1170// if(ghostInterface[4] && (myOffset[2] == 0)) {
1171// if(gIndices[2] + coarseRate[2] > gFineNodesPerDir[2]) {
1172// startingGID -= endRate[2]*gFineNodesPerDir[1]*gFineNodesPerDir[0];
1173// } else {
1174// startingGID -= coarseRate[2]*gFineNodesPerDir[1]*gFineNodesPerDir[0];
1175// }
1176// } else {
1177// startingGID -= myOffset[2]*gFineNodesPerDir[1]*gFineNodesPerDir[0];
1178// }
1179// if(ghostInterface[2] && (myOffset[1] == 0)) {
1180// if(gIndices[1] + coarseRate[1] > gFineNodesPerDir[1]) {
1181// startingGID -= endRate[1]*gFineNodesPerDir[0];
1182// } else {
1183// startingGID -= coarseRate[1]*gFineNodesPerDir[0];
1184// }
1185// } else {
1186// startingGID -= myOffset[1]*gFineNodesPerDir[0];
1187// }
1188// if(ghostInterface[0] && (myOffset[0] == 0)) {
1189// if(gIndices[0] + coarseRate[0] > gFineNodesPerDir[0]) {
1190// startingGID -= endRate[0];
1191// } else {
1192// startingGID -= coarseRate[0];
1193// }
1194// } else {
1195// startingGID -= myOffset[0];
1196// }
1197
1198// { // scope for tmp
1199// GO tmp;
1200// startingIndices[2] = startingGID / (gFineNodesPerDir[1]*gFineNodesPerDir[0]);
1201// tmp = startingGID % (gFineNodesPerDir[1]*gFineNodesPerDir[0]);
1202// startingIndices[1] = tmp / gFineNodesPerDir[0];
1203// startingIndices[0] = tmp % gFineNodesPerDir[0];
1204// }
1205
1206// GO ghostOffset[3] = {0, 0, 0};
1207// LO lengthZero = lCoarseNodesPerDir[0];
1208// LO lengthOne = lCoarseNodesPerDir[1];
1209// LO lengthTwo = lCoarseNodesPerDir[2];
1210// if(ghostInterface[0]) {++lengthZero;}
1211// if(ghostInterface[1]) {++lengthZero;}
1212// if(ghostInterface[2]) {++lengthOne;}
1213// if(ghostInterface[3]) {++lengthOne;}
1214// if(ghostInterface[4]) {++lengthTwo;}
1215// if(ghostInterface[5]) {++lengthTwo;}
1216
1217// // First check the bottom face as it will have the lowest GIDs
1218// if(ghostInterface[4]) {
1219// ghostOffset[2] = startingGID;
1220// for(LO j = 0; j < lengthOne; ++j) {
1221// if( (j == lengthOne-1) && (startingIndices[1] + j*coarseRate[1] + 1 > gFineNodesPerDir[1]) ) {
1222// ghostOffset[1] = ((j-1)*coarseRate[1] + endRate[1])*gFineNodesPerDir[0];
1223// } else {
1224// ghostOffset[1] = j*coarseRate[1]*gFineNodesPerDir[0];
1225// }
1226// for(LO k = 0; k < lengthZero; ++k) {
1227// if( (k == lengthZero-1) && (startingIndices[0] + k*coarseRate[0] + 1 > gFineNodesPerDir[0]) ) {
1228// ghostOffset[0] = (k-1)*coarseRate[0] + endRate[0];
1229// } else {
1230// ghostOffset[0] = k*coarseRate[0];
1231// }
1232// // If the partition includes a changed rate at the edge, ghost nodes need to be picked carefully.
1233// // This if statement is repeated each time a ghost node is selected.
1234// ghostGIDs[countGhosts] = ghostOffset[2] + ghostOffset[1] + ghostOffset[0];
1235// ++countGhosts;
1236// }
1237// }
1238// }
1239
1240// // Sweep over the lCoarseNodesPerDir[2] coarse layers in direction 2 and gather necessary ghost nodes
1241// // located on these layers.
1242// for(LO i = 0; i < lengthTwo; ++i) {
1243// // Exclude the cases where ghost nodes exists on the faces in directions 2, these faces are swept
1244// // seperatly for efficiency.
1245// if( !((i == lengthTwo-1) && ghostInterface[5]) && !((i == 0) && ghostInterface[4]) ) {
1246// // Set the ghostOffset in direction 2 taking into account a possible endRate different
1247// // from the regular coarseRate.
1248// if( (i == lengthTwo-1) && !ghostInterface[5] ) {
1249// ghostOffset[2] = startingGID + ((i-1)*coarseRate[2] + endRate[2])*gFineNodesPerDir[1]*gFineNodesPerDir[0];
1250// } else {
1251// ghostOffset[2] = startingGID + i*coarseRate[2]*gFineNodesPerDir[1]*gFineNodesPerDir[0];
1252// }
1253// for(LO j = 0; j < lengthOne; ++j) {
1254// if( (j == 0) && ghostInterface[2] ) {
1255// for(LO k = 0; k < lengthZero; ++k) {
1256// if( (k == lengthZero-1) && (startingIndices[0] + k*coarseRate[0] + 1 > gFineNodesPerDir[0]) ) {
1257// if(k == 0) {
1258// ghostOffset[0] = endRate[0];
1259// } else {
1260// ghostOffset[0] = (k-1)*coarseRate[0] + endRate[0];
1261// }
1262// } else {
1263// ghostOffset[0] = k*coarseRate[0];
1264// }
1265// // In this case j == 0 so ghostOffset[1] is zero and can be ignored
1266// ghostGIDs[countGhosts] = ghostOffset[2] + ghostOffset[0];
1267// ++countGhosts;
1268// }
1269// } else if( (j == lengthOne-1) && ghostInterface[3] ) {
1270// // Set the ghostOffset in direction 1 taking into account a possible endRate different
1271// // from the regular coarseRate.
1272// if( (j == lengthOne-1) && (startingIndices[1] + j*coarseRate[1] + 1 > gFineNodesPerDir[1]) ) {
1273// ghostOffset[1] = ((j-1)*coarseRate[1] + endRate[1])*gFineNodesPerDir[0];
1274// } else {
1275// ghostOffset[1] = j*coarseRate[1]*gFineNodesPerDir[0];
1276// }
1277// for(LO k = 0; k < lengthZero; ++k) {
1278// if( (k == lengthZero-1) && (startingIndices[0] + k*coarseRate[0] + 1 > gFineNodesPerDir[0]) ) {
1279// ghostOffset[0] = (k-1)*coarseRate[0] + endRate[0];
1280// } else {
1281// ghostOffset[0] = k*coarseRate[0];
1282// }
1283// ghostGIDs[countGhosts] = ghostOffset[2] + ghostOffset[1] + ghostOffset[0];
1284// ++countGhosts;
1285// }
1286// } else {
1287// // Set ghostOffset[1] for side faces sweep
1288// if( (j == lengthOne-1) && (startingIndices[1] + j*coarseRate[1] + 1 > gFineNodesPerDir[1]) ) {
1289// ghostOffset[1] = ( (j-1)*coarseRate[1] + endRate[1] )*gFineNodesPerDir[0];
1290// } else {
1291// ghostOffset[1] = j*coarseRate[1]*gFineNodesPerDir[0];
1292// }
1293
1294// // Set ghostOffset[0], ghostGIDs and countGhosts
1295// if(ghostInterface[0]) { // In that case ghostOffset[0]==0, so we can ignore it
1296// ghostGIDs[countGhosts] = ghostOffset[2] + ghostOffset[1];
1297// ++countGhosts;
1298// }
1299// if(ghostInterface[1]) { // Grab ghost point at the end of direction 0.
1300// if( (startingIndices[0] + (lengthZero-1)*coarseRate[0]) > gFineNodesPerDir[0] - 1 ) {
1301// if(lengthZero > 2) {
1302// ghostOffset[0] = (lengthZero-2)*coarseRate[0] + endRate[0];
1303// } else {
1304// ghostOffset[0] = endRate[0];
1305// }
1306// } else {
1307// ghostOffset[0] = (lengthZero-1)*coarseRate[0];
1308// }
1309// ghostGIDs[countGhosts] = ghostOffset[2] + ghostOffset[1] + ghostOffset[0];
1310// ++countGhosts;
1311// }
1312// }
1313// }
1314// }
1315// }
1316
1317// // Finally check the top face
1318// if(ghostInterface[5]) {
1319// if( startingIndices[2] + (lengthTwo-1)*coarseRate[2] + 1 > gFineNodesPerDir[2] ) {
1320// ghostOffset[2] = startingGID + ((lengthTwo-2)*coarseRate[2] + endRate[2])*gFineNodesPerDir[1]*gFineNodesPerDir[0];
1321// } else {
1322// ghostOffset[2] = startingGID + (lengthTwo-1)*coarseRate[2]*gFineNodesPerDir[1]*gFineNodesPerDir[0];
1323// }
1324// for(LO j = 0; j < lengthOne; ++j) {
1325// if( (j == lengthOne-1) && (startingIndices[1] + j*coarseRate[1] + 1 > gFineNodesPerDir[1]) ) { // && !ghostInterface[3] ) {
1326// ghostOffset[1] = ( (j-1)*coarseRate[1] + endRate[1] )*gFineNodesPerDir[0];
1327// } else {
1328// ghostOffset[1] = j*coarseRate[1]*gFineNodesPerDir[0];
1329// }
1330// for(LO k = 0; k < lengthZero; ++k) {
1331// if( (k == lengthZero-1) && (startingIndices[0] + k*coarseRate[0] + 1 > gFineNodesPerDir[0]) ) {
1332// ghostOffset[0] = (k-1)*coarseRate[0] + endRate[0];
1333// } else {
1334// ghostOffset[0] = k*coarseRate[0];
1335// }
1336// ghostGIDs[countGhosts] = ghostOffset[2] + ghostOffset[1] + ghostOffset[0];
1337// ++countGhosts;
1338// }
1339// }
1340// }
1341
1342// // Phase 3 //
1343// // ------------------------------------------------------------------ //
1344// // Final phase of this function, lists are being built to create the //
1345// // column and domain maps of the projection as well as the map and //
1346// // arrays for the coarse coordinates multivector. //
1347// // ------------------------------------------------------------------ //
1348
1349// Array<GO> gCoarseNodesGIDs(lNumCoarseNodes);
1350// LO currentNode, offset2, offset1, offset0;
1351// // Find the GIDs of the coarse nodes on the partition.
1352// for(LO ind2 = 0; ind2 < lCoarseNodesPerDir[2]; ++ind2) {
1353// if(myOffset[2] == 0) {
1354// offset2 = startingIndices[2] + myOffset[2];
1355// } else {
1356// if(startingIndices[2] + endRate[2] == gFineNodesPerDir[2] - 1) {
1357// offset2 = startingIndices[2] + endRate[2];
1358// } else {
1359// offset2 = startingIndices[2] + coarseRate[2];
1360// }
1361// }
1362// if(offset2 + ind2*coarseRate[2] > gFineNodesPerDir[2] - 1) {
1363// offset2 += (ind2 - 1)*coarseRate[2] + endRate[2];
1364// } else {
1365// offset2 += ind2*coarseRate[2];
1366// }
1367// offset2 = offset2*gFineNodesPerDir[1]*gFineNodesPerDir[0];
1368
1369// for(LO ind1 = 0; ind1 < lCoarseNodesPerDir[1]; ++ind1) {
1370// if(myOffset[1] == 0) {
1371// offset1 = startingIndices[1] + myOffset[1];
1372// } else {
1373// if(startingIndices[1] + endRate[1] == gFineNodesPerDir[1] - 1) {
1374// offset1 = startingIndices[1] + endRate[1];
1375// } else {
1376// offset1 = startingIndices[1] + coarseRate[1];
1377// }
1378// }
1379// if(offset1 + ind1*coarseRate[1] > gFineNodesPerDir[1] - 1) {
1380// offset1 += (ind1 - 1)*coarseRate[1] + endRate[1];
1381// } else {
1382// offset1 += ind1*coarseRate[1];
1383// }
1384// offset1 = offset1*gFineNodesPerDir[0];
1385// for(LO ind0 = 0; ind0 < lCoarseNodesPerDir[0]; ++ind0) {
1386// offset0 = startingIndices[0];
1387// if(myOffset[0] == 0) {
1388// offset0 += ind0*coarseRate[0];
1389// } else {
1390// offset0 += (ind0 + 1)*coarseRate[0];
1391// }
1392// if(offset0 > gFineNodesPerDir[0] - 1) {offset0 += endRate[0] - coarseRate[0];}
1393
1394// currentNode = ind2*lCoarseNodesPerDir[1]*lCoarseNodesPerDir[0]
1395// + ind1*lCoarseNodesPerDir[0]
1396// + ind0;
1397// gCoarseNodesGIDs[currentNode] = offset2 + offset1 + offset0;
1398// }
1399// }
1400// }
1401
1402// // Actual loop over all the coarse/ghost nodes to find their index on the coarse mesh
1403// // and the corresponding dofs that will need to be added to colMapP.
1404// colGIDs.resize(BlkSize*(lNumCoarseNodes+lNumGhostNodes));
1405// coarseNodesGIDs.resize(lNumCoarseNodes);
1406// for(LO i = 0; i < numDimensions; ++i) {coarseNodes[i].resize(lNumCoarseNodes);}
1407// GO fineNodesPerCoarseSlab = coarseRate[2]*gFineNodesPerDir[1]*gFineNodesPerDir[0];
1408// GO fineNodesEndCoarseSlab = endRate[2]*gFineNodesPerDir[1]*gFineNodesPerDir[0];
1409// GO fineNodesPerCoarsePlane = coarseRate[1]*gFineNodesPerDir[0];
1410// GO fineNodesEndCoarsePlane = endRate[1]*gFineNodesPerDir[0];
1411// GO coarseNodesPerCoarseLayer = gCoarseNodesPerDir[1]*gCoarseNodesPerDir[0];
1412// GO gCoarseNodeOnCoarseGridGID;
1413// LO gInd[3], lCol;
1414// Array<int> ghostPIDs (lNumGhostNodes);
1415// Array<LO> ghostLIDs (lNumGhostNodes);
1416// Array<LO> ghostPermut(lNumGhostNodes);
1417// for(LO k = 0; k < lNumGhostNodes; ++k) {ghostPermut[k] = k;}
1418// coordinatesMap->getRemoteIndexList(ghostGIDs, ghostPIDs, ghostLIDs);
1419// sh_sort_permute(ghostPIDs.begin(),ghostPIDs.end(), ghostPermut.begin(),ghostPermut.end());
1420
1421// { // scope for tmpInds, tmpVars and tmp.
1422// GO tmpInds[3], tmpVars[2];
1423// LO tmp;
1424// // Loop over the coarse nodes of the partition and add them to colGIDs
1425// // that will be used to construct the column and domain maps of P as well
1426// // as to construct the coarse coordinates map.
1427// // for(LO col = 0; col < lNumCoarseNodes; ++col) { // This should most likely be replaced by loops of lCoarseNodesPerDir[] to simplify arithmetics
1428// LO col = 0;
1429// LO firstCoarseNodeInds[3], currentCoarseNode;
1430// for(LO dim = 0; dim < 3; ++dim) {
1431// if(myOffset[dim] == 0) {
1432// firstCoarseNodeInds[dim] = 0;
1433// } else {
1434// firstCoarseNodeInds[dim] = coarseRate[dim] - myOffset[dim];
1435// }
1436// }
1437// Array<ArrayRCP<const typename Teuchos::ScalarTraits<Scalar>::coordinateType> > fineNodes(numDimensions);
1438// for(LO dim = 0; dim < numDimensions; ++dim) {fineNodes[dim] = coordinates->getData(dim);}
1439// for(LO k = 0; k < lCoarseNodesPerDir[2]; ++k) {
1440// for(LO j = 0; j < lCoarseNodesPerDir[1]; ++j) {
1441// for(LO i = 0; i < lCoarseNodesPerDir[0]; ++i) {
1442// col = k*lCoarseNodesPerDir[1]*lCoarseNodesPerDir[0] + j*lCoarseNodesPerDir[0] + i;
1443
1444// // Check for endRate
1445// currentCoarseNode = 0;
1446// if(firstCoarseNodeInds[0] + i*coarseRate[0] > lFineNodesPerDir[0] - 1) {
1447// currentCoarseNode += firstCoarseNodeInds[0] + (i-1)*coarseRate[0] + endRate[0];
1448// } else {
1449// currentCoarseNode += firstCoarseNodeInds[0] + i*coarseRate[0];
1450// }
1451// if(firstCoarseNodeInds[1] + j*coarseRate[1] > lFineNodesPerDir[1] - 1) {
1452// currentCoarseNode += (firstCoarseNodeInds[1] + (j-1)*coarseRate[1] + endRate[1])*lFineNodesPerDir[0];
1453// } else {
1454// currentCoarseNode += (firstCoarseNodeInds[1] + j*coarseRate[1])*lFineNodesPerDir[0];
1455// }
1456// if(firstCoarseNodeInds[2] + k*coarseRate[2] > lFineNodesPerDir[2] - 1) {
1457// currentCoarseNode += (firstCoarseNodeInds[2] + (k-1)*coarseRate[2] + endRate[2])*lFineNodesPerDir[1]*lFineNodesPerDir[0];
1458// } else {
1459// currentCoarseNode += (firstCoarseNodeInds[2] + k*coarseRate[2])*lFineNodesPerDir[1]*lFineNodesPerDir[0];
1460// }
1461// // Load coordinates
1462// for(LO dim = 0; dim < numDimensions; ++dim) {
1463// coarseNodes[dim][col] = fineNodes[dim][currentCoarseNode];
1464// }
1465
1466// if((endRate[2] != coarseRate[2]) && (gCoarseNodesGIDs[col] > (gCoarseNodesPerDir[2] - 2)*fineNodesPerCoarseSlab + fineNodesEndCoarseSlab - 1)) {
1467// tmpInds[2] = gCoarseNodesGIDs[col] / fineNodesPerCoarseSlab + 1;
1468// tmpVars[0] = gCoarseNodesGIDs[col] - (tmpInds[2] - 1)*fineNodesPerCoarseSlab - fineNodesEndCoarseSlab;
1469// } else {
1470// tmpInds[2] = gCoarseNodesGIDs[col] / fineNodesPerCoarseSlab;
1471// tmpVars[0] = gCoarseNodesGIDs[col] % fineNodesPerCoarseSlab;
1472// }
1473// if((endRate[1] != coarseRate[1]) && (tmpVars[0] > (gCoarseNodesPerDir[1] - 2)*fineNodesPerCoarsePlane + fineNodesEndCoarsePlane - 1)) {
1474// tmpInds[1] = tmpVars[0] / fineNodesPerCoarsePlane + 1;
1475// tmpVars[1] = tmpVars[0] - (tmpInds[1] - 1)*fineNodesPerCoarsePlane - fineNodesEndCoarsePlane;
1476// } else {
1477// tmpInds[1] = tmpVars[0] / fineNodesPerCoarsePlane;
1478// tmpVars[1] = tmpVars[0] % fineNodesPerCoarsePlane;
1479// }
1480// if(tmpVars[1] == gFineNodesPerDir[0] - 1) {
1481// tmpInds[0] = gCoarseNodesPerDir[0] - 1;
1482// } else {
1483// tmpInds[0] = tmpVars[1] / coarseRate[0];
1484// }
1485// gInd[2] = col / (lCoarseNodesPerDir[1]*lCoarseNodesPerDir[0]);
1486// tmp = col % (lCoarseNodesPerDir[1]*lCoarseNodesPerDir[0]);
1487// gInd[1] = tmp / lCoarseNodesPerDir[0];
1488// gInd[0] = tmp % lCoarseNodesPerDir[0];
1489// lCol = gInd[2]*(lCoarseNodesPerDir[1]*lCoarseNodesPerDir[0]) + gInd[1]*lCoarseNodesPerDir[0] + gInd[0];
1490// gCoarseNodeOnCoarseGridGID = tmpInds[2]*coarseNodesPerCoarseLayer + tmpInds[1]*gCoarseNodesPerDir[0] + tmpInds[0];
1491// coarseNodesGIDs[lCol] = gCoarseNodeOnCoarseGridGID;
1492// for(LO dof = 0; dof < BlkSize; ++dof) {
1493// colGIDs[BlkSize*lCol + dof] = BlkSize*gCoarseNodeOnCoarseGridGID + dof;
1494// }
1495// }
1496// }
1497// }
1498// // Now loop over the ghost nodes of the partition to add them to colGIDs
1499// // since they will need to be included in the column map of P
1500// for(col = lNumCoarseNodes; col < lNumCoarseNodes + lNumGhostNodes; ++col) {
1501// if((endRate[2] != coarseRate[2]) && (ghostGIDs[ghostPermut[col - lNumCoarseNodes]] > (gCoarseNodesPerDir[2] - 2)*fineNodesPerCoarseSlab + fineNodesEndCoarseSlab - 1)) {
1502// tmpInds[2] = ghostGIDs[ghostPermut[col - lNumCoarseNodes]] / fineNodesPerCoarseSlab + 1;
1503// tmpVars[0] = ghostGIDs[ghostPermut[col - lNumCoarseNodes]] - (tmpInds[2] - 1)*fineNodesPerCoarseSlab - fineNodesEndCoarseSlab;
1504// } else {
1505// tmpInds[2] = ghostGIDs[ghostPermut[col - lNumCoarseNodes]] / fineNodesPerCoarseSlab;
1506// tmpVars[0] = ghostGIDs[ghostPermut[col - lNumCoarseNodes]] % fineNodesPerCoarseSlab;
1507// }
1508// if((endRate[1] != coarseRate[1]) && (tmpVars[0] > (gCoarseNodesPerDir[1] - 2)*fineNodesPerCoarsePlane + fineNodesEndCoarsePlane - 1)) {
1509// tmpInds[1] = tmpVars[0] / fineNodesPerCoarsePlane + 1;
1510// tmpVars[1] = tmpVars[0] - (tmpInds[1] - 1)*fineNodesPerCoarsePlane - fineNodesEndCoarsePlane;
1511// } else {
1512// tmpInds[1] = tmpVars[0] / fineNodesPerCoarsePlane;
1513// tmpVars[1] = tmpVars[0] % fineNodesPerCoarsePlane;
1514// }
1515// if(tmpVars[1] == gFineNodesPerDir[0] - 1) {
1516// tmpInds[0] = gCoarseNodesPerDir[0] - 1;
1517// } else {
1518// tmpInds[0] = tmpVars[1] / coarseRate[0];
1519// }
1520// gCoarseNodeOnCoarseGridGID = tmpInds[2]*coarseNodesPerCoarseLayer + tmpInds[1]*gCoarseNodesPerDir[0] + tmpInds[0];
1521// for(LO dof = 0; dof < BlkSize; ++dof) {
1522// colGIDs[BlkSize*col + dof] = BlkSize*gCoarseNodeOnCoarseGridGID + dof;
1523// }
1524// }
1525// } // End of scope for tmpInds, tmpVars and tmp
1526
1527// } // GetGeometricData()
1528
1529template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1531 const LO numDimensions, const Array<GO> currentNodeIndices,
1532 const Array<GO> coarseNodeIndices, const LO rate[3],
1533 const Array<Array<typename Teuchos::ScalarTraits<Scalar>::coordinateType> > coord, const int interpolationOrder,
1534 std::vector<double>& stencil) const {
1535 TEUCHOS_TEST_FOR_EXCEPTION((interpolationOrder > 1) || (interpolationOrder < 0),
1537 "The interpolation order can be set to 0 or 1 only.");
1538
1539 if (interpolationOrder == 0) {
1540 ComputeConstantInterpolationStencil(numDimensions, currentNodeIndices, coarseNodeIndices,
1541 rate, stencil);
1542 } else if (interpolationOrder == 1) {
1543 ComputeLinearInterpolationStencil(numDimensions, coord, stencil);
1544 }
1545
1546} // End ComputeStencil
1547
1548template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1550 ComputeConstantInterpolationStencil(const LO numDimensions, const Array<GO> currentNodeIndices,
1551 const Array<GO> coarseNodeIndices, const LO rate[3],
1552 std::vector<double>& stencil) const {
1553 LO coarseNode = 0;
1554 if (numDimensions > 2) {
1555 if ((currentNodeIndices[2] - coarseNodeIndices[2]) > (rate[2] / 2)) {
1556 coarseNode += 4;
1557 }
1558 }
1559 if (numDimensions > 1) {
1560 if ((currentNodeIndices[1] - coarseNodeIndices[1]) > (rate[1] / 2)) {
1561 coarseNode += 2;
1562 }
1563 }
1564 if ((currentNodeIndices[0] - coarseNodeIndices[0]) > (rate[0] / 2)) {
1565 coarseNode += 1;
1566 }
1567 stencil[0] = coarseNode;
1568
1569} // ComputeConstantInterpolationStencil
1570
1571template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1573 ComputeLinearInterpolationStencil(const LO numDimensions, const Array<Array<typename Teuchos::ScalarTraits<Scalar>::coordinateType> > coord,
1574 std::vector<double>& stencil)
1575 const {
1576 // 7 8 Find xi, eta and zeta such that
1577 // x---------x
1578 // /| /| Rx = x_p - sum N_i(xi,eta,zeta)x_i = 0
1579 // 5/ | 6/ | Ry = y_p - sum N_i(xi,eta,zeta)y_i = 0
1580 // x---------x | Rz = z_p - sum N_i(xi,eta,zeta)z_i = 0
1581 // | | *P | |
1582 // | x------|--x We can do this with a Newton solver:
1583 // | /3 | /4 We will start with initial guess (xi,eta,zeta) = (0,0,0)
1584 // |/ |/ We compute the Jacobian and iterate until convergence...
1585 // z y x---------x
1586 // | / 1 2 Once we have (xi,eta,zeta), we can evaluate all N_i which
1587 // |/ give us the weights for the interpolation stencil!
1588 // o---x
1589 //
1590
1591 Teuchos::SerialDenseMatrix<LO, double> Jacobian(numDimensions, numDimensions);
1592 Teuchos::SerialDenseVector<LO, double> residual(numDimensions);
1593 Teuchos::SerialDenseVector<LO, double> solutionDirection(numDimensions);
1594 Teuchos::SerialDenseVector<LO, double> paramCoords(numDimensions);
1595 Teuchos::SerialDenseSolver<LO, double> problem;
1596 LO numTerms = std::pow(2, numDimensions), iter = 0, max_iter = 5;
1597 double functions[4][8], norm_ref = 1, norm2 = 1, tol = 1e-5;
1598 paramCoords.size(numDimensions);
1599
1600 while ((iter < max_iter) && (norm2 > tol * norm_ref)) {
1601 ++iter;
1602 norm2 = 0;
1603 solutionDirection.size(numDimensions);
1604 residual.size(numDimensions);
1605 Jacobian = 0.0;
1606
1607 // Compute Jacobian and Residual
1608 GetInterpolationFunctions(numDimensions, paramCoords, functions);
1609 for (LO i = 0; i < numDimensions; ++i) {
1610 residual(i) = coord[0][i]; // Add coordinates from point of interest
1611 for (LO k = 0; k < numTerms; ++k) {
1612 residual(i) -= functions[0][k] * coord[k + 1][i]; // Remove contribution from all coarse points
1613 }
1614 if (iter == 1) {
1615 norm_ref += residual(i) * residual(i);
1616 if (i == numDimensions - 1) {
1617 norm_ref = std::sqrt(norm_ref);
1618 }
1619 }
1620
1621 for (LO j = 0; j < numDimensions; ++j) {
1622 for (LO k = 0; k < numTerms; ++k) {
1623 Jacobian(i, j) += functions[j + 1][k] * coord[k + 1][i];
1624 }
1625 }
1626 }
1627
1628 // Set Jacobian, Vectors and solve problem
1629 problem.setMatrix(Teuchos::rcp(&Jacobian, false));
1630 problem.setVectors(Teuchos::rcp(&solutionDirection, false), Teuchos::rcp(&residual, false));
1631 problem.factorWithEquilibration(true);
1632 problem.solve();
1633 problem.unequilibrateLHS();
1634
1635 for (LO i = 0; i < numDimensions; ++i) {
1636 paramCoords(i) = paramCoords(i) + solutionDirection(i);
1637 }
1638
1639 // Recompute Residual norm
1640 GetInterpolationFunctions(numDimensions, paramCoords, functions);
1641 for (LO i = 0; i < numDimensions; ++i) {
1642 double tmp = coord[0][i];
1643 for (LO k = 0; k < numTerms; ++k) {
1644 tmp -= functions[0][k] * coord[k + 1][i];
1645 }
1646 norm2 += tmp * tmp;
1647 tmp = 0;
1648 }
1649 norm2 = std::sqrt(norm2);
1650 }
1651
1652 // Load the interpolation values onto the stencil.
1653 for (LO i = 0; i < 8; ++i) {
1654 stencil[i] = functions[0][i];
1655 }
1656
1657} // End ComputeLinearInterpolationStencil
1658
1659template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1661 GetInterpolationFunctions(const LO numDimensions,
1662 const Teuchos::SerialDenseVector<LO, double> parameters,
1663 double functions[4][8]) const {
1664 double xi = 0.0, eta = 0.0, zeta = 0.0, denominator = 0.0;
1665 if (numDimensions == 1) {
1666 xi = parameters[0];
1667 denominator = 2.0;
1668 } else if (numDimensions == 2) {
1669 xi = parameters[0];
1670 eta = parameters[1];
1671 denominator = 4.0;
1672 } else if (numDimensions == 3) {
1673 xi = parameters[0];
1674 eta = parameters[1];
1675 zeta = parameters[2];
1676 denominator = 8.0;
1677 }
1678
1679 functions[0][0] = (1.0 - xi) * (1.0 - eta) * (1.0 - zeta) / denominator;
1680 functions[0][1] = (1.0 + xi) * (1.0 - eta) * (1.0 - zeta) / denominator;
1681 functions[0][2] = (1.0 - xi) * (1.0 + eta) * (1.0 - zeta) / denominator;
1682 functions[0][3] = (1.0 + xi) * (1.0 + eta) * (1.0 - zeta) / denominator;
1683 functions[0][4] = (1.0 - xi) * (1.0 - eta) * (1.0 + zeta) / denominator;
1684 functions[0][5] = (1.0 + xi) * (1.0 - eta) * (1.0 + zeta) / denominator;
1685 functions[0][6] = (1.0 - xi) * (1.0 + eta) * (1.0 + zeta) / denominator;
1686 functions[0][7] = (1.0 + xi) * (1.0 + eta) * (1.0 + zeta) / denominator;
1687
1688 functions[1][0] = -(1.0 - eta) * (1.0 - zeta) / denominator;
1689 functions[1][1] = (1.0 - eta) * (1.0 - zeta) / denominator;
1690 functions[1][2] = -(1.0 + eta) * (1.0 - zeta) / denominator;
1691 functions[1][3] = (1.0 + eta) * (1.0 - zeta) / denominator;
1692 functions[1][4] = -(1.0 - eta) * (1.0 + zeta) / denominator;
1693 functions[1][5] = (1.0 - eta) * (1.0 + zeta) / denominator;
1694 functions[1][6] = -(1.0 + eta) * (1.0 + zeta) / denominator;
1695 functions[1][7] = (1.0 + eta) * (1.0 + zeta) / denominator;
1696
1697 functions[2][0] = -(1.0 - xi) * (1.0 - zeta) / denominator;
1698 functions[2][1] = -(1.0 + xi) * (1.0 - zeta) / denominator;
1699 functions[2][2] = (1.0 - xi) * (1.0 - zeta) / denominator;
1700 functions[2][3] = (1.0 + xi) * (1.0 - zeta) / denominator;
1701 functions[2][4] = -(1.0 - xi) * (1.0 + zeta) / denominator;
1702 functions[2][5] = -(1.0 + xi) * (1.0 + zeta) / denominator;
1703 functions[2][6] = (1.0 - xi) * (1.0 + zeta) / denominator;
1704 functions[2][7] = (1.0 + xi) * (1.0 + zeta) / denominator;
1705
1706 functions[3][0] = -(1.0 - xi) * (1.0 - eta) / denominator;
1707 functions[3][1] = -(1.0 + xi) * (1.0 - eta) / denominator;
1708 functions[3][2] = -(1.0 - xi) * (1.0 + eta) / denominator;
1709 functions[3][3] = -(1.0 + xi) * (1.0 + eta) / denominator;
1710 functions[3][4] = (1.0 - xi) * (1.0 - eta) / denominator;
1711 functions[3][5] = (1.0 + xi) * (1.0 - eta) / denominator;
1712 functions[3][6] = (1.0 - xi) * (1.0 + eta) / denominator;
1713 functions[3][7] = (1.0 + xi) * (1.0 + eta) / denominator;
1714
1715} // End GetInterpolationFunctions
1716
1717template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1719 const typename Teuchos::Array<LocalOrdinal>::iterator& first1,
1720 const typename Teuchos::Array<LocalOrdinal>::iterator& last1,
1721 const typename Teuchos::Array<LocalOrdinal>::iterator& first2,
1722 const typename Teuchos::Array<LocalOrdinal>::iterator& /* last2 */) const {
1723 typedef typename std::iterator_traits<typename Teuchos::Array<LocalOrdinal>::iterator>::difference_type DT;
1724 DT n = last1 - first1;
1725 DT m = n / 2;
1726 DT z = Teuchos::OrdinalTraits<DT>::zero();
1727 while (m > z) {
1728 DT max = n - m;
1729 for (DT j = 0; j < max; j++) {
1730 for (DT k = j; k >= 0; k -= m) {
1731 if (first1[first2[k + m]] >= first1[first2[k]])
1732 break;
1733 std::swap(first2[k + m], first2[k]);
1734 }
1735 }
1736 m = m / 2;
1737 }
1738} // End sh_sort_permute
1739
1740template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1742 const typename Teuchos::Array<LocalOrdinal>::iterator& first1,
1743 const typename Teuchos::Array<LocalOrdinal>::iterator& last1,
1744 const typename Teuchos::Array<LocalOrdinal>::iterator& first2,
1745 const typename Teuchos::Array<LocalOrdinal>::iterator& /* last2 */) const {
1746 typedef typename std::iterator_traits<typename Teuchos::Array<LocalOrdinal>::iterator>::difference_type DT;
1747 DT n = last1 - first1;
1748 DT m = n / 2;
1749 DT z = Teuchos::OrdinalTraits<DT>::zero();
1750 while (m > z) {
1751 DT max = n - m;
1752 for (DT j = 0; j < max; j++) {
1753 for (DT k = j; k >= 0; k -= m) {
1754 if (first1[k + m] >= first1[k])
1755 break;
1756 std::swap(first1[k + m], first1[k]);
1757 std::swap(first2[k + m], first2[k]);
1758 }
1759 }
1760 m = m / 2;
1761 }
1762} // End sh_sort2
1763
1764template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
1766 GetGIDLocalLexicographic(const GO i, const GO j, const GO k,
1767 const Array<LO> coarseNodeFineIndices, const RCP<GeometricData> myGeo,
1768 const LO myRankIndex, const LO pi, const LO pj, const LO /* pk */,
1769 const typename std::vector<std::vector<GO> >::iterator blockStart,
1770 const typename std::vector<std::vector<GO> >::iterator blockEnd,
1771 GO& myGID, LO& myPID, LO& myLID) const {
1772 LO ni = -1, nj = -1, li = -1, lj = -1, lk = -1;
1773 LO myRankGuess = myRankIndex;
1774 // We try to make a logical guess as to which PID owns the current coarse node
1775 if (i == 0 && myGeo->ghostInterface[0]) {
1776 --myRankGuess;
1777 } else if ((i == myGeo->ghostedCoarseNodesPerDir[0] - 1) && myGeo->ghostInterface[1]) {
1778 ++myRankGuess;
1779 }
1780 if (j == 0 && myGeo->ghostInterface[2]) {
1781 myRankGuess -= pi;
1782 } else if ((j == myGeo->ghostedCoarseNodesPerDir[1] - 1) && myGeo->ghostInterface[3]) {
1783 myRankGuess += pi;
1784 }
1785 if (k == 0 && myGeo->ghostInterface[4]) {
1786 myRankGuess -= pj * pi;
1787 } else if ((k == myGeo->ghostedCoarseNodesPerDir[2] - 1) && myGeo->ghostInterface[5]) {
1788 myRankGuess += pj * pi;
1789 }
1790 if (coarseNodeFineIndices[0] >= myGeo->meshData[myRankGuess][3] && coarseNodeFineIndices[0] <= myGeo->meshData[myRankGuess][4] && coarseNodeFineIndices[1] >= myGeo->meshData[myRankGuess][5] && coarseNodeFineIndices[1] <= myGeo->meshData[myRankGuess][6] && coarseNodeFineIndices[2] >= myGeo->meshData[myRankGuess][7] && coarseNodeFineIndices[2] <= myGeo->meshData[myRankGuess][8]) {
1791 myPID = myGeo->meshData[myRankGuess][0];
1792 ni = myGeo->meshData[myRankGuess][4] - myGeo->meshData[myRankGuess][3] + 1;
1793 nj = myGeo->meshData[myRankGuess][6] - myGeo->meshData[myRankGuess][5] + 1;
1794 li = coarseNodeFineIndices[0] - myGeo->meshData[myRankGuess][3];
1795 lj = coarseNodeFineIndices[1] - myGeo->meshData[myRankGuess][5];
1796 lk = coarseNodeFineIndices[2] - myGeo->meshData[myRankGuess][7];
1797 myLID = lk * nj * ni + lj * ni + li;
1798 myGID = myGeo->meshData[myRankGuess][9] + myLID;
1799 } else { // The guess failed, let us use the heavy artilery: std::find_if()
1800 // It could be interesting to monitor how many times this branch of the code gets
1801 // used as it is far more expensive than the above one...
1802 auto nodeRank = std::find_if(blockStart, blockEnd,
1803 [coarseNodeFineIndices](const std::vector<GO>& vec) {
1804 if (coarseNodeFineIndices[0] >= vec[3] && coarseNodeFineIndices[0] <= vec[4] && coarseNodeFineIndices[1] >= vec[5] && coarseNodeFineIndices[1] <= vec[6] && coarseNodeFineIndices[2] >= vec[7] && coarseNodeFineIndices[2] <= vec[8]) {
1805 return true;
1806 } else {
1807 return false;
1808 }
1809 });
1810 myPID = (*nodeRank)[0];
1811 ni = (*nodeRank)[4] - (*nodeRank)[3] + 1;
1812 nj = (*nodeRank)[6] - (*nodeRank)[5] + 1;
1813 li = coarseNodeFineIndices[0] - (*nodeRank)[3];
1814 lj = coarseNodeFineIndices[1] - (*nodeRank)[5];
1815 lk = coarseNodeFineIndices[2] - (*nodeRank)[7];
1816 myLID = lk * nj * ni + lj * ni + li;
1817 myGID = (*nodeRank)[9] + myLID;
1818 }
1819} // End GetGIDLocalLexicographic
1820
1821} // namespace MueLu
1822
1823#define MUELU_GENERALGEOMETRICPFACTORY_SHORT
1824#endif // MUELU_GENERALGEOMETRICPFACTORY_DEF_HPP
MueLu::DefaultNode Node
Exception throws to report errors in the internal logical of the program.
Timer to be used in factories. Similar to Monitor but with additional timers.
void Input(Level &level, const std::string &varName) const
T Get(Level &level, const std::string &varName) const
void Set(Level &level, const std::string &varName, const T &data) const
void MakeGeneralGeometricP(RCP< GeometricData > myGeo, const RCP< Xpetra::MultiVector< typename Teuchos::ScalarTraits< Scalar >::coordinateType, LO, GO, NO > > &fCoords, const LO nnzP, const LO dofsPerNode, RCP< const Map > &stridedDomainMapP, RCP< Matrix > &Amat, RCP< Matrix > &P, RCP< Xpetra::MultiVector< typename Teuchos::ScalarTraits< Scalar >::coordinateType, LO, GO, NO > > &cCoords, RCP< NodesIDs > ghostedCoarseNodes, Array< Array< GO > > coarseNodesGIDs, int interpolationOrder) const
RCP< const ParameterList > GetValidParameterList() const
Return a const parameter list of valid parameters that setParameterList() will accept.
void DeclareInput(Level &fineLevel, Level &coarseLevel) const
Input.
void Build(Level &fineLevel, Level &coarseLevel) const
Build an object with this factory.
void sh_sort_permute(const typename Teuchos::Array< LocalOrdinal >::iterator &first1, const typename Teuchos::Array< LocalOrdinal >::iterator &last1, const typename Teuchos::Array< LocalOrdinal >::iterator &first2, const typename Teuchos::Array< LocalOrdinal >::iterator &last2) const
void ComputeLinearInterpolationStencil(const LO numDimension, const Array< Array< typename Teuchos::ScalarTraits< Scalar >::coordinateType > > coord, std::vector< double > &stencil) const
void BuildP(Level &fineLevel, Level &coarseLevel) const
Abstract Build method.
void ComputeStencil(const LO numDimension, const Array< GO > currentNodeIndices, const Array< GO > coarseNodeIndices, const LO rate[3], const Array< Array< typename Teuchos::ScalarTraits< Scalar >::coordinateType > > coord, const int interpolationOrder, std::vector< double > &stencil) const
void GetCoarsePoints(const int interpolationOrder, const LO blkSize, RCP< const Map > fineCoordsMap, RCP< GeometricData > myGeometry, RCP< NodesIDs > ghostedCoarseNodes, Array< Array< GO > > &lCoarseNodesGIDs) const
void GetInterpolationFunctions(const LO numDimension, const Teuchos::SerialDenseVector< LO, double > parameters, double functions[4][8]) const
void MeshLayoutInterface(const int interpolationOrder, const LO blkSize, RCP< const Map > fineCoordsMap, RCP< GeometricData > myGeometry, RCP< NodesIDs > ghostedCoarseNodes, Array< Array< GO > > &lCoarseNodesGIDs) const
void sh_sort2(const typename Teuchos::Array< LocalOrdinal >::iterator &first1, const typename Teuchos::Array< LocalOrdinal >::iterator &last1, const typename Teuchos::Array< LocalOrdinal >::iterator &first2, const typename Teuchos::Array< LocalOrdinal >::iterator &last2) const
void ComputeConstantInterpolationStencil(const LO numDimension, const Array< GO > currentNodeIndices, const Array< GO > coarseNodeIndices, const LO rate[3], std::vector< double > &stencil) const
Class that holds all level-specific information.
bool IsAvailable(const std::string &ename, const FactoryBase *factory=NoFactory::get()) const
Test whether a need's value has been saved.
void DeclareInput(const std::string &ename, const FactoryBase *factory, const FactoryBase *requestedBy=NoFactory::get())
Callback from FactoryBase::CallDeclareInput() and FactoryBase::DeclareInput().
int GetLevelID() const
Return level number.
T & Get(const std::string &ename, const FactoryBase *factory=NoFactory::get())
Get data without decrementing associated storage counter (i.e., read-only access)....
static const NoFactory * get()
virtual const Teuchos::ParameterList & GetParameterList() const
Teuchos::FancyOStream & GetOStream(MsgType type, int thisProcRankOnly=0) const
Get an output stream for outputting the input message type.
Namespace for MueLu classes and methods.
@ Runtime1
Description of what is happening (more verbose).