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