MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_IntrepidPCoarsenFactory_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_IPCFACTORY_DEF_HPP
11#define MUELU_IPCFACTORY_DEF_HPP
12
13#include <Xpetra_Matrix.hpp>
14#include <Xpetra_IO.hpp>
15#include <sstream>
16#include <algorithm>
17
19
20#include "MueLu_Level.hpp"
21#include "MueLu_MasterList.hpp"
22#include "MueLu_Monitor.hpp"
23#include "MueLu_PerfUtils.hpp"
24#include "MueLu_Utilities.hpp"
25
26#include "Teuchos_ScalarTraits.hpp"
27
28// Intrepid Headers
29
30// Intrepid_HGRAD_HEX_C1_FEM.hpp
31// Intrepid_HGRAD_HEX_C2_FEM.hpp
32// Intrepid_HGRAD_HEX_Cn_FEM.hpp
33// Intrepid_HGRAD_HEX_I2_FEM.hpp
34#include "Intrepid2_HGRAD_LINE_C1_FEM.hpp"
35#include "Intrepid2_HGRAD_LINE_Cn_FEM.hpp"
36// Intrepid_HGRAD_LINE_Cn_FEM_JACOBI.hpp
37// Intrepid_HGRAD_POLY_C1_FEM.hpp
38// Intrepid_HGRAD_PYR_C1_FEM.hpp
39// Intrepid_HGRAD_PYR_I2_FEM.hpp
40#include "Intrepid2_HGRAD_QUAD_C1_FEM.hpp"
41//#include Intrepid_HGRAD_QUAD_C2_FEM.hpp
42#include "Intrepid2_HGRAD_QUAD_Cn_FEM.hpp"
43// Intrepid_HGRAD_TET_C1_FEM.hpp
44// Intrepid_HGRAD_TET_C2_FEM.hpp
45// Intrepid_HGRAD_TET_Cn_FEM.hpp
46// Intrepid_HGRAD_TET_Cn_FEM_ORTH.hpp
47// Intrepid_HGRAD_TET_COMP12_FEM.hpp
48// Intrepid_HGRAD_TRI_C1_FEM.hpp
49// Intrepid_HGRAD_TRI_C2_FEM.hpp
50// Intrepid_HGRAD_TRI_Cn_FEM.hpp
51// Intrepid_HGRAD_TRI_Cn_FEM_ORTH.hpp
52// Intrepid_HGRAD_WEDGE_C1_FEM.hpp
53// Intrepid_HGRAD_WEDGE_C2_FEM.hpp
54// Intrepid_HGRAD_WEDGE_I2_FEM.hpp
55
56// Helper Macro to avoid "unrequested" warnings
57#define MUELU_LEVEL_SET_IF_REQUESTED_OR_KEPT(level, ename, entry) \
58 { \
59 if (level.IsRequested(ename, this) || level.GetKeepFlag(ename, this) != 0) this->Set(level, ename, entry); \
60 }
61
62namespace MueLu {
63
64/*********************************************************************************************************/
65namespace MueLuIntrepid {
66inline std::string tolower(const std::string &str) {
67 std::string data(str);
68 std::transform(data.begin(), data.end(), data.begin(), ::tolower);
69 return data;
70}
71
72/*********************************************************************************************************/
73template <class Basis, class LOFieldContainer, class LocalOrdinal, class GlobalOrdinal, class Node>
74void FindGeometricSeedOrdinals(Teuchos::RCP<Basis> basis, const LOFieldContainer &elementToNodeMap,
75 std::vector<std::vector<LocalOrdinal>> &seeds,
76 const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node> &rowMap,
77 const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node> &columnMap) {
78 // For each subcell represented by the elements in elementToNodeMap, we want to identify a globally
79 // unique degree of freedom. Because the other "seed" interfaces in MueLu expect a local ordinal, we
80 // store local ordinals in the resulting seeds container.
81
82 // The approach is as follows. For each element, we iterate through the subcells of the domain topology.
83 // We determine which, if any, of these has the lowest global ID owned that is locally owned. We then insert
84 // the local ID corresponding to this in a vector<set<int>> container whose outer index is the spatial dimension
85 // of the subcell. The set lets us conveniently enforce uniqueness of the stored LIDs.
86
87 shards::CellTopology cellTopo = basis->getBaseCellTopology();
88 int spaceDim = cellTopo.getDimension();
89 seeds.clear();
90 seeds.resize(spaceDim + 1);
91 typedef GlobalOrdinal GO;
92 typedef LocalOrdinal LO;
93
94 LocalOrdinal lo_invalid = Teuchos::OrdinalTraits<LO>::invalid();
95 GlobalOrdinal go_invalid = Teuchos::OrdinalTraits<GO>::invalid();
96
97 std::vector<std::set<LocalOrdinal>> seedSets(spaceDim + 1);
98
99 int numCells = elementToNodeMap.extent(0);
100 auto elementToNodeMap_host = Kokkos::create_mirror_view(elementToNodeMap);
101 Kokkos::deep_copy(elementToNodeMap_host, elementToNodeMap);
102 for (int cellOrdinal = 0; cellOrdinal < numCells; cellOrdinal++) {
103 for (int d = 0; d <= spaceDim; d++) {
104 int subcellCount = cellTopo.getSubcellCount(d);
105 for (int subcord = 0; subcord < subcellCount; subcord++) {
106 int dofCount = basis->getDofCount(d, subcord);
107 if (dofCount == 0) continue;
108 // otherwise, we want to insert the LID corresponding to the least globalID that is locally owned
109 GO leastGlobalDofOrdinal = go_invalid;
110 LO LID_leastGlobalDofOrdinal = lo_invalid;
111 for (int basisOrdinalOrdinal = 0; basisOrdinalOrdinal < dofCount; basisOrdinalOrdinal++) {
112 int basisOrdinal = basis->getDofOrdinal(d, subcord, basisOrdinalOrdinal);
113 int colLID = elementToNodeMap_host(cellOrdinal, basisOrdinal);
114 if (colLID != Teuchos::OrdinalTraits<LO>::invalid()) {
115 GlobalOrdinal colGID = columnMap.getGlobalElement(colLID);
116 LocalOrdinal rowLID = rowMap.getLocalElement(colGID);
117 if (rowLID != lo_invalid) {
118 if ((leastGlobalDofOrdinal == go_invalid) || (colGID < leastGlobalDofOrdinal)) {
119 // replace with rowLID
120 leastGlobalDofOrdinal = colGID;
121 LID_leastGlobalDofOrdinal = rowLID;
122 }
123 }
124 }
125 }
126 if (leastGlobalDofOrdinal != go_invalid) {
127 seedSets[d].insert(LID_leastGlobalDofOrdinal);
128 }
129 }
130 }
131 }
132 for (int d = 0; d <= spaceDim; d++) {
133 seeds[d] = std::vector<LocalOrdinal>(seedSets[d].begin(), seedSets[d].end());
134 }
135}
136
137/*********************************************************************************************************/
138// Syntax [HGRAD|HCURL|HDIV][_| ][HEX|LINE|POLY|PYR|QUAD|TET|TRI|WEDGE][_| ][C|I][1|2|n]
139// Inputs:
140// name - name of the intrepid basis to generate
141// Outputs:
142// degree - order of resulting discretization
143// return value - Intrepid2 basis correspionding to the name
144template <class Scalar, class KokkosExecutionSpace>
145Teuchos::RCP<Intrepid2::Basis<KokkosExecutionSpace, Scalar, Scalar>> BasisFactory(const std::string &name, int &degree) {
146 using std::string;
147 using Teuchos::rcp;
148 string myerror("IntrepidBasisFactory: cannot parse string name '" + name + "'");
149
150 // Syntax [HGRAD|HCURL|HDIV][_| ][HEX|LINE|POLY|PYR|QUAD|TET|TRI|WEDGE][_| ][C|I][1|2|n]
151
152 // Get the derivative type
153 size_t pos1 = name.find_first_of(" _");
154 if (pos1 == 0) throw std::runtime_error(myerror);
155 string deriv = tolower(name.substr(0, pos1));
156 if (deriv != "hgrad" && deriv != "hcurl" && deriv != "hdiv") throw std::runtime_error(myerror);
157
158 // Get the element type
159 pos1++;
160 size_t pos2 = name.find_first_of(" _", pos1);
161 if (pos2 == 0) throw std::runtime_error(myerror);
162 string el = tolower(name.substr(pos1, pos2 - pos1));
163 if (el != "hex" && el != "line" && el != "poly" && el != "pyr" && el != "quad" && el != "tet" && el != "tri" && el != "wedge") throw std::runtime_error(myerror);
164
165 // Get the polynomial type
166 pos2++;
167 string poly = tolower(name.substr(pos2, 1));
168 if (poly != "c" && poly != "i") throw std::runtime_error(myerror);
169
170 // Get the degree
171 pos2++;
172 degree = std::stoi(name.substr(pos2, 1));
173 if (degree <= 0) throw std::runtime_error(myerror);
174
175 // FIXME LATER: Allow for alternative point types for Kirby elements
176 if (deriv == "hgrad" && el == "quad" && poly == "c") {
177 if (degree == 1)
178 return rcp(new Intrepid2::Basis_HGRAD_QUAD_C1_FEM<KokkosExecutionSpace, Scalar, Scalar>());
179 else
180 return rcp(new Intrepid2::Basis_HGRAD_QUAD_Cn_FEM<KokkosExecutionSpace, Scalar, Scalar>(degree, Intrepid2::POINTTYPE_EQUISPACED));
181 } else if (deriv == "hgrad" && el == "line" && poly == "c") {
182 if (degree == 1)
183 return rcp(new Intrepid2::Basis_HGRAD_LINE_C1_FEM<KokkosExecutionSpace, Scalar, Scalar>());
184 else
185 return rcp(new Intrepid2::Basis_HGRAD_LINE_Cn_FEM<KokkosExecutionSpace, Scalar, Scalar>(degree, Intrepid2::POINTTYPE_EQUISPACED));
186 }
187
188 // Error out
189 throw std::runtime_error(myerror);
190 TEUCHOS_UNREACHABLE_RETURN(Teuchos::null);
191}
192
193/*********************************************************************************************************/
194// Gets the "lo" nodes nested into a "hi" basis. Only works on quads and lines for a lo basis of p=1
195// Inputs:
196// hi_basis - Higher order Basis
197// Outputs:
198// lo_node_in_hi - std::vector<size_t> of size lo dofs in the reference element, which describes the coindcident hi dots
199// hi_DofCoords - FC<Scalar> of size (#hi dofs, dim) with the coordinate locations of the hi dofs on the reference element
200template <class Scalar, class KokkosDeviceType>
201void IntrepidGetP1NodeInHi(const Teuchos::RCP<Intrepid2::Basis<typename KokkosDeviceType::execution_space, Scalar, Scalar>> &hi_basis,
202 std::vector<size_t> &lo_node_in_hi,
203 Kokkos::DynRankView<Scalar, KokkosDeviceType> &hi_DofCoords) {
204 typedef typename KokkosDeviceType::execution_space KokkosExecutionSpace;
205 // Figure out which unknowns in hi_basis correspond to nodes on lo_basis. This varies by element type.
206 size_t degree = hi_basis->getDegree();
207 lo_node_in_hi.resize(0);
208
209 if (!rcp_dynamic_cast<Intrepid2::Basis_HGRAD_QUAD_Cn_FEM<KokkosExecutionSpace, Scalar, Scalar>>(hi_basis).is_null()) {
210 // HGRAD QUAD Cn: Numbering as per the Kirby convention (straight across, bottom to top)
211 lo_node_in_hi.insert(lo_node_in_hi.end(), {0, degree, (degree + 1) * (degree + 1) - 1, degree * (degree + 1)});
212 } else if (!rcp_dynamic_cast<Intrepid2::Basis_HGRAD_LINE_Cn_FEM<KokkosExecutionSpace, Scalar, Scalar>>(hi_basis).is_null()) {
213 // HGRAD LINE Cn: Numbering as per the Kirby convention (straight across)
214 lo_node_in_hi.insert(lo_node_in_hi.end(), {0, degree});
215 } else
216 throw std::runtime_error("IntrepidPCoarsenFactory: Unknown element type");
217
218 // Get coordinates of the hi_basis dof's
219 Kokkos::resize(hi_DofCoords, hi_basis->getCardinality(), hi_basis->getBaseCellTopology().getDimension());
220 hi_basis->getDofCoords(hi_DofCoords);
221}
222
223/*********************************************************************************************************/
224// Given a list of candidates picks a definitive list of "representative" higher order nodes for each lo order node via the "smallest GID" rule
225// Input:
226// representative_node_candidates - std::vector<std::vector<size_t> > of lists of "representative candidate" hi dofs for each lo dof
227// hi_elemToNode - FC<LO> containing the high order element-to-node map
228// hi_columnMap - Column map of the higher order matrix
229// Output:
230// lo_elemToHiRepresentativeNode - FC<LO> of size (# elements, # lo dofs per element) listing the hi unknown chosen as the single representative for each lo unknown for counting purposes
231template <class LocalOrdinal, class GlobalOrdinal, class Node, class LOFieldContainer>
232void GenerateLoNodeInHiViaGIDs(const std::vector<std::vector<size_t>> &candidates, const LOFieldContainer &hi_elemToNode,
233 RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> &hi_columnMap,
234 LOFieldContainer &lo_elemToHiRepresentativeNode) {
235 typedef GlobalOrdinal GO;
236
237 // Given: A set of "candidate" hi-DOFs to serve as the "representative" DOF for each lo-DOF on the reference element.
238 // Algorithm: For each element, we choose the lowest GID of the candidates for each DOF to generate the lo_elemToHiRepresentativeNode map
239
240 size_t numElem = hi_elemToNode.extent(0);
241 size_t lo_nperel = candidates.size();
242 Kokkos::resize(lo_elemToHiRepresentativeNode, numElem, lo_nperel);
243
244 auto lo_elemToHiRepresentativeNode_host = Kokkos::create_mirror_view(lo_elemToHiRepresentativeNode);
245 auto hi_elemToNode_host = Kokkos::create_mirror_view(hi_elemToNode);
246 Kokkos::deep_copy(hi_elemToNode_host, hi_elemToNode);
247 for (size_t i = 0; i < numElem; i++)
248 for (size_t j = 0; j < lo_nperel; j++) {
249 if (candidates[j].size() == 1)
250 lo_elemToHiRepresentativeNode_host(i, j) = hi_elemToNode_host(i, candidates[j][0]);
251 else {
252 // First we get the GIDs for each candidate
253 std::vector<GO> GID(candidates[j].size());
254 for (size_t k = 0; k < (size_t)candidates[j].size(); k++)
255 GID[k] = hi_columnMap->getGlobalElement(hi_elemToNode_host(i, candidates[j][k]));
256
257 // Find the one with smallest GID
258 size_t which = std::distance(GID.begin(), std::min_element(GID.begin(), GID.end()));
259
260 // Record this
261 lo_elemToHiRepresentativeNode_host(i, j) = hi_elemToNode_host(i, candidates[j][which]);
262 }
263 }
264 Kokkos::deep_copy(lo_elemToHiRepresentativeNode, lo_elemToHiRepresentativeNode_host);
265}
266
267/*********************************************************************************************************/
268// Inputs:
269// hi_elemToNode - FC<LO> containing the high order element-to-node map
270// hi_nodeIsOwned - std::vector<bool> of size hi's column map, which described hi node ownership
271// lo_elemToHiRepresentativeNode - FC<LO> of size (# elements, # lo dofs per element) listing the hi unknown chosen as the single representative for each lo unknown for counting purposes
272// Outputs:
273// lo_elemToNode - FC<LO> containing the low order element-to-node map.
274// lo_nodeIsOwned - std::vector<bool> of size lo's (future) column map, which described lo node ownership
275// hi_to_lo_map - std::vector<LO> of size equal to hi's column map, which contains the lo id each hi idea maps to (or invalid if it doesn't)
276// lo_numOwnedNodes- Number of lo owned nodes
277template <class LocalOrdinal, class LOFieldContainer>
278void BuildLoElemToNodeViaRepresentatives(const LOFieldContainer &hi_elemToNode,
279 const std::vector<bool> &hi_nodeIsOwned,
280 const LOFieldContainer &lo_elemToHiRepresentativeNode,
281 LOFieldContainer &lo_elemToNode,
282 std::vector<bool> &lo_nodeIsOwned,
283 std::vector<LocalOrdinal> &hi_to_lo_map,
284 int &lo_numOwnedNodes) {
285 typedef LocalOrdinal LO;
286 using Teuchos::RCP;
287 // printf("CMS:BuildLoElemToNodeViaRepresentatives: hi_elemToNode.rank() = %d hi_elemToNode.size() = %d\n",hi_elemToNode.rank(), hi_elemToNode.size());
288 size_t numElem = hi_elemToNode.extent(0);
289 size_t hi_numNodes = hi_nodeIsOwned.size();
290 size_t lo_nperel = lo_elemToHiRepresentativeNode.extent(1);
291 Kokkos::resize(lo_elemToNode, numElem, lo_nperel);
292
293 // Start by flagginc the representative nodes
294 auto lo_elemToHiRepresentativeNode_host = Kokkos::create_mirror_view(lo_elemToHiRepresentativeNode);
295 Kokkos::deep_copy(lo_elemToHiRepresentativeNode_host, lo_elemToHiRepresentativeNode);
296 std::vector<bool> is_low_order(hi_numNodes, false);
297 for (size_t i = 0; i < numElem; i++)
298 for (size_t j = 0; j < lo_nperel; j++) {
299 LO id = lo_elemToHiRepresentativeNode_host(i, j);
300 is_low_order[id] = true; // This can overwrite and that is OK.
301 }
302
303 // Count the number of lo owned nodes, generating a local index for lo nodes
304 lo_numOwnedNodes = 0;
305 size_t lo_numNodes = 0;
306 hi_to_lo_map.resize(hi_numNodes, Teuchos::OrdinalTraits<LO>::invalid());
307
308 for (size_t i = 0; i < hi_numNodes; i++)
309 if (is_low_order[i]) {
310 hi_to_lo_map[i] = lo_numNodes;
311 lo_numNodes++;
312 if (hi_nodeIsOwned[i]) lo_numOwnedNodes++;
313 }
314
315 // Flag the owned lo nodes
316 lo_nodeIsOwned.resize(lo_numNodes, false);
317 for (size_t i = 0; i < hi_numNodes; i++) {
318 if (is_low_order[i] && hi_nodeIsOwned[i])
319 lo_nodeIsOwned[hi_to_lo_map[i]] = true;
320 }
321
322 // Translate lo_elemToNode to a lo local index
323 auto lo_elemToNode_host = Kokkos::create_mirror_view(lo_elemToNode);
324 for (size_t i = 0; i < numElem; i++)
325 for (size_t j = 0; j < lo_nperel; j++)
326 lo_elemToNode_host(i, j) = hi_to_lo_map[lo_elemToHiRepresentativeNode_host(i, j)];
327
328 // Check for the [E|T]petra column map ordering property, namely LIDs for owned nodes should all appear first.
329 // Since we're injecting from the higher-order mesh, it should be true, but we should add an error check & throw in case.
330 bool map_ordering_test_passed = true;
331 for (size_t i = 0; i < lo_numNodes - 1; i++)
332 if (!lo_nodeIsOwned[i] && lo_nodeIsOwned[i + 1])
333 map_ordering_test_passed = false;
334
335 if (!map_ordering_test_passed)
336 throw std::runtime_error("MueLu::MueLuIntrepid::BuildLoElemToNodeViaRepresentatives failed map ordering test");
337 Kokkos::deep_copy(lo_elemToNode, lo_elemToNode_host);
338}
339
340/*********************************************************************************************************/
341// Inputs:
342// hi_elemToNode - FC<LO> containing the high order element-to-node map
343// hi_nodeIsOwned - std::vector<bool> of size hi's column map, which described hi node ownership
344// lo_node_in_hi - std::vector<size_t> of size lo dofs in the reference element, which describes the coindcident hi dots
345// hi_isDirichlet - ArrayView<int> of size of hi's column map, which has a 1 if the unknown is Dirichlet and a 0 if it isn't.
346// Outputs:
347// lo_elemToNode - FC<LO> containing the low order element-to-node map.
348// lo_nodeIsOwned - std::vector<bool> of size lo's (future) column map, which described lo node ownership
349// hi_to_lo_map - std::vector<LO> of size equal to hi's column map, which contains the lo id each hi idea maps to (or invalid if it doesn't)
350// lo_numOwnedNodes- Number of lo owned nodes
351template <class LocalOrdinal, class LOFieldContainer>
352void BuildLoElemToNode(const LOFieldContainer &hi_elemToNode,
353 const std::vector<bool> &hi_nodeIsOwned,
354 const std::vector<size_t> &lo_node_in_hi,
355 const Teuchos::ArrayRCP<const int> &hi_isDirichlet,
356 LOFieldContainer &lo_elemToNode,
357 std::vector<bool> &lo_nodeIsOwned,
358 std::vector<LocalOrdinal> &hi_to_lo_map,
359 int &lo_numOwnedNodes) {
360 typedef LocalOrdinal LO;
361 using Teuchos::RCP;
362 LocalOrdinal LOINVALID = Teuchos::OrdinalTraits<LocalOrdinal>::invalid();
363 // printf("CMS:BuildLoElemToNode: hi_elemToNode.rank() = %d hi_elemToNode.size() = %d\n",hi_elemToNode.rank(), hi_elemToNode.size());
364
365 size_t numElem = hi_elemToNode.extent(0);
366 size_t hi_numNodes = hi_nodeIsOwned.size();
367
368 size_t lo_nperel = lo_node_in_hi.size();
369 Kokkos::resize(lo_elemToNode, numElem, lo_nperel);
370
371 // Build lo_elemToNode (in the hi local index ordering) and flag owned ones
372 std::vector<bool> is_low_order(hi_numNodes, false);
373 auto hi_elemToNode_host = Kokkos::create_mirror_view(hi_elemToNode);
374 Kokkos::deep_copy(hi_elemToNode_host, hi_elemToNode);
375 auto lo_elemToNode_host = Kokkos::create_mirror_view(lo_elemToNode);
376 for (size_t i = 0; i < numElem; i++)
377 for (size_t j = 0; j < lo_nperel; j++) {
378 LO lid = hi_elemToNode_host(i, lo_node_in_hi[j]);
379
380 // Remove Dirichlet
381 if (hi_isDirichlet[lid])
382 lo_elemToNode_host(i, j) = LOINVALID;
383 else {
384 lo_elemToNode_host(i, j) = lid;
385 is_low_order[hi_elemToNode_host(i, lo_node_in_hi[j])] = true; // This can overwrite and that is OK.
386 }
387 }
388
389 // Count the number of lo owned nodes, generating a local index for lo nodes
390 lo_numOwnedNodes = 0;
391 size_t lo_numNodes = 0;
392 hi_to_lo_map.resize(hi_numNodes, Teuchos::OrdinalTraits<LO>::invalid());
393
394 for (size_t i = 0; i < hi_numNodes; i++)
395 if (is_low_order[i]) {
396 hi_to_lo_map[i] = lo_numNodes;
397 lo_numNodes++;
398 if (hi_nodeIsOwned[i]) lo_numOwnedNodes++;
399 }
400
401 // Flag the owned lo nodes
402 lo_nodeIsOwned.resize(lo_numNodes, false);
403 for (size_t i = 0; i < hi_numNodes; i++) {
404 if (is_low_order[i] && hi_nodeIsOwned[i])
405 lo_nodeIsOwned[hi_to_lo_map[i]] = true;
406 }
407
408 // Translate lo_elemToNode to a lo local index
409 for (size_t i = 0; i < numElem; i++)
410 for (size_t j = 0; j < lo_nperel; j++) {
411 if (lo_elemToNode_host(i, j) != LOINVALID)
412 lo_elemToNode_host(i, j) = hi_to_lo_map[lo_elemToNode_host(i, j)];
413 }
414 Kokkos::deep_copy(lo_elemToNode, lo_elemToNode_host);
415
416 // Check for the [E|T]petra column map ordering property, namely LIDs for owned nodes should all appear first.
417 // Since we're injecting from the higher-order mesh, it should be true, but we should add an error check & throw in case.
418 bool map_ordering_test_passed = true;
419 for (size_t i = 0; i < lo_numNodes - 1; i++)
420 if (!lo_nodeIsOwned[i] && lo_nodeIsOwned[i + 1])
421 map_ordering_test_passed = false;
422
423 if (!map_ordering_test_passed)
424 throw std::runtime_error("MueLu::MueLuIntrepid::BuildLoElemToNode failed map ordering test");
425}
426
427/*********************************************************************************************************/
428// Generates the lo_columnMap
429// Input:
430// hi_importer - Importer from the hi matrix
431// hi_to_lo_map - std::vector<LO> of size equal to hi's column map, which contains the lo id each hi idea maps to (or invalid if it doesn't)
432// lo_DomainMap - Domain map for the lo matrix
433// lo_columnMapLength - Number of local columns in the lo column map
434// Output:
435// lo_columnMap - Column map of the lower order matrix
436template <class LocalOrdinal, class GlobalOrdinal, class Node>
437void GenerateColMapFromImport(const Xpetra::Import<LocalOrdinal, GlobalOrdinal, Node> &hi_importer, const std::vector<LocalOrdinal> &hi_to_lo_map, const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node> &lo_domainMap, const size_t &lo_columnMapLength, RCP<const Xpetra::Map<LocalOrdinal, GlobalOrdinal, Node>> &lo_columnMap) {
438 typedef LocalOrdinal LO;
439 typedef GlobalOrdinal GO;
440 typedef Node NO;
441 typedef Xpetra::Map<LO, GO, NO> Map;
442 typedef Xpetra::Vector<GO, LO, GO, NO> GOVector;
443
444 GO go_invalid = Teuchos::OrdinalTraits<GO>::invalid();
445 LO lo_invalid = Teuchos::OrdinalTraits<LO>::invalid();
446
447 RCP<const Map> hi_domainMap = hi_importer.getSourceMap();
448 RCP<const Map> hi_columnMap = hi_importer.getTargetMap();
449 // Figure out the GIDs of my non-owned P1 nodes
450 // HOW: We can build a GOVector(domainMap) and fill the values with either invalid() or the P1 domainMap.GID() for that guy.
451 // Then we can use A's importer to get a GOVector(colMap) with that information.
452
453 // NOTE: This assumes rowMap==colMap and [E|T]petra ordering of all the locals first in the colMap
454 RCP<GOVector> dvec = Xpetra::VectorFactory<GO, LO, GO, NO>::Build(hi_domainMap);
455 {
456 ArrayRCP<GO> dvec_data = dvec->getDataNonConst(0);
457 for (size_t i = 0; i < hi_domainMap->getLocalNumElements(); i++) {
458 if (hi_to_lo_map[i] != lo_invalid)
459 dvec_data[i] = lo_domainMap.getGlobalElement(hi_to_lo_map[i]);
460 else
461 dvec_data[i] = go_invalid;
462 }
463 }
464
465 RCP<GOVector> cvec = Xpetra::VectorFactory<GO, LO, GO, NO>::Build(hi_columnMap, true);
466 cvec->doImport(*dvec, hi_importer, Xpetra::ADD);
467
468 // Generate the lo_columnMap
469 // HOW: We can use the local hi_to_lo_map from the GID's in cvec to generate the non-contiguous colmap ids.
470 Array<GO> lo_col_data(lo_columnMapLength);
471 {
472 ArrayRCP<GO> cvec_data = cvec->getDataNonConst(0);
473 for (size_t i = 0, idx = 0; i < hi_columnMap->getLocalNumElements(); i++) {
474 if (hi_to_lo_map[i] != lo_invalid) {
475 lo_col_data[idx] = cvec_data[i];
476 idx++;
477 }
478 }
479 }
480
481 lo_columnMap = Xpetra::MapFactory<LO, GO, NO>::Build(lo_domainMap.lib(), Teuchos::OrdinalTraits<Xpetra::global_size_t>::invalid(), lo_col_data(), lo_domainMap.getIndexBase(), lo_domainMap.getComm());
482}
483
484/*********************************************************************************************************/
485// Generates a list of "representative candidate" hi dofs for each lo dof on the reference element. This is to be used in global numbering.
486// Input:
487// basis - The low order basis
488// ReferenceNodeLocations - FC<Scalar> of size (#hidofs, dim) Locations of higher order nodes on the reference element
489// threshold - tolerance for equivalance testing
490// Output:
491// representative_node_candidates - std::vector<std::vector<size_t> > of lists of "representative candidate" hi dofs for each lo dof
492template <class Basis, class SCFieldContainer>
493void GenerateRepresentativeBasisNodes(const Basis &basis, const SCFieldContainer &ReferenceNodeLocations, const double threshold, std::vector<std::vector<size_t>> &representative_node_candidates) {
494 typedef SCFieldContainer FC;
495 typedef typename FC::data_type SC;
496
497 // Evaluate the linear basis functions at the Pn nodes
498 size_t numFieldsHi = ReferenceNodeLocations.extent(0);
499 // size_t dim = ReferenceNodeLocations.extent(1);
500 size_t numFieldsLo = basis.getCardinality();
501
502 FC LoValues("LoValues", numFieldsLo, numFieldsHi);
503
504 basis.getValues(LoValues, ReferenceNodeLocations, Intrepid2::OPERATOR_VALUE);
505
506 Kokkos::fence(); // for kernel in getValues
507
508#if 0
509 printf("** LoValues[%d,%d] **\n",(int)numFieldsLo,(int)numFieldsHi);
510 for(size_t i=0; i<numFieldsLo; i++) {
511 for(size_t j=0; j<numFieldsHi; j++)
512 printf("%6.4e ",LoValues(i,j));
513 printf("\n");
514 }
515 printf("**************\n");fflush(stdout);
516#endif
517
518 representative_node_candidates.resize(numFieldsLo);
519 auto LoValues_host = Kokkos::create_mirror_view(LoValues);
520 Kokkos::deep_copy(LoValues_host, LoValues);
521 for (size_t i = 0; i < numFieldsLo; i++) {
522 // 1st pass: find the max value
523 typename Teuchos::ScalarTraits<SC>::magnitudeType vmax = Teuchos::ScalarTraits<typename Teuchos::ScalarTraits<SC>::magnitudeType>::zero();
524 for (size_t j = 0; j < numFieldsHi; j++)
525 vmax = std::max(vmax, Teuchos::ScalarTraits<SC>::magnitude(LoValues_host(i, j)));
526
527 // 2nd pass: Find all values w/i threshold of target
528 for (size_t j = 0; j < numFieldsHi; j++) {
529 if (Teuchos::ScalarTraits<SC>::magnitude(vmax - LoValues_host(i, j)) < threshold * vmax)
530 representative_node_candidates[i].push_back(j);
531 }
532 }
533
534 // Sanity check
535 for (size_t i = 0; i < numFieldsLo; i++)
536 if (!representative_node_candidates[i].size())
537 throw std::runtime_error("ERROR: GenerateRepresentativeBasisNodes: No candidates found!");
538}
539
540} // namespace MueLuIntrepid
541
542/*********************************************************************************************************/
543/*********************************************************************************************************/
544/*********************************************************************************************************/
545template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
547 const std::vector<bool> &hi_nodeIsOwned,
548 const SCFieldContainer &hi_DofCoords,
549 const std::vector<size_t> &lo_node_in_hi,
550 const Basis &lo_basis,
551 const std::vector<LocalOrdinal> &hi_to_lo_map,
552 const Teuchos::RCP<const Map> &lo_colMap,
553 const Teuchos::RCP<const Map> &lo_domainMap,
554 const Teuchos::RCP<const Map> &hi_map,
555 Teuchos::RCP<Matrix> &P) const {
556 typedef SCFieldContainer FC;
557 // Evaluate the linear basis functions at the Pn nodes
558 size_t numFieldsHi = hi_elemToNode.extent(1);
559 size_t numFieldsLo = lo_basis.getCardinality();
560 LocalOrdinal LOINVALID = Teuchos::OrdinalTraits<LocalOrdinal>::invalid();
561 FC LoValues_at_HiDofs("LoValues_at_HiDofs", numFieldsLo, numFieldsHi);
562 lo_basis.getValues(LoValues_at_HiDofs, hi_DofCoords, Intrepid2::OPERATOR_VALUE);
563 auto LoValues_at_HiDofs_host = Kokkos::create_mirror_view(LoValues_at_HiDofs);
564 Kokkos::deep_copy(LoValues_at_HiDofs_host, LoValues_at_HiDofs);
565 Kokkos::fence(); // for kernel in getValues
566
567 typedef typename Teuchos::ScalarTraits<SC>::halfPrecision SClo;
568 typedef typename Teuchos::ScalarTraits<SClo>::magnitudeType MT;
569 MT effective_zero = Teuchos::ScalarTraits<MT>::eps();
570
571 // Allocate P
572 P = rcp(new CrsMatrixWrap(hi_map, lo_colMap, numFieldsHi)); // FIXLATER: Need faster fill
573 RCP<CrsMatrix> Pcrs = toCrsMatrix(P);
574
575 // Slow-ish fill
576 size_t Nelem = hi_elemToNode.extent(0);
577 std::vector<bool> touched(hi_map->getLocalNumElements(), false);
578 Teuchos::Array<GO> col_gid(1);
579 Teuchos::Array<SC> val(1);
580 auto hi_elemToNode_host = Kokkos::create_mirror_view(hi_elemToNode);
581 Kokkos::deep_copy(hi_elemToNode_host, hi_elemToNode);
582 for (size_t i = 0; i < Nelem; i++) {
583 for (size_t j = 0; j < numFieldsHi; j++) {
584 LO row_lid = hi_elemToNode_host(i, j);
585 GO row_gid = hi_map->getGlobalElement(row_lid);
586 if (hi_nodeIsOwned[row_lid] && !touched[row_lid]) {
587 for (size_t k = 0; k < numFieldsLo; k++) {
588 // Get the local id in P1's column map
589 LO col_lid = hi_to_lo_map[hi_elemToNode_host(i, lo_node_in_hi[k])];
590 if (col_lid == LOINVALID) continue;
591
592 col_gid[0] = {lo_colMap->getGlobalElement(col_lid)};
593 val[0] = LoValues_at_HiDofs_host(k, j);
594
595 // Skip near-zeros
596 if (Teuchos::ScalarTraits<SC>::magnitude(val[0]) >= effective_zero)
597 P->insertGlobalValues(row_gid, col_gid(), val());
598 }
599 touched[row_lid] = true;
600 }
601 }
602 }
603 P->fillComplete(lo_domainMap, hi_map);
604}
605
606/*********************************************************************************************************/
607template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
609 const std::vector<bool> &hi_nodeIsOwned,
610 const SCFieldContainer &hi_DofCoords,
611 const LOFieldContainer &lo_elemToHiRepresentativeNode,
612 const Basis &lo_basis,
613 const std::vector<LocalOrdinal> &hi_to_lo_map,
614 const Teuchos::RCP<const Map> &lo_colMap,
615 const Teuchos::RCP<const Map> &lo_domainMap,
616 const Teuchos::RCP<const Map> &hi_map,
617 Teuchos::RCP<Matrix> &P) const {
618 typedef SCFieldContainer FC;
619 // Evaluate the linear basis functions at the Pn nodes
620 size_t numFieldsHi = hi_elemToNode.extent(1);
621 size_t numFieldsLo = lo_basis.getCardinality();
622 FC LoValues_at_HiDofs("LoValues_at_HiDofs", numFieldsLo, numFieldsHi);
623 lo_basis.getValues(LoValues_at_HiDofs, hi_DofCoords, Intrepid2::OPERATOR_VALUE);
624 auto LoValues_at_HiDofs_host = Kokkos::create_mirror_view(LoValues_at_HiDofs);
625 auto hi_elemToNode_host = Kokkos::create_mirror_view(hi_elemToNode);
626 auto lo_elemToHiRepresentativeNode_host = Kokkos::create_mirror_view(lo_elemToHiRepresentativeNode);
627 Kokkos::deep_copy(LoValues_at_HiDofs_host, LoValues_at_HiDofs);
628 Kokkos::deep_copy(hi_elemToNode_host, hi_elemToNode);
629 Kokkos::deep_copy(lo_elemToHiRepresentativeNode_host, lo_elemToHiRepresentativeNode);
630 Kokkos::fence(); // for kernel in getValues
631
632 typedef typename Teuchos::ScalarTraits<SC>::halfPrecision SClo;
633 typedef typename Teuchos::ScalarTraits<SClo>::magnitudeType MT;
634 MT effective_zero = Teuchos::ScalarTraits<MT>::eps();
635
636 // Allocate P
637 P = rcp(new CrsMatrixWrap(hi_map, lo_colMap, numFieldsHi)); // FIXLATER: Need faster fill
638 RCP<CrsMatrix> Pcrs = toCrsMatrix(P);
639
640 // Slow-ish fill
641 size_t Nelem = hi_elemToNode.extent(0);
642 std::vector<bool> touched(hi_map->getLocalNumElements(), false);
643 Teuchos::Array<GO> col_gid(1);
644 Teuchos::Array<SC> val(1);
645 for (size_t i = 0; i < Nelem; i++) {
646 for (size_t j = 0; j < numFieldsHi; j++) {
647 LO row_lid = hi_elemToNode_host(i, j);
648 GO row_gid = hi_map->getGlobalElement(row_lid);
649 if (hi_nodeIsOwned[row_lid] && !touched[row_lid]) {
650 for (size_t k = 0; k < numFieldsLo; k++) {
651 // Get the local id in P1's column map
652 LO col_lid = hi_to_lo_map[lo_elemToHiRepresentativeNode_host(i, k)];
653 col_gid[0] = {lo_colMap->getGlobalElement(col_lid)};
654 val[0] = LoValues_at_HiDofs_host(k, j);
655
656 // Skip near-zeros
657 if (Teuchos::ScalarTraits<SC>::magnitude(val[0]) >= effective_zero)
658 P->insertGlobalValues(row_gid, col_gid(), val());
659 }
660 touched[row_lid] = true;
661 }
662 }
663 }
664 P->fillComplete(lo_domainMap, hi_map);
665}
666
667/*********************************************************************************************************/
668template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
670 RCP<ParameterList> validParamList = rcp(new ParameterList());
671
672#define SET_VALID_ENTRY(name) validParamList->setEntry(name, MasterList::getEntry(name))
673 SET_VALID_ENTRY("pcoarsen: hi basis");
674 SET_VALID_ENTRY("pcoarsen: lo basis");
675#undef SET_VALID_ENTRY
676
677 validParamList->set<RCP<const FactoryBase>>("A", Teuchos::null, "Generating factory of the matrix A used during the prolongator smoothing process");
678
679 validParamList->set<RCP<const FactoryBase>>("Nullspace", Teuchos::null, "Generating factory of the nullspace");
680 validParamList->set<RCP<const FactoryBase>>("pcoarsen: element to node map", Teuchos::null, "Generating factory of the element to node map");
681 return validParamList;
682}
683
684/*********************************************************************************************************/
685template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
687 Input(fineLevel, "A");
688 Input(fineLevel, "pcoarsen: element to node map");
689 Input(fineLevel, "Nullspace");
690}
691
692/*********************************************************************************************************/
693template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
695 return BuildP(fineLevel, coarseLevel);
696}
697
698/*********************************************************************************************************/
699template <class Scalar, class LocalOrdinal, class GlobalOrdinal, class Node>
701 FactoryMonitor m(*this, "P Coarsening", coarseLevel);
702 std::string levelIDs = toString(coarseLevel.GetLevelID());
703 const std::string prefix = "MueLu::IntrepidPCoarsenFactory(" + levelIDs + "): ";
704
705 // NOTE: This is hardwired to double on purpose. See the note below.
706 typedef Kokkos::DynRankView<LocalOrdinal, typename Node::device_type> FCi;
707 typedef Kokkos::DynRankView<double, typename Node::device_type> FC;
708
709 // Level Get
710 RCP<Matrix> A = Get<RCP<Matrix>>(fineLevel, "A");
711 RCP<MultiVector> fineNullspace = Get<RCP<MultiVector>>(fineLevel, "Nullspace");
712 Xpetra::CrsMatrixWrap<Scalar, LocalOrdinal, GlobalOrdinal, Node> &Acrs = dynamic_cast<Xpetra::CrsMatrixWrap<Scalar, LocalOrdinal, GlobalOrdinal, Node> &>(*A);
713
714 if (restrictionMode_) {
715 SubFactoryMonitor m2(*this, "Transpose A", coarseLevel);
716 A = Utilities::Transpose(*A, true); // build transpose of A explicitly
717 }
718
719 // Find the Dirichlet rows in A
720 std::vector<LocalOrdinal> A_dirichletRows;
721 Utilities::FindDirichletRows(A, A_dirichletRows);
722
723 // Build final prolongator
724 RCP<Matrix> finalP;
725
726 // Reuse pattern if available
727 RCP<ParameterList> APparams = rcp(new ParameterList);
728 if (coarseLevel.IsAvailable("AP reuse data", this)) {
729 GetOStream(static_cast<MsgType>(Runtime0 | Test)) << "Reusing previous AP data" << std::endl;
730
731 APparams = coarseLevel.Get<RCP<ParameterList>>("AP reuse data", this);
732
733 if (APparams->isParameter("graph"))
734 finalP = APparams->get<RCP<Matrix>>("graph");
735 }
736 const ParameterList &pL = GetParameterList();
737
738 /*******************/
739 // FIXME LATER: Allow these to be manually specified instead of Intrepid
740 // Get the Intrepid bases
741 // NOTE: To make sure Stokhos works we only instantiate these guys with double. There's a lot
742 // of stuff in the guts of Intrepid2 that doesn't play well with Stokhos as of yet.
743 int lo_degree, hi_degree;
744 RCP<Basis> hi_basis = MueLuIntrepid::BasisFactory<double, typename Node::device_type::execution_space>(pL.get<std::string>("pcoarsen: hi basis"), hi_degree);
745 RCP<Basis> lo_basis = MueLuIntrepid::BasisFactory<double, typename Node::device_type::execution_space>(pL.get<std::string>("pcoarsen: lo basis"), lo_degree);
746
747 // Useful Output
748 GetOStream(Statistics1) << "P-Coarsening from basis " << pL.get<std::string>("pcoarsen: hi basis") << " to " << pL.get<std::string>("pcoarsen: lo basis") << std::endl;
749
750 /*******************/
751 // Get the higher-order element-to-node map
752 const Teuchos::RCP<FCi> Pn_elemToNode = Get<Teuchos::RCP<FCi>>(fineLevel, "pcoarsen: element to node map");
753
754 // Calculate node ownership (the quick and dirty way)
755 // NOTE: This exploits two things:
756 // 1) domainMap == rowMap
757 // 2) Standard [e|t]petra ordering (namely the local unknowns are always numbered first).
758 // This routine does not work in general.
759 RCP<const Map> rowMap = A->getRowMap();
760 RCP<const Map> colMap = Acrs.getColMap();
761 RCP<const Map> domainMap = A->getDomainMap();
762 int NumProc = rowMap->getComm()->getSize();
763 assert(rowMap->isSameAs(*domainMap));
764 std::vector<bool> Pn_nodeIsOwned(colMap->getLocalNumElements(), false);
765 LO num_owned_rows = 0;
766 for (size_t i = 0; i < rowMap->getLocalNumElements(); i++) {
767 if (rowMap->getGlobalElement(i) == colMap->getGlobalElement(i)) {
768 Pn_nodeIsOwned[i] = true;
769 num_owned_rows++;
770 }
771 }
772
773 // Used in all cases
774 FC hi_DofCoords;
775 Teuchos::RCP<FCi> P1_elemToNode = rcp(new FCi());
776
777 std::vector<bool> P1_nodeIsOwned;
778 int P1_numOwnedNodes;
779 std::vector<LO> hi_to_lo_map;
780
781 // Degree-1 variables
782 std::vector<size_t> lo_node_in_hi;
783
784 // Degree-n variables
785 FCi lo_elemToHiRepresentativeNode;
786
787 // Get Dirichlet unknown information
788 RCP<Xpetra::Vector<int, LocalOrdinal, GlobalOrdinal, Node>> hi_isDirichletRow, hi_isDirichletCol;
789 Utilities::FindDirichletRowsAndPropagateToCols(A, hi_isDirichletRow, hi_isDirichletCol);
790
791#if 0
792 printf("[%d] isDirichletRow = ",A->getRowMap()->getComm()->getRank());
793 for(size_t i=0;i<hi_isDirichletRow->getMap()->getLocalNumElements(); i++)
794 printf("%d ",hi_isDirichletRow->getData(0)[i]);
795 printf("\n");
796 printf("[%d] isDirichletCol = ",A->getRowMap()->getComm()->getRank());
797 for(size_t i=0;i<hi_isDirichletCol->getMap()->getLocalNumElements(); i++)
798 printf("%d ",hi_isDirichletCol->getData(0)[i]);
799 printf("\n");
800 fflush(stdout);
801#endif
802
803 /*******************/
804 if (lo_degree == 1) {
805 // Get reference coordinates and the lo-to-hi injection list for the reference element
806 MueLuIntrepid::IntrepidGetP1NodeInHi(hi_basis, lo_node_in_hi, hi_DofCoords);
807
808 // Generate lower-order element-to-node map
809 MueLuIntrepid::BuildLoElemToNode(*Pn_elemToNode, Pn_nodeIsOwned, lo_node_in_hi, hi_isDirichletCol->getData(0), *P1_elemToNode, P1_nodeIsOwned, hi_to_lo_map, P1_numOwnedNodes);
810 assert(hi_to_lo_map.size() == colMap->getLocalNumElements());
811 } else {
812 // Get lo-order candidates
813 double threshold = 1e-10;
814 std::vector<std::vector<size_t>> candidates;
815 Kokkos::resize(hi_DofCoords, hi_basis->getCardinality(), hi_basis->getBaseCellTopology().getDimension());
816 hi_basis->getDofCoords(hi_DofCoords);
817
818 MueLu::MueLuIntrepid::GenerateRepresentativeBasisNodes<Basis, FC>(*lo_basis, hi_DofCoords, threshold, candidates);
819
820 // Generate the representative nodes
821 MueLu::MueLuIntrepid::GenerateLoNodeInHiViaGIDs(candidates, *Pn_elemToNode, colMap, lo_elemToHiRepresentativeNode);
822 MueLu::MueLuIntrepid::BuildLoElemToNodeViaRepresentatives(*Pn_elemToNode, Pn_nodeIsOwned, lo_elemToHiRepresentativeNode, *P1_elemToNode, P1_nodeIsOwned, hi_to_lo_map, P1_numOwnedNodes);
823 }
824 MUELU_LEVEL_SET_IF_REQUESTED_OR_KEPT(coarseLevel, "pcoarsen: element to node map", P1_elemToNode);
825
826 /*******************/
827 // Generate the P1_domainMap
828 // HOW: Since we know how many each proc has, we can use the non-uniform contiguous map constructor to do the work for us
829 RCP<const Map> P1_domainMap = MapFactory::Build(rowMap->lib(), Teuchos::OrdinalTraits<Xpetra::global_size_t>::invalid(), P1_numOwnedNodes, rowMap->getIndexBase(), rowMap->getComm());
830 MUELU_LEVEL_SET_IF_REQUESTED_OR_KEPT(coarseLevel, "CoarseMap", P1_domainMap);
831
832 // Generate the P1_columnMap
833 RCP<const Map> P1_colMap;
834 if (NumProc == 1)
835 P1_colMap = P1_domainMap;
836 else
837 MueLuIntrepid::GenerateColMapFromImport<LO, GO, NO>(*Acrs.getCrsGraph()->getImporter(), hi_to_lo_map, *P1_domainMap, P1_nodeIsOwned.size(), P1_colMap);
838
839 /*******************/
840 // Generate the coarsening
841 if (lo_degree == 1)
842 GenerateLinearCoarsening_pn_kirby_to_p1(*Pn_elemToNode, Pn_nodeIsOwned, hi_DofCoords, lo_node_in_hi, *lo_basis, hi_to_lo_map, P1_colMap, P1_domainMap, A->getRowMap(), finalP);
843 else
844 GenerateLinearCoarsening_pn_kirby_to_pm(*Pn_elemToNode, Pn_nodeIsOwned, hi_DofCoords, lo_elemToHiRepresentativeNode, *lo_basis, hi_to_lo_map, P1_colMap, P1_domainMap, A->getRowMap(), finalP);
845
846 /*******************/
847 // Zero out the Dirichlet rows in P
848 Utilities::ZeroDirichletRows(finalP, A_dirichletRows);
849
850 /*******************/
851 // Build the nullspace
852 RCP<MultiVector> coarseNullspace = MultiVectorFactory::Build(P1_domainMap, fineNullspace->getNumVectors());
853 finalP->apply(*fineNullspace, *coarseNullspace, Teuchos::TRANS);
854 Set(coarseLevel, "Nullspace", coarseNullspace);
855
856 // Level Set
857 if (!restrictionMode_) {
858 // The factory is in prolongation mode
859 Set(coarseLevel, "P", finalP);
860
861 APparams->set("graph", finalP);
862 MUELU_LEVEL_SET_IF_REQUESTED_OR_KEPT(coarseLevel, "AP reuse data", APparams);
863
864 if (IsPrint(Statistics1)) {
865 RCP<ParameterList> params = rcp(new ParameterList());
866 params->set("printLoadBalancingInfo", true);
867 params->set("printCommInfo", true);
868 GetOStream(Statistics1) << PerfUtils::PrintMatrixInfo(*finalP, "P", params);
869 }
870 } else {
871 // The factory is in restriction mode
872 RCP<Matrix> R;
873 {
874 SubFactoryMonitor m2(*this, "Transpose P", coarseLevel);
875 R = Utilities::Transpose(*finalP, true);
876 }
877
878 Set(coarseLevel, "R", R);
879
880 if (IsPrint(Statistics2)) {
881 RCP<ParameterList> params = rcp(new ParameterList());
882 params->set("printLoadBalancingInfo", true);
883 params->set("printCommInfo", true);
884 GetOStream(Statistics2) << PerfUtils::PrintMatrixInfo(*R, "R", params);
885 }
886 }
887
888} // Build()
889
890} // namespace MueLu
891
892#endif // MUELU_IPCFACTORY_DEF_HPP
#define SET_VALID_ENTRY(name)
#define MUELU_LEVEL_SET_IF_REQUESTED_OR_KEPT(level, ename, entry)
MueLu::DefaultLocalOrdinal LocalOrdinal
MueLu::DefaultGlobalOrdinal GlobalOrdinal
MueLu::DefaultNode Node
Timer to be used in factories. Similar to Monitor but with additional timers.
RCP< const ParameterList > GetValidParameterList() const
Return a const parameter list of valid parameters that setParameterList() will accept.
Kokkos::DynRankView< LocalOrdinal, typename Node::device_type > LOFieldContainer
void GenerateLinearCoarsening_pn_kirby_to_p1(const LOFieldContainer &hi_elemToNode, const std::vector< bool > &hi_nodeIsOwned, const SCFieldContainer &hi_DofCoords, const std::vector< size_t > &lo_node_in_hi, const Basis &lo_Basis, const std::vector< LocalOrdinal > &hi_to_lo_map, const Teuchos::RCP< const Map > &lo_colMap, const Teuchos::RCP< const Map > &lo_domainMap, const Teuchos::RCP< const Map > &hi_map, Teuchos::RCP< Matrix > &P) const
void BuildP(Level &fineLevel, Level &coarseLevel) const
Abstract Build method.
Kokkos::DynRankView< double, typename Node::device_type > SCFieldContainer
Intrepid2::Basis< typename Node::device_type::execution_space, double, double > Basis
void Build(Level &fineLevel, Level &coarseLevel) const
Build method.
void DeclareInput(Level &fineLevel, Level &coarseLevel) const
Input.
void GenerateLinearCoarsening_pn_kirby_to_pm(const LOFieldContainer &hi_elemToNode, const std::vector< bool > &hi_nodeIsOwned, const SCFieldContainer &hi_DofCoords, const LOFieldContainer &lo_elemToHiRepresentativeNode, const Basis &lo_basis, const std::vector< LocalOrdinal > &hi_to_lo_map, const Teuchos::RCP< const Map > &lo_colMap, const Teuchos::RCP< const Map > &lo_domainMap, const Teuchos::RCP< const Map > &hi_map, Teuchos::RCP< Matrix > &P) 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.
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 std::string PrintMatrixInfo(const Matrix &A, const std::string &msgTag, RCP< const Teuchos::ParameterList > params=Teuchos::null)
Timer to be used in factories. Similar to SubMonitor but adds a timer level by level.
static void FindDirichletRowsAndPropagateToCols(Teuchos::RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > &A, Teuchos::RCP< Xpetra::Vector< int, LocalOrdinal, GlobalOrdinal, Node > > &isDirichletRow, Teuchos::RCP< Xpetra::Vector< int, LocalOrdinal, GlobalOrdinal, Node > > &isDirichletCol)
static void ZeroDirichletRows(Teuchos::RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > &A, const std::vector< LocalOrdinal > &dirichletRows, Scalar replaceWith=Teuchos::ScalarTraits< Scalar >::zero())
static void FindDirichletRows(Teuchos::RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > &A, std::vector< LocalOrdinal > &dirichletRows, bool count_twos_as_dirichlet=false)
static RCP< Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > > Transpose(Xpetra::Matrix< Scalar, LocalOrdinal, GlobalOrdinal, Node > &Op, bool optimizeTranspose=false, const std::string &label=std::string(), const Teuchos::RCP< Teuchos::ParameterList > &params=Teuchos::null)
void IntrepidGetP1NodeInHi(const Teuchos::RCP< Intrepid2::Basis< typename KokkosDeviceType::execution_space, Scalar, Scalar > > &hi_basis, std::vector< size_t > &lo_node_in_hi, Kokkos::DynRankView< Scalar, KokkosDeviceType > &hi_DofCoords)
void FindGeometricSeedOrdinals(Teuchos::RCP< Basis > basis, const LOFieldContainer &elementToNodeMap, std::vector< std::vector< LocalOrdinal > > &seeds, const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > &rowMap, const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > &columnMap)
void GenerateRepresentativeBasisNodes(const Basis &basis, const SCFieldContainer &ReferenceNodeLocations, const double threshold, std::vector< std::vector< size_t > > &representative_node_candidates)
void GenerateColMapFromImport(const Xpetra::Import< LocalOrdinal, GlobalOrdinal, Node > &hi_importer, const std::vector< LocalOrdinal > &hi_to_lo_map, const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > &lo_domainMap, const size_t &lo_columnMapLength, RCP< const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > > &lo_columnMap)
void BuildLoElemToNodeViaRepresentatives(const LOFieldContainer &hi_elemToNode, const std::vector< bool > &hi_nodeIsOwned, const LOFieldContainer &lo_elemToHiRepresentativeNode, LOFieldContainer &lo_elemToNode, std::vector< bool > &lo_nodeIsOwned, std::vector< LocalOrdinal > &hi_to_lo_map, int &lo_numOwnedNodes)
std::string tolower(const std::string &str)
void BuildLoElemToNode(const LOFieldContainer &hi_elemToNode, const std::vector< bool > &hi_nodeIsOwned, const std::vector< size_t > &lo_node_in_hi, const Teuchos::ArrayRCP< const int > &hi_isDirichlet, LOFieldContainer &lo_elemToNode, std::vector< bool > &lo_nodeIsOwned, std::vector< LocalOrdinal > &hi_to_lo_map, int &lo_numOwnedNodes)
void GenerateLoNodeInHiViaGIDs(const std::vector< std::vector< size_t > > &candidates, const LOFieldContainer &hi_elemToNode, RCP< const Xpetra::Map< LocalOrdinal, GlobalOrdinal, Node > > &hi_columnMap, LOFieldContainer &lo_elemToHiRepresentativeNode)
Teuchos::RCP< Intrepid2::Basis< KokkosExecutionSpace, Scalar, Scalar > > BasisFactory(const std::string &name, int &degree)
Namespace for MueLu classes and methods.
@ Statistics2
Print even more statistics.
@ Statistics1
Print more statistics.
@ Runtime0
One-liner description of what is happening.
std::string toString(const T &what)
Little helper function to convert non-string types to strings.