Panzer Version of the Day
Loading...
Searching...
No Matches
Panzer_BlockedTpetraLinearObjFactory_impl.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// Panzer: A partial differential equation assembly
4// engine for strongly coupled complex multiphysics systems
5//
6// Copyright 2011 NTESS and the Panzer contributors.
7// SPDX-License-Identifier: BSD-3-Clause
8// *****************************************************************************
9// @HEADER
10
11#ifndef __Panzer_BlockedTpetraLinearObjFactory_impl_hpp__
12#define __Panzer_BlockedTpetraLinearObjFactory_impl_hpp__
13
14// Panzer
16#include <utility>
17#ifdef PANZER_HAVE_EPETRA_STACK
18#include "Panzer_EpetraVector_Write_GlobalEvaluationData.hpp" // JMG: Remove this eventually.
19#endif
22
23#include "KokkosSparse_SortCrs.hpp"
24
25// Thyra
26#include "Thyra_DefaultBlockedLinearOp.hpp"
27#include "Thyra_DefaultProductVector.hpp"
28#include "Thyra_DefaultProductVectorSpace.hpp"
29#include "Thyra_SpmdVectorBase.hpp"
30#include "Thyra_TpetraLinearOp.hpp"
31#include "Thyra_TpetraThyraWrappers.hpp"
32
33// Tpetra
34#include "Tpetra_CrsMatrix.hpp"
35#include "Tpetra_MultiVector.hpp"
36#include "Tpetra_Vector.hpp"
37
38namespace panzer {
39
40using Teuchos::RCP;
41
42// ************************************************************
43// class BlockedTpetraLinearObjFactory
44// ************************************************************
45
46template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
48BlockedTpetraLinearObjFactory(const Teuchos::RCP<const Teuchos::MpiComm<int> > & comm,
49 const Teuchos::RCP<const BlockedDOFManager> & gidProvider,
50 bool useFEAssembly)
51 : blockProvider_(gidProvider), blockedDOFManager_(gidProvider), hasColProvider_(false), comm_(comm)
52 , useFEAssembly_(useFEAssembly)
53{
54 for(std::size_t i=0;i<gidProvider->getFieldDOFManagers().size();i++)
55 gidProviders_.push_back(gidProvider->getFieldDOFManagers()[i]);
56
58
59 // build and register the gather/scatter evaluators with
60 // the base class.
62}
63
64template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
66BlockedTpetraLinearObjFactory(const Teuchos::RCP<const Teuchos::MpiComm<int> > & comm,
67 const std::vector<Teuchos::RCP<const panzer::GlobalIndexer>> & gidProviders,
68 bool useFEAssembly)
69 : gidProviders_(gidProviders), hasColProvider_(false), comm_(comm)
70 , useFEAssembly_(useFEAssembly)
71{
73}
74
75template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
77BlockedTpetraLinearObjFactory(const Teuchos::RCP<const Teuchos::MpiComm<int> > & comm,
78 const Teuchos::RCP<const GlobalIndexer> & rowProvider,
79 const Teuchos::RCP<const GlobalIndexer> & colProvider,
80 bool useFEAssembly)
81 : blockProvider_(rowProvider), hasColProvider_(true), colBlockProvider_(colProvider), comm_(comm)
82 , useFEAssembly_(useFEAssembly)
83{
84 // FE assembly fuses the owned and ghosted matrix into one Tpetra::FECrsMatrix
85 // built from an FECrsGraph, and buildFEGraph() has no column-side counterpart
86 // to the row objects it uses. Rather than assemble into a graph whose columns
87 // are wrong, refuse the combination.
88 TEUCHOS_TEST_FOR_EXCEPTION(useFEAssembly,std::logic_error,
89 "BlockedTpetraLinearObjFactory: FE assembly is not supported for a non-square "
90 "factory (one built with a separate column provider).");
91
94
96
97 // build and register the gather/scatter evaluators with
98 // the base class.
100}
101
102template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
104splitIntoBlocks(const Teuchos::RCP<const GlobalIndexer> & ugi,
105 Teuchos::RCP<const BlockedDOFManager> & blocked,
106 std::vector<Teuchos::RCP<const GlobalIndexer> > & blocks)
107{
108 blocked = Teuchos::rcp_dynamic_cast<const BlockedDOFManager>(ugi);
109
110 blocks.clear();
111 if(blocked!=Teuchos::null) {
112 const auto & dofManagers = blocked->getFieldDOFManagers();
113 for(std::size_t i=0;i<dofManagers.size();i++)
114 blocks.push_back(dofManagers[i]);
115 }
116 else {
117 TEUCHOS_TEST_FOR_EXCEPTION(ugi==Teuchos::null,std::logic_error,
118 "BlockedTpetraLinearObjFactory: a null global indexer was supplied.");
119 blocks.push_back(ugi);
120 }
121}
122
123template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
127
128// LinearObjectFactory functions
130
131template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
132Teuchos::RCP<LinearObjContainer>
135{
136 std::vector<Teuchos::RCP<const MapType> > blockMaps;
137 std::size_t blockDim = gidProviders_.size();
138 for(std::size_t i=0;i<blockDim;i++)
139 blockMaps.push_back(getMap(i));
140
141 Teuchos::RCP<BTLOC> container = Teuchos::rcp(new BTLOC);
142 container->setMapsForBlocks(blockMaps);
143
144 return container;
145}
146
147template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
148Teuchos::RCP<LinearObjContainer>
151{
152 std::vector<Teuchos::RCP<const MapType> > blockMaps;
153 std::size_t blockDim = gidProviders_.size();
154 for(std::size_t i=0;i<blockDim;i++)
155 blockMaps.push_back(getGhostedMap(i));
156
157 Teuchos::RCP<BTLOC> container = Teuchos::rcp(new BTLOC);
158 container->setMapsForBlocks(blockMaps);
159
160 return container;
161}
162
163template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
166{
167 using Teuchos::is_null;
168
169 typedef LinearObjContainer LOC;
170
171 const BTLOC & b_in = Teuchos::dyn_cast<const BTLOC>(in);
172 BTLOC & b_out = Teuchos::dyn_cast<BTLOC>(out);
173
174 // Operations occur if the GLOBAL container has the correct targets!
175 // Users set the GLOBAL continer arguments
176 if ( !is_null(b_in.get_x()) && !is_null(b_out.get_x()) && ((mem & LOC::X)==LOC::X))
177 globalToGhostThyraVector(b_in.get_x(),b_out.get_x(),true);
178
179 if ( !is_null(b_in.get_dxdt()) && !is_null(b_out.get_dxdt()) && ((mem & LOC::DxDt)==LOC::DxDt))
180 globalToGhostThyraVector(b_in.get_dxdt(),b_out.get_dxdt(),true);
181
182 if ( !is_null(b_in.get_f()) && !is_null(b_out.get_f()) && ((mem & LOC::F)==LOC::F))
183 globalToGhostThyraVector(b_in.get_f(),b_out.get_f(),false);
184}
185
186template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
189{
190 using Teuchos::is_null;
191
192 typedef LinearObjContainer LOC;
193
194 const BTLOC & b_in = Teuchos::dyn_cast<const BTLOC>(in);
195 BTLOC & b_out = Teuchos::dyn_cast<BTLOC>(out);
196
197 // Operations occur if the GLOBAL container has the correct targets!
198 // Users set the GLOBAL continer arguments
199 if ( !is_null(b_in.get_x()) && !is_null(b_out.get_x()) && ((mem & LOC::X)==LOC::X))
200 ghostToGlobalThyraVector(b_in.get_x(),b_out.get_x(),true);
201
202 if ( !is_null(b_in.get_f()) && !is_null(b_out.get_f()) && ((mem & LOC::F)==LOC::F))
203 ghostToGlobalThyraVector(b_in.get_f(),b_out.get_f(),false);
204
205 if ( !is_null(b_in.get_A()) && !is_null(b_out.get_A()) && ((mem & LOC::Mat)==LOC::Mat))
206 ghostToGlobalThyraMatrix(*b_in.get_A(),*b_out.get_A());
207}
208
209template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
212 const LinearObjContainer & globalBCRows,
213 LinearObjContainer & ghostedObjs,
214 bool zeroVectorRows, bool adjustX) const
215{
216 using Teuchos::RCP;
217 using Teuchos::rcp_dynamic_cast;
219 using Thyra::PhysicallyBlockedLinearOpBase;
220 using Thyra::VectorBase;
222
223 // The Dirichlet rows come from the range; only the operator's columns follow
224 // the domain, so the two loops below can have different extents.
225 std::size_t blockDim = getBlockRowCount();
226 std::size_t colBlockDim = getBlockColCount();
227
228 // first cast to block LOCs
229 const BTLOC & b_localBCRows = Teuchos::dyn_cast<const BTLOC>(localBCRows);
230 const BTLOC & b_globalBCRows = Teuchos::dyn_cast<const BTLOC>(globalBCRows);
231 BTLOC & b_ghosted = Teuchos::dyn_cast<BTLOC>(ghostedObjs);
232
233 TEUCHOS_ASSERT(b_localBCRows.get_f()!=Teuchos::null);
234 TEUCHOS_ASSERT(b_globalBCRows.get_f()!=Teuchos::null);
235
236 // cast each component as needed to their product form
237 RCP<PhysicallyBlockedLinearOpBase<ScalarT> > A = rcp_dynamic_cast<PhysicallyBlockedLinearOpBase<ScalarT> >(b_ghosted.get_A());
238 RCP<ProductVectorBase<ScalarT> > f = rcp_dynamic_cast<ProductVectorBase<ScalarT> >(b_ghosted.get_f());
239 RCP<ProductVectorBase<ScalarT> > local_bcs = rcp_dynamic_cast<ProductVectorBase<ScalarT> >(b_localBCRows.get_f(),true);
240 RCP<ProductVectorBase<ScalarT> > global_bcs = rcp_dynamic_cast<ProductVectorBase<ScalarT> >(b_globalBCRows.get_f(),true);
241
242 if(adjustX) f = rcp_dynamic_cast<ProductVectorBase<ScalarT> >(b_ghosted.get_x());
243
244 // sanity check!
245 if(A!=Teuchos::null) TEUCHOS_ASSERT(A->productRange()->numBlocks()==(int) blockDim);
246 if(A!=Teuchos::null) TEUCHOS_ASSERT(A->productDomain()->numBlocks()==(int) colBlockDim);
247 if(f!=Teuchos::null) TEUCHOS_ASSERT(f->productSpace()->numBlocks()==(int) blockDim);
248 TEUCHOS_ASSERT(local_bcs->productSpace()->numBlocks()==(int) blockDim);
249 TEUCHOS_ASSERT(global_bcs->productSpace()->numBlocks()==(int) blockDim);
250
251 for(std::size_t i=0;i<blockDim;i++) {
252 // grab epetra vector
253 RCP<const VectorType> t_local_bcs = rcp_dynamic_cast<const ThyraVector>(local_bcs->getVectorBlock(i),true)->getConstTpetraVector();
254 RCP<const VectorType> t_global_bcs = rcp_dynamic_cast<const ThyraVector>(global_bcs->getVectorBlock(i),true)->getConstTpetraVector();
255
256 // pull out epetra values
257 RCP<VectorBase<ScalarT> > th_f = (f==Teuchos::null) ? Teuchos::null : f->getNonconstVectorBlock(i);
258 RCP<VectorType> t_f;
259 if(th_f==Teuchos::null)
260 t_f = Teuchos::null;
261 else
262 t_f = rcp_dynamic_cast<ThyraVector>(th_f,true)->getTpetraVector();
263
264 for(std::size_t j=0;j<colBlockDim;j++) {
265 RCP<const MapType> map_i = getGhostedMap(i);
266 RCP<const MapType> map_j = getGhostedColMap(j);
267
268 // pull out epetra values
269 RCP<LinearOpBase<ScalarT> > th_A = (A== Teuchos::null)? Teuchos::null : A->getNonconstBlock(i,j);
270
271 // don't do anyting if opertor is null
272 RCP<CrsMatrixType> t_A;
273 if(th_A==Teuchos::null)
274 t_A = Teuchos::null;
275 else {
276 RCP<OperatorType> t_A_op = rcp_dynamic_cast<ThyraLinearOp>(th_A,true)->getTpetraOperator();
277 t_A = rcp_dynamic_cast<CrsMatrixType>(t_A_op,true);
278 }
279
280 // adjust Block operator
281 if(t_A!=Teuchos::null) {
282 t_A->resumeFill();
283 }
284
285 adjustForDirichletConditions(*t_local_bcs,*t_global_bcs,t_f.ptr(),t_A.ptr(),zeroVectorRows);
286
287 if(t_A!=Teuchos::null) {
288 //t_A->fillComplete(map_j,map_i);
289 }
290
291 t_f = Teuchos::null; // this is so we only adjust it once on the first pass
292 }
293 }
294}
295
296template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
299 const VectorType & global_bcs,
300 const Teuchos::Ptr<VectorType> & f,
301 const Teuchos::Ptr<CrsMatrixType> & A,
302 bool zeroVectorRows) const
303{
304 if(f==Teuchos::null && A==Teuchos::null)
305 return;
306
307 Teuchos::ArrayRCP<ScalarT> f_array = f!=Teuchos::null ? f->get1dViewNonConst() : Teuchos::null;
308
309 Teuchos::ArrayRCP<const ScalarT> local_bcs_array = local_bcs.get1dView();
310 Teuchos::ArrayRCP<const ScalarT> global_bcs_array = global_bcs.get1dView();
311
312 TEUCHOS_ASSERT(local_bcs.getLocalLength()==global_bcs.getLocalLength());
313 for(std::size_t i=0;i<local_bcs.getLocalLength();i++) {
314 if(global_bcs_array[i]==0.0)
315 continue;
316
317 if(local_bcs_array[i]==0.0 || zeroVectorRows) {
318 // this boundary condition was NOT set by this processor
319
320 // if they exist put 0.0 in each entry
321 if(!Teuchos::is_null(f))
322 f_array[i] = 0.0;
323 if(!Teuchos::is_null(A)) {
324 std::size_t numEntries = 0;
325 std::size_t sz = A->getNumEntriesInLocalRow(i);
326 typename CrsMatrixType::nonconst_local_inds_host_view_type indices("indices", sz);
327 typename CrsMatrixType::nonconst_values_host_view_type values("values", sz);
328
329 A->getLocalRowCopy(i,indices,values,numEntries);
330
331 for(std::size_t c=0;c<numEntries;c++)
332 values(c) = 0.0;
333
334 A->replaceLocalValues(i,indices,values);
335 }
336 }
337 else {
338 // this boundary condition was set by this processor
339
340 ScalarT scaleFactor = global_bcs_array[i];
341
342 // if they exist scale linear objects by scale factor
343 if(!Teuchos::is_null(f))
344 f_array[i] /= scaleFactor;
345 if(!Teuchos::is_null(A)) {
346 std::size_t numEntries = 0;
347 std::size_t sz = A->getNumEntriesInLocalRow(i);
348 typename CrsMatrixType::nonconst_local_inds_host_view_type indices("indices", sz);
349 typename CrsMatrixType::nonconst_values_host_view_type values("values", sz);
350
351 A->getLocalRowCopy(i,indices,values,numEntries);
352
353 for(std::size_t c=0;c<numEntries;c++)
354 values(c) /= scaleFactor;
355
356 A->replaceLocalValues(i,indices,values);
357 }
358 }
359 }
360}
361
362template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
364applyDirichletBCs(const LinearObjContainer & /* counter */,
365 LinearObjContainer & /* result */) const
366{
367 TEUCHOS_ASSERT(false); // not yet implemented
368}
369
371//
372// buildReadOnlyDomainContainer()
373//
375template<typename Traits, typename ScalarT, typename LocalOrdinalT,
376 typename GlobalOrdinalT, typename NodeT>
377Teuchos::RCP<ReadOnlyVector_GlobalEvaluationData>
378BlockedTpetraLinearObjFactory<Traits, ScalarT, LocalOrdinalT, GlobalOrdinalT,
379 NodeT>::
380buildReadOnlyDomainContainer() const
381{
382 using std::vector;
383 using Teuchos::RCP;
384 using Teuchos::rcp;
387 LocalOrdinalT, GlobalOrdinalT, NodeT>;
388 // A flat column side is a single vector, not a product vector, and
389 // buildGatherDomain() pairs it with the non-blocked gather. That gather reads
390 // a TpetraVector_ReadOnly_GlobalEvaluationData, so the two must agree here.
391 if (hasColProvider_ and colBlockedDOFManager_.is_null())
392 {
393 auto tvroged = rcp(new TVROGED);
394 tvroged->initialize(getGhostedColImport(0), getGhostedColMap(0), getColMap(0));
395 return tvroged;
396 }
397
398 vector<RCP<ReadOnlyVector_GlobalEvaluationData>> gedBlocks;
399 for (int i(0); i < getBlockColCount(); ++i)
400 {
401 auto tvroged = rcp(new TVROGED);
402 tvroged->initialize(getGhostedColImport(i), getGhostedColMap(i), getColMap(i));
403 gedBlocks.push_back(tvroged);
404 }
405 auto ged = rcp(new BVROGED);
406 ged->initialize(getGhostedThyraDomainSpace(), getThyraDomainSpace(),
407 gedBlocks);
408 return ged;
409} // end of buildReadOnlyDomainContainer()
410
411#ifdef PANZER_HAVE_EPETRA_STACK
413//
414// buildWriteDomainContainer()
415//
417template<typename Traits, typename ScalarT, typename LocalOrdinalT,
418 typename GlobalOrdinalT, typename NodeT>
419Teuchos::RCP<WriteVector_GlobalEvaluationData>
420BlockedTpetraLinearObjFactory<Traits, ScalarT, LocalOrdinalT, GlobalOrdinalT,
421 NodeT>::
422buildWriteDomainContainer() const
423{
424 using std::logic_error;
425 using Teuchos::rcp;
427 auto ged = rcp(new EVWGED);
428 TEUCHOS_TEST_FOR_EXCEPTION(true, logic_error, "NOT YET IMPLEMENTED")
429 return ged;
430} // end of buildWriteDomainContainer()
431#endif // PANZER_HAVE_EPETRA_STACK
432
433template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
439
440template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
442initializeContainer(int mem,LinearObjContainer & loc) const
443{
444 BTLOC & bloc = Teuchos::dyn_cast<BTLOC>(loc);
445 initializeContainer(mem,bloc);
446}
447
448template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
451{
452 BTLOC & bloc = Teuchos::dyn_cast<BTLOC>(loc);
453 initializeGhostedContainer(mem,bloc);
454}
455
456// Generic methods
458
459template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
461initializeContainer(int mem,BTLOC & loc) const
462{
463 typedef LinearObjContainer LOC;
464
465 loc.clear();
466
467 if((mem & LOC::X) == LOC::X)
468 loc.set_x(getThyraDomainVector());
469
470 if((mem & LOC::DxDt) == LOC::DxDt)
471 loc.set_dxdt(getThyraDomainVector());
472
473 if((mem & LOC::F) == LOC::F)
474 loc.set_f(getThyraRangeVector());
475
476 if((mem & LOC::Mat) == LOC::Mat)
477 loc.set_A(getThyraMatrix());
478}
479
480template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
482initializeGhostedContainer(int mem,BTLOC & loc) const
483{
484 typedef LinearObjContainer LOC;
485
486 loc.clear();
487
488 if((mem & LOC::X) == LOC::X)
489 loc.set_x(getGhostedThyraDomainVector());
490
491 if((mem & LOC::DxDt) == LOC::DxDt)
492 loc.set_dxdt(getGhostedThyraDomainVector());
493
494 if((mem & LOC::F) == LOC::F) {
495 loc.set_f(getGhostedThyraRangeVector());
497 }
498
499 if((mem & LOC::Mat) == LOC::Mat) {
500 // Under FE assembly the ghosted container owns no operator; beginFill(ghosted,owned)
501 // points it at the owned container's for the duration of the assembly.
502 if(!useFEAssembly_)
503 loc.set_A(getGhostedThyraMatrix());
505 }
506}
507
508template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
510addExcludedPair(int rowBlock,int colBlock)
511{
512 excludedPairs_.insert(std::make_pair(rowBlock,colBlock));
513}
514
515template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
517addExcludedPairs(const std::vector<std::pair<int,int> > & exPairs)
518{
519 for(std::size_t i=0;i<exPairs.size();i++)
520 excludedPairs_.insert(exPairs[i]);
521}
522
523template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
524Teuchos::RCP<const GlobalIndexer>
530
531template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
532Teuchos::RCP<const GlobalIndexer>
534getColGlobalIndexer(int i) const
535{
536 return hasColProvider_ ? colGidProviders_[i] : gidProviders_[i];
537}
538
539template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
541makeRoomForBlocks(std::size_t blockCnt,std::size_t colBlockCnt)
542{
543 maps_.resize(blockCnt);
544 ghostedMaps_.resize(blockCnt);
545 importers_.resize(blockCnt);
546 exporters_.resize(blockCnt);
547
548 if(colBlockCnt>0) {
549 colMaps_.resize(colBlockCnt);
550 ghostedColMaps_.resize(colBlockCnt);
551 colImporters_.resize(colBlockCnt);
552 colExporters_.resize(colBlockCnt);
553 }
554}
555
556// Thyra methods
558
559template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
560Teuchos::RCP<const Thyra::VectorSpaceBase<ScalarT> >
563{
564 if(domainSpace_==Teuchos::null) {
565 if(hasColProvider_ and colBlockedDOFManager_.is_null()) {
566 // a flat column provider is a single SPMD space, not a product space
567 domainSpace_ = Thyra::createVectorSpace<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT>(getColMap(0));
568 }
569 else {
570 // loop over all vectors and build the vector space
571 std::vector<Teuchos::RCP<const Thyra::VectorSpaceBase<ScalarT> > > vsArray;
572 for(int i=0;i<getBlockColCount();i++)
573 vsArray.push_back(Thyra::createVectorSpace<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT>(getColMap(i)));
574
575 domainSpace_ = Thyra::productVectorSpace<ScalarT>(vsArray);
576 }
577 }
578
579 return domainSpace_;
580}
581
582template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
583Teuchos::RCP<const Thyra::VectorSpaceBase<ScalarT> >
585getThyraRangeSpace() const
586{
587 if(rangeSpace_==Teuchos::null) {
588 // loop over all vectors and build the vector space
589 std::vector<Teuchos::RCP<const Thyra::VectorSpaceBase<ScalarT> > > vsArray;
590 for(std::size_t i=0;i<gidProviders_.size();i++)
591 vsArray.push_back(Thyra::createVectorSpace<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT>(getMap(i)));
592
593 rangeSpace_ = Thyra::productVectorSpace<ScalarT>(vsArray);
594 }
595
596 return rangeSpace_;
597}
598
599template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
600Teuchos::RCP<const Thyra::VectorSpaceBase<ScalarT> >
602getThyraDomainSpace(int blk) const
603{
604 if(domainSpace_==Teuchos::null) {
605 getThyraDomainSpace();
606 }
607
608 auto prod_space = Teuchos::rcp_dynamic_cast<const Thyra::ProductVectorSpaceBase<ScalarT> >(domainSpace_);
609 if(prod_space==Teuchos::null) {
610 // not a product space: there is exactly one block, and it is the space itself
611 TEUCHOS_ASSERT(blk==0);
612 return domainSpace_;
613 }
614
615 return prod_space->getBlock(blk);
616}
617
618template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
619Teuchos::RCP<const Thyra::VectorSpaceBase<ScalarT> >
621getThyraRangeSpace(int blk) const
622{
623 if(rangeSpace_==Teuchos::null) {
624 getThyraRangeSpace();
625 }
626
627 return rangeSpace_->getBlock(blk);
628}
629
630template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
631Teuchos::RCP<Thyra::VectorBase<ScalarT> >
634{
635 Teuchos::RCP<Thyra::VectorBase<ScalarT> > vec =
636 Thyra::createMember<ScalarT>(*getThyraDomainSpace());
637 Thyra::assign(vec.ptr(),0.0);
638
639 // Check the block sizes against the column maps by unwrapping to Tpetra
640 // rather than going through Thyra's SPMD interface. A flat column provider
641 // gives a single vector, not a product vector.
642 using ThyraTpetraVector = Thyra::TpetraVector<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT>;
643 Teuchos::RCP<Thyra::ProductVectorBase<ScalarT> > p_vec = Teuchos::rcp_dynamic_cast<Thyra::ProductVectorBase<ScalarT> >(vec);
644 if(p_vec==Teuchos::null) {
645 TEUCHOS_ASSERT(getBlockColCount()==1);
646 auto tp_vec = Teuchos::rcp_dynamic_cast<ThyraTpetraVector>(vec,true)->getTpetraVector();
647 TEUCHOS_ASSERT(tp_vec->getLocalLength()==getColMap(0)->getLocalNumElements());
648 }
649 else {
650 for(int i=0;i<getBlockColCount();i++) {
651 auto tp_blk = Teuchos::rcp_dynamic_cast<ThyraTpetraVector>(p_vec->getNonconstVectorBlock(i),true)->getTpetraVector();
652 TEUCHOS_ASSERT(tp_blk->getLocalLength()==getColMap(i)->getLocalNumElements());
653 }
654 }
655
656 return vec;
657}
658
659template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
660Teuchos::RCP<Thyra::VectorBase<ScalarT> >
663{
664 Teuchos::RCP<Thyra::VectorBase<ScalarT> > vec =
665 Thyra::createMember<ScalarT>(*getThyraRangeSpace());
666 Thyra::assign(vec.ptr(),0.0);
667
668 return vec;
669}
670
671template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
672Teuchos::RCP<Thyra::LinearOpBase<ScalarT> >
674getThyraMatrix() const
675{
676 Teuchos::RCP<Thyra::PhysicallyBlockedLinearOpBase<ScalarT> > blockedOp = Thyra::defaultBlockedLinearOp<ScalarT>();
677
678 // get the block dimensions
679 std::size_t rowBlockDim = getBlockRowCount();
680 std::size_t colBlockDim = getBlockColCount();
681
682 blockedOp->beginBlockFill(rowBlockDim,colBlockDim);
683
684 // loop over each block
685 for(std::size_t i=0;i<rowBlockDim;i++) {
686 for(std::size_t j=0;j<colBlockDim;j++) {
687 if(excludedPairs_.find(std::make_pair(i,j))==excludedPairs_.end()) {
688 // build (i,j) block matrix and add it to blocked operator
689 //
690 // In FE mode the spaces must be stated explicitly rather than deduced from the
691 // matrix. A shared FECrsMatrix reports whichever maps its ACTIVE view has, and
692 // that view flips between owned+shared and owned across the assembly cycle --
693 // a freshly constructed one even starts in owned+shared. Deducing the spaces
694 // would stamp this owned operator with ghosted spaces. The classic path is
695 // unaffected, since its matrix only ever has the owned maps.
696 Teuchos::RCP<Thyra::LinearOpBase<ScalarT> > block;
697 if(useFEAssembly_)
698 block = Thyra::tpetraLinearOp<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT>(
699 getThyraRangeSpace(i),getThyraDomainSpace(j),getTpetraMatrix(i,j));
700 else
701 block = Thyra::createLinearOp<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT>(getTpetraMatrix(i,j));
702 blockedOp->setNonconstBlock(i,j,block);
703 }
704 }
705 }
706
707 // all done
708 blockedOp->endBlockFill();
709
710 return blockedOp;
711}
712
713template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
714Teuchos::RCP<const Thyra::VectorSpaceBase<ScalarT> >
717{
718 if(ghostedDomainSpace_==Teuchos::null) {
719 if(hasColProvider_ and colBlockedDOFManager_.is_null()) {
720 // a flat column provider is a single SPMD space, not a product space
721 ghostedDomainSpace_ = Thyra::createVectorSpace<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT>(getGhostedColMap(0));
722 }
723 else {
724 // loop over all vectors and build the vector space
725 std::vector<Teuchos::RCP<const Thyra::VectorSpaceBase<ScalarT> > > vsArray;
726 for(int i=0;i<getBlockColCount();i++)
727 vsArray.push_back(Thyra::createVectorSpace<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT>(getGhostedColMap(i)));
728
729 ghostedDomainSpace_ = Thyra::productVectorSpace<ScalarT>(vsArray);
730 }
731 }
732
733 return ghostedDomainSpace_;
734}
735
736template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
737Teuchos::RCP<const Thyra::VectorSpaceBase<ScalarT> >
740{
741 if(ghostedRangeSpace_==Teuchos::null) {
742 // loop over all vectors and build the vector space
743 std::vector<Teuchos::RCP<const Thyra::VectorSpaceBase<ScalarT> > > vsArray;
744 for(std::size_t i=0;i<gidProviders_.size();i++)
745 vsArray.push_back(Thyra::createVectorSpace<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT>(getGhostedMap(i)));
746
747 ghostedRangeSpace_ = Thyra::productVectorSpace<ScalarT>(vsArray);
748 }
749
750 return ghostedRangeSpace_;
751}
752
753template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
754Teuchos::RCP<Thyra::VectorBase<ScalarT> >
757{
758 Teuchos::RCP<Thyra::VectorBase<ScalarT> > vec =
759 Thyra::createMember<ScalarT>(*getGhostedThyraDomainSpace());
760 Thyra::assign(vec.ptr(),0.0);
761
762 return vec;
763}
764
765template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
766Teuchos::RCP<Thyra::VectorBase<ScalarT> >
769{
770 Teuchos::RCP<Thyra::VectorBase<ScalarT> > vec =
771 Thyra::createMember<ScalarT>(*getGhostedThyraRangeSpace());
772 Thyra::assign(vec.ptr(),0.0);
773
774 return vec;
775}
776
777template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
778Teuchos::RCP<Thyra::BlockedLinearOpBase<ScalarT> >
781{
782 // No ghosted operator exists under FE assembly -- see getGhostedTpetraMatrix(i,j).
783 TEUCHOS_TEST_FOR_EXCEPTION(useFEAssembly_,std::logic_error,
784 "BlockedTpetraLinearObjFactory::getGhostedThyraMatrix: not available under FE "
785 "assembly. The ghosted container shares the owned container's operator, which is "
786 "connected by beginFill(ghosted,owned); use getThyraMatrix() to allocate one.");
787
788 Teuchos::RCP<Thyra::PhysicallyBlockedLinearOpBase<ScalarT> > blockedOp = Thyra::defaultBlockedLinearOp<ScalarT>();
789
790 // get the block dimensions
791 std::size_t rowBlockDim = getBlockRowCount();
792 std::size_t colBlockDim = getBlockColCount();
793
794 blockedOp->beginBlockFill(rowBlockDim,colBlockDim);
795
796 // loop over each block
797 for(std::size_t i=0;i<rowBlockDim;i++) {
798 for(std::size_t j=0;j<colBlockDim;j++) {
799 if(excludedPairs_.find(std::make_pair(i,j))==excludedPairs_.end()) {
800 // build (i,j) block matrix and add it to blocked operator
801 Teuchos::RCP<Thyra::LinearOpBase<ScalarT> > block
802 = Thyra::createLinearOp<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT>(getGhostedTpetraMatrix(i,j));
803 blockedOp->setNonconstBlock(i,j,block);
804 }
805 }
806 }
807
808 // all done
809 blockedOp->endBlockFill();
810
811 return blockedOp;
812}
813
814template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
816ghostToGlobalThyraVector(const Teuchos::RCP<const Thyra::VectorBase<ScalarT> > & in,
817 const Teuchos::RCP<Thyra::VectorBase<ScalarT> > & out,bool col) const
818{
819 using Teuchos::RCP;
820 using Teuchos::rcp_dynamic_cast;
822
823 std::size_t blockDim = col ? getBlockColCount() : getBlockRowCount();
824
825 // get product vectors, viewing a flat vector as a single block
826 RCP<const ProductVectorBase<ScalarT> > prod_in = Thyra::castOrCreateProductVectorBase(in);
827 RCP<ProductVectorBase<ScalarT> > prod_out = Thyra::castOrCreateNonconstProductVectorBase(out);
828
829 TEUCHOS_ASSERT(prod_in->productSpace()->numBlocks()==(int) blockDim);
830 TEUCHOS_ASSERT(prod_out->productSpace()->numBlocks()==(int) blockDim);
831
832 for(std::size_t i=0;i<blockDim;i++) {
833 // first get each Tpetra vector
834 RCP<const VectorType> tp_in = rcp_dynamic_cast<const ThyraVector>(prod_in->getVectorBlock(i),true)->getConstTpetraVector();
835 RCP<VectorType> tp_out = rcp_dynamic_cast<ThyraVector>(prod_out->getNonconstVectorBlock(i),true)->getTpetraVector();
836
837 // use Tpetra to do global communication
838 ghostToGlobalTpetraVector(i,*tp_in,*tp_out,col);
839 }
840}
841
842template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
845{
846 using Teuchos::RCP;
847 using Teuchos::rcp_dynamic_cast;
848 using Teuchos::dyn_cast;
850 using Thyra::PhysicallyBlockedLinearOpBase;
851
852 std::size_t rowBlockDim = getBlockRowCount();
853
854 // get product vectors
855 const PhysicallyBlockedLinearOpBase<ScalarT> & prod_in = dyn_cast<const PhysicallyBlockedLinearOpBase<ScalarT> >(in);
856 PhysicallyBlockedLinearOpBase<ScalarT> & prod_out = dyn_cast<PhysicallyBlockedLinearOpBase<ScalarT> >(out);
857
858 std::size_t colBlockDim = getBlockColCount();
859
860 TEUCHOS_ASSERT(prod_in.productRange()->numBlocks()==(int) rowBlockDim);
861 TEUCHOS_ASSERT(prod_in.productDomain()->numBlocks()==(int) colBlockDim);
862 TEUCHOS_ASSERT(prod_out.productRange()->numBlocks()==(int) rowBlockDim);
863 TEUCHOS_ASSERT(prod_out.productDomain()->numBlocks()==(int) colBlockDim);
864
865 for(std::size_t i=0;i<rowBlockDim;i++) {
866 for(std::size_t j=0;j<colBlockDim;j++) {
867 if(excludedPairs_.find(std::make_pair(i,j))==excludedPairs_.end()) {
868 // extract the blocks
869 RCP<const LinearOpBase<ScalarT> > th_in = prod_in.getBlock(i,j);
870 RCP<LinearOpBase<ScalarT> > th_out = prod_out.getNonconstBlock(i,j);
871
872 // sanity check
873 TEUCHOS_ASSERT(th_in!=Teuchos::null);
874 TEUCHOS_ASSERT(th_out!=Teuchos::null);
875
876 // get the epetra version of the blocks
877 RCP<const OperatorType> tp_op_in = rcp_dynamic_cast<const ThyraLinearOp>(th_in,true)->getConstTpetraOperator();
878 RCP<OperatorType> tp_op_out = rcp_dynamic_cast<ThyraLinearOp>(th_out,true)->getTpetraOperator();
879
880 RCP<const CrsMatrixType> tp_in = rcp_dynamic_cast<const CrsMatrixType>(tp_op_in,true);
881 RCP<CrsMatrixType> tp_out = rcp_dynamic_cast<CrsMatrixType>(tp_op_out,true);
882
883 // In FE mode this block is one shared FECrsMatrix, and endFill()'s endAssembly()
884 // has already migrated its ghost rows onto the owned rows. Exporting it onto
885 // itself here would double every shared-interface contribution.
886 //
887 // The test has to be on the extracted Tpetra matrices: even when the blocks are
888 // shared, the owned and ghosted containers hold distinct DefaultBlockedLinearOp
889 // objects wrapping distinct Thyra::TpetraLinearOps, so comparing get_A() -- or
890 // the Thyra blocks -- would never report a match.
891 if(tp_in.get()==tp_out.get())
892 continue;
893
894 // use Tpetra to do global communication
895 ghostToGlobalTpetraMatrix(i,*tp_in,*tp_out);
896 }
897 }
898 }
899}
900
901template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
903globalToGhostThyraVector(const Teuchos::RCP<const Thyra::VectorBase<ScalarT> > & in,
904 const Teuchos::RCP<Thyra::VectorBase<ScalarT> > & out,bool col) const
905{
906 using Teuchos::RCP;
907 using Teuchos::rcp_dynamic_cast;
909
910 std::size_t blockDim = col ? getBlockColCount() : getBlockRowCount();
911
912 // get product vectors, viewing a flat vector as a single block
913 RCP<const ProductVectorBase<ScalarT> > prod_in = Thyra::castOrCreateProductVectorBase(in);
914 RCP<ProductVectorBase<ScalarT> > prod_out = Thyra::castOrCreateNonconstProductVectorBase(out);
915
916 TEUCHOS_ASSERT(prod_in->productSpace()->numBlocks()==(int) blockDim);
917 TEUCHOS_ASSERT(prod_out->productSpace()->numBlocks()==(int) blockDim);
918
919 for(std::size_t i=0;i<blockDim;i++) {
920 // first get each Tpetra vector
921 RCP<const VectorType> tp_in = rcp_dynamic_cast<const ThyraVector>(prod_in->getVectorBlock(i),true)->getConstTpetraVector();
922 RCP<VectorType> tp_out = rcp_dynamic_cast<ThyraVector>(prod_out->getNonconstVectorBlock(i),true)->getTpetraVector();
923
924 // use Tpetra to do global communication
925 globalToGhostTpetraVector(i,*tp_in,*tp_out,col);
926 }
927}
928
929// Tpetra methods
931
932template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
934ghostToGlobalTpetraVector(int i,const VectorType & in,VectorType & out,bool col) const
935{
936 using Teuchos::RCP;
937
938 // do the global distribution
939 RCP<const ExportType> exporter = col ? getGhostedColExport(i) : getGhostedExport(i);
940 out.putScalar(0.0);
941 out.doExport(in,*exporter,Tpetra::ADD);
942}
943
944template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
946ghostToGlobalTpetraMatrix(int blockRow,const CrsMatrixType & in,CrsMatrixType & out) const
947{
948 using Teuchos::RCP;
949
950 RCP<const MapType> map_i = out.getRangeMap();
951 RCP<const MapType> map_j = out.getDomainMap();
952
953 // do the global distribution
954 RCP<const ExportType> exporter = getGhostedExport(blockRow);
955
956 out.resumeFill();
957 out.setAllToScalar(0.0);
958 out.doExport(in,*exporter,Tpetra::ADD);
959 out.fillComplete(map_j,map_i);
960}
961
962template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
964globalToGhostTpetraVector(int i,const VectorType & in,VectorType & out,bool col) const
965{
966 using Teuchos::RCP;
967
968 // do the global distribution
969 RCP<const ImportType> importer = col ? getGhostedColImport(i) : getGhostedImport(i);
970 out.putScalar(0.0);
971 out.doImport(in,*importer,Tpetra::INSERT);
972}
973
974// get the map from the matrix
975template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
976Teuchos::RCP<const Tpetra::Map<LocalOrdinalT,GlobalOrdinalT,NodeT> >
978getMap(int i) const
979{
980 if(maps_[i]==Teuchos::null)
981 maps_[i] = buildTpetraMap(i);
982
983 return maps_[i];
984}
985
986template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
987Teuchos::RCP<const Tpetra::Map<LocalOrdinalT,GlobalOrdinalT,NodeT> >
989getGhostedMap(int i) const
990{
991 if(ghostedMaps_[i]==Teuchos::null)
992 ghostedMaps_[i] = buildTpetraGhostedMap(i);
993
994 return ghostedMaps_[i];
995}
996
997// get the graph of the crs matrix
998template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
999Teuchos::RCP<const Tpetra::CrsGraph<LocalOrdinalT,GlobalOrdinalT,NodeT> >
1001getGraph(int i,int j) const
1002{
1003 typedef std::unordered_map<std::pair<int,int>,Teuchos::RCP<const CrsGraphType>,panzer::pair_hash> GraphMap;
1004
1005 typename GraphMap::const_iterator itr = graphs_.find(std::make_pair(i,j));
1006 Teuchos::RCP<const CrsGraphType> graph;
1007 if(itr==graphs_.end()) {
1008 graph = buildTpetraGraph(i,j);
1009 graphs_[std::make_pair(i,j)] = graph;
1010 }
1011 else
1012 graph = itr->second;
1013
1014 TEUCHOS_ASSERT(graph!=Teuchos::null);
1015 return graph;
1016}
1017
1018template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1019Teuchos::RCP<const Tpetra::CrsGraph<LocalOrdinalT,GlobalOrdinalT,NodeT> >
1021getGhostedGraph(int i,int j) const
1022{
1023 typedef std::unordered_map<std::pair<int,int>,Teuchos::RCP<const CrsGraphType>,panzer::pair_hash> GraphMap;
1024
1025 typename GraphMap::const_iterator itr = ghostedGraphs_.find(std::make_pair(i,j));
1026 Teuchos::RCP<const CrsGraphType> ghostedGraph;
1027 if(itr==ghostedGraphs_.end()) {
1028 ghostedGraph = buildTpetraGhostedGraph(i,j);
1029 ghostedGraphs_[std::make_pair(i,j)] = ghostedGraph;
1030 }
1031 else
1032 ghostedGraph = itr->second;
1033
1034 TEUCHOS_ASSERT(ghostedGraph!=Teuchos::null);
1035 return ghostedGraph;
1036}
1037
1038template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1039Teuchos::RCP<const Tpetra::Import<LocalOrdinalT,GlobalOrdinalT,NodeT> >
1041getGhostedImport(int i) const
1042{
1043 if(importers_[i]==Teuchos::null)
1044 importers_[i] = Teuchos::rcp(new ImportType(getMap(i),getGhostedMap(i)));
1045
1046 return importers_[i];
1047}
1048
1049template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1050Teuchos::RCP<const Tpetra::Export<LocalOrdinalT,GlobalOrdinalT,NodeT> >
1052getGhostedExport(int i) const
1053{
1054 if(exporters_[i]==Teuchos::null)
1055 exporters_[i] = Teuchos::rcp(new ExportType(getGhostedMap(i),getMap(i)));
1056
1057 return exporters_[i];
1058}
1059
1060template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1061Teuchos::RCP<const Tpetra::Map<LocalOrdinalT,GlobalOrdinalT,NodeT> >
1063getColMap(int i) const
1064{
1065 if(!hasColProvider_)
1066 return getMap(i); // the row and column spaces are the same in this case
1067
1068 if(colMaps_[i]==Teuchos::null)
1069 colMaps_[i] = buildColTpetraMap(i);
1070
1071 return colMaps_[i];
1072}
1073
1074template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1075Teuchos::RCP<const Tpetra::Map<LocalOrdinalT,GlobalOrdinalT,NodeT> >
1077getGhostedColMap(int i) const
1078{
1079 if(!hasColProvider_)
1080 return getGhostedMap(i); // the row and column spaces are the same in this case
1081
1082 if(ghostedColMaps_[i]==Teuchos::null)
1083 ghostedColMaps_[i] = buildColTpetraGhostedMap(i);
1084
1085 return ghostedColMaps_[i];
1086}
1087
1088template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1089Teuchos::RCP<const Tpetra::Import<LocalOrdinalT,GlobalOrdinalT,NodeT> >
1091getGhostedColImport(int i) const
1092{
1093 if(!hasColProvider_)
1094 return getGhostedImport(i); // the row and column spaces are the same in this case
1095
1096 if(colImporters_[i]==Teuchos::null)
1097 colImporters_[i] = Teuchos::rcp(new ImportType(getColMap(i),getGhostedColMap(i)));
1098
1099 return colImporters_[i];
1100}
1101
1102template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1103Teuchos::RCP<const Tpetra::Export<LocalOrdinalT,GlobalOrdinalT,NodeT> >
1105getGhostedColExport(int i) const
1106{
1107 if(!hasColProvider_)
1108 return getGhostedExport(i); // the row and column spaces are the same in this case
1109
1110 if(colExporters_[i]==Teuchos::null)
1111 colExporters_[i] = Teuchos::rcp(new ExportType(getGhostedColMap(i),getColMap(i)));
1112
1113 return colExporters_[i];
1114}
1115
1116template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1117Teuchos::RCP<const Tpetra::Map<LocalOrdinalT,GlobalOrdinalT,NodeT> >
1119buildTpetraMap(int i) const
1120{
1121 std::vector<GlobalOrdinalT> indices;
1122
1123 // get the global indices
1124 getGlobalIndexer(i)->getOwnedIndices(indices);
1125
1126 return Teuchos::rcp(new MapType(Teuchos::OrdinalTraits<GlobalOrdinalT>::invalid(),indices,0,comm_));
1127}
1128
1129// build the ghosted map
1130template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1131Teuchos::RCP<const Tpetra::Map<LocalOrdinalT,GlobalOrdinalT,NodeT> >
1133buildTpetraGhostedMap(int i) const
1134{
1135 std::vector<GlobalOrdinalT> indices;
1136
1137 // get the global indices
1138 getGlobalIndexer(i)->getOwnedAndGhostedIndices(indices);
1139
1140 return Teuchos::rcp(new MapType(Teuchos::OrdinalTraits<GlobalOrdinalT>::invalid(),indices,0,comm_));
1141}
1142
1143template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1144Teuchos::RCP<const Tpetra::Map<LocalOrdinalT,GlobalOrdinalT,NodeT> >
1146buildColTpetraMap(int i) const
1147{
1148 std::vector<GlobalOrdinalT> indices;
1149
1150 // get the global indices
1151 getColGlobalIndexer(i)->getOwnedIndices(indices);
1152
1153 return Teuchos::rcp(new MapType(Teuchos::OrdinalTraits<GlobalOrdinalT>::invalid(),indices,0,comm_));
1154}
1155
1156template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1157Teuchos::RCP<const Tpetra::Map<LocalOrdinalT,GlobalOrdinalT,NodeT> >
1159buildColTpetraGhostedMap(int i) const
1160{
1161 std::vector<GlobalOrdinalT> indices;
1162
1163 // get the global indices
1164 getColGlobalIndexer(i)->getOwnedAndGhostedIndices(indices);
1165
1166 return Teuchos::rcp(new MapType(Teuchos::OrdinalTraits<GlobalOrdinalT>::invalid(),indices,0,comm_));
1167}
1168
1169// get the graph of the crs matrix
1170template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1171Teuchos::RCP<const Tpetra::CrsGraph<LocalOrdinalT,GlobalOrdinalT,NodeT> >
1173buildTpetraGraph(int i,int j) const
1174{
1175 using Teuchos::RCP;
1176 using Teuchos::rcp;
1177
1178 // build the map and allocate the space for the graph and
1179 // grab the ghosted graph
1180 RCP<const MapType> map_i = getMap(i);
1181 RCP<const MapType> map_j = getColMap(j);
1182
1183 RCP<CrsGraphType> graph = rcp(new CrsGraphType(map_i,0));
1184 RCP<const CrsGraphType> oGraph = getGhostedGraph(i,j);
1185
1186 // perform the communication to finish building graph
1187 RCP<const ExportType> exporter = getGhostedExport(i);
1188 graph->doExport( *oGraph, *exporter, Tpetra::INSERT );
1189 graph->fillComplete(map_j,map_i);
1190
1191 return graph;
1192}
1193
1194template <class LocalOrdinalT>
1196 LocalOrdinalT row;
1197 LocalOrdinalT col;
1198};
1199
1200template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1201Teuchos::RCP<const Tpetra::CrsGraph<LocalOrdinalT,GlobalOrdinalT,NodeT> >
1203buildTpetraGhostedGraph(int i,int j) const
1204{
1205 PANZER_FUNC_TIME_MONITOR_DIFF("panzer::BlockedTpetraLinearObjFactory::buildTpetraGhostedGraph",BTLOF);
1206
1207 using Teuchos::RCP;
1208 using Teuchos::rcp;
1209 using exec_space = typename CrsGraphType::execution_space;
1210 using memory_space = typename NodeT::memory_space;
1211
1212 // build the map and allocate the space for the graph and
1213 // grab the ghosted graph
1214 RCP<const MapType> map_i = getGhostedMap(i);
1215 RCP<const MapType> map_j = getGhostedColMap(j);
1216
1217 std::vector<std::string> elementBlockIds;
1218
1219 Teuchos::RCP<const GlobalIndexer> rowProvider, colProvider;
1220
1221 rowProvider = getGlobalIndexer(i);
1222 colProvider = getColGlobalIndexer(j);
1223
1224 gidProviders_[0]->getElementBlockIds(elementBlockIds); // each sub provider "should" have the
1225 // same element blocks
1226
1227 RCP<CrsGraphType> graph;
1228 if constexpr (NodeT::is_gpu) {
1229
1230 // Gather elements from mesh blocks.
1231 size_t numElements;
1232 Kokkos::View<LocalOrdinalT *, memory_space> elementsFromBlocks;
1233 {
1234 auto numElementBlocks = elementBlockIds.size();
1235
1236 std::vector<size_t> elementBlockOffsets(numElementBlocks + 1);
1237 elementBlockOffsets[0] = 0;
1238
1239 numElements = 0;
1240 size_t blockNo = 0;
1241 std::vector<std::string>::const_iterator blockItr;
1242 for (blockItr = elementBlockIds.begin();
1243 blockItr != elementBlockIds.end(); ++blockItr) {
1244 std::string blockId = *blockItr;
1245 const std::vector<LocalOrdinalT> &elements =
1246 gidProviders_[0]->getElementBlock(
1247 blockId); // each sub provider "should" have the
1248 // same elements in each element block
1249 numElements += elements.size();
1250 ++blockNo;
1251 elementBlockOffsets[blockNo] = numElements;
1252 }
1253 elementsFromBlocks = Kokkos::View<LocalOrdinalT *, memory_space>(
1254 "elementsFromBlocks", numElements);
1255 blockNo = 0;
1256 for (blockItr = elementBlockIds.begin();
1257 blockItr != elementBlockIds.end(); ++blockItr) {
1258 std::string blockId = *blockItr;
1259 const std::vector<LocalOrdinalT> &elements =
1260 gidProviders_[0]->getElementBlock(
1261 blockId); // each sub provider "should" have the
1262 // same elements in each element block
1263 Kokkos::View<const LocalOrdinalT *, Kokkos::HostSpace,
1264 Kokkos::MemoryTraits<Kokkos::Unmanaged>>
1265 elements_h(elements.data(), elements.size());
1266 Kokkos::deep_copy(
1267 Kokkos::subview(
1268 elementsFromBlocks,
1269 Kokkos::make_pair(elementBlockOffsets[blockNo],
1270 elementBlockOffsets[blockNo + 1])),
1271 elements_h);
1272 ++blockNo;
1273 }
1274 }
1275
1276 {
1277
1278 using local_graph_type = typename CrsGraphType::local_graph_device_type;
1279 using rowptr_type =
1280 typename local_graph_type::row_map_type::non_const_type;
1281 using colidx_type =
1282 typename local_graph_type::entries_type::non_const_type;
1283
1284 using entries_map_type =
1285 Kokkos::UnorderedMap<entry_type<LocalOrdinalT>, void, exec_space>;
1286
1287 auto numRows = map_i->getLocalNumElements();
1288
1289 // We are overallocating by 1 here. This simplifies the logic below. But
1290 // we have to remember to take a subview in the end.
1291 rowptr_type rowptr("ghostedGraph_rowptr", numRows + 2);
1292
1293 auto rowLIDs = rowProvider->getLIDs();
1294 auto colLIDs = colProvider->getLIDs();
1295
1296 auto numDoFsPerElementRow = rowLIDs.extent(1);
1297 auto numDoFsPerElementCol = colLIDs.extent(1);
1298
1299 auto capacity =
1300 numElements * numDoFsPerElementRow * numDoFsPerElementCol;
1301 entries_map_type entries(capacity);
1302
1303 while (true) {
1304
1305 // Loop over all elements and record the entries that we need in the
1306 // graph. Also start building the rowptr.
1307 Kokkos::parallel_for(
1308 "collect_entries", Kokkos::RangePolicy<exec_space>(0, numElements),
1309 KOKKOS_LAMBDA(const LocalOrdinalT k) {
1310 auto elementId = elementsFromBlocks(k);
1312 for (size_t dofNoRow = 0; dofNoRow < numDoFsPerElementRow;
1313 ++dofNoRow) {
1314 entry.row = rowLIDs(elementId, dofNoRow);
1315 for (size_t dofNoCol = 0; dofNoCol < numDoFsPerElementCol;
1316 ++dofNoCol) {
1317 entry.col = colLIDs(elementId, dofNoCol);
1318 auto result = entries.insert(entry);
1319 if (result.success()) {
1320 // New entry. We offset by 2 here.
1321 Kokkos::atomic_inc(&rowptr(entry.row + 2));
1322 }
1323 }
1324 }
1325 });
1326
1327 if (!entries.failed_insert()) {
1328 auto numEntries = entries.size();
1329
1330 // Prefix sum to get offsets.
1331 // This is not the correct rowptr yet.
1332 // We have essentially shifted everything by one position.
1333 // This is useful for when we fill.
1334 typename rowptr_type::value_type numEntries2;
1335 Kokkos::parallel_scan(
1336 "prefix_sum", Kokkos::RangePolicy<exec_space>(0, numRows + 2),
1337 KOKKOS_LAMBDA(const size_t rlid,
1338 typename rowptr_type::value_type &nnz,
1339 const bool is_final) {
1340 nnz += rowptr(rlid);
1341 if (is_final)
1342 rowptr(rlid) = nnz;
1343 },
1344 numEntries2);
1345 TEUCHOS_ASSERT_EQUALITY(numEntries, numEntries2);
1346
1347 // The column indices.
1348 colidx_type colidx(
1349 Kokkos::ViewAllocateWithoutInitializing("ghostedGraph_colidx"),
1350 numEntries);
1351
1352 // Fill the column indices.
1353 // We are using the rowptr to figure out offsets.
1354 // After this step the rowptr is correct.
1355 Kokkos::parallel_for(
1356 "fill", Kokkos::RangePolicy<exec_space>(0, entries.capacity()),
1357 KOKKOS_LAMBDA(const uint32_t c) {
1358 if (entries.valid_at(c)) {
1359 auto entry = entries.key_at(c);
1360 auto offset =
1361 Kokkos::atomic_fetch_inc(&rowptr(entry.row + 1));
1362 colidx(offset) = entry.col;
1363 }
1364 });
1365
1366 // Sort the rows.
1367 KokkosSparse::sort_crs_graph(rowptr, colidx);
1368
1369 // Create the graph
1370 graph = rcp(new CrsGraphType(
1371 map_i, map_j,
1372 Kokkos::subview(rowptr, Kokkos::make_pair((decltype(numRows))0,
1373 numRows + 1)),
1374 colidx));
1375 graph->fillComplete(getMap(j), getMap(i));
1376
1377 break;
1378 } else {
1379 // We ended up not having enough capacity in the UnorderedMap.
1380 // Bump it up and try again.
1381 std::cout << "Insufficient capacity: " << capacity << std::endl;
1382 capacity *= 2;
1383 Kokkos::deep_copy(rowptr, 0);
1384 entries = entries_map_type(capacity);
1385 }
1386 }
1387 }
1388 } else {
1389 // Count number of entries in each row of graph; needed for graph
1390 // constructor
1391 std::vector<size_t> nEntriesPerRow(map_i->getLocalNumElements(), 0);
1392 std::vector<std::string>::const_iterator blockItr;
1393 for (blockItr = elementBlockIds.begin(); blockItr != elementBlockIds.end();
1394 ++blockItr) {
1395 std::string blockId = *blockItr;
1396 // grab elements for this block
1397 const std::vector<LocalOrdinalT> &elements =
1398 gidProviders_[0]->getElementBlock(
1399 blockId); // each sub provider "should" have the
1400 // same elements in each element block
1401
1402 // get information about number of indicies
1403 std::vector<GlobalOrdinalT> row_gids;
1404 std::vector<GlobalOrdinalT> col_gids;
1405
1406 // loop over the elemnts
1407 for (std::size_t elmt = 0; elmt < elements.size(); elmt++) {
1408
1409 rowProvider->getElementGIDs(elements[elmt], row_gids);
1410 colProvider->getElementGIDs(elements[elmt], col_gids);
1411 for (std::size_t row = 0; row < row_gids.size(); row++) {
1412 LocalOrdinalT lid = map_i->getLocalElement(row_gids[row]);
1413 nEntriesPerRow[lid] += col_gids.size();
1414 }
1415 }
1416 }
1417 Teuchos::ArrayView<const size_t> nEntriesPerRowView(nEntriesPerRow);
1418 graph = rcp(new CrsGraphType(map_i, map_j, nEntriesPerRowView));
1419
1420 // graph information about the mesh
1421 for (blockItr = elementBlockIds.begin(); blockItr != elementBlockIds.end();
1422 ++blockItr) {
1423 std::string blockId = *blockItr;
1424
1425 // grab elements for this block
1426 const std::vector<LocalOrdinalT> &elements =
1427 gidProviders_[0]->getElementBlock(
1428 blockId); // each sub provider "should" have the
1429 // same elements in each element block
1430
1431 // get information about number of indicies
1432 std::vector<GlobalOrdinalT> row_gids;
1433 std::vector<GlobalOrdinalT> col_gids;
1434
1435 // loop over the elemnts
1436 for (std::size_t elmt = 0; elmt < elements.size(); elmt++) {
1437
1438 rowProvider->getElementGIDs(elements[elmt], row_gids);
1439 colProvider->getElementGIDs(elements[elmt], col_gids);
1440 for (std::size_t row = 0; row < row_gids.size(); row++)
1441 graph->insertGlobalIndices(row_gids[row], col_gids);
1442 }
1443 }
1444
1445 // finish filling the graph: Make sure the colmap and row maps coincide to
1446 // minimize calls to LID lookups
1447 graph->fillComplete(getMap(j), getMap(i));
1448 }
1449
1450 return graph;
1451}
1452
1453// Build the FE graph for block (i,j).
1454//
1455// This walks the same element/GID traversal as buildTpetraGhostedGraph(i,j) -- rows come
1456// from indexer i, columns from indexer j -- but hands the result to the FECrsGraph "V2"
1457// constructor, which carries both the owned and the owned+shared graph in one object.
1458//
1459// There is no getColMap() in this class and none is needed: block (i,j) takes its rows from
1460// indexer i and its columns from indexer j, so block j's row map IS this block's column map.
1461// That is the same convention the classic path already uses -- buildTpetraGhostedGraph()
1462// fill-completes with (getMap(j),getMap(i)).
1463//
1464// The V2 constructor requires that the owned row/domain gids appear, in the same order, as a
1465// leading prefix of the owned+shared row/domain map. Every concrete panzer::GlobalIndexer
1466// builds getOwnedAndGhostedIndices() as owned_ followed by ghosted_, so getMap(i)/
1467// getGhostedMap(i) satisfy this directly.
1468template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1469Teuchos::RCP<typename BlockedTpetraLinearObjFactory<Traits,ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT>::FECrsGraphType>
1471buildFEGraph(int i,int j) const
1472{
1473 using Teuchos::RCP;
1474 using Teuchos::rcp;
1475
1476 // NOTE: these must be RCP<const MapType> (not RCP<MapType>). FECrsGraph has both a "V1"
1477 // ctor (4th positional arg = importer) and a "V2" ctor (4th positional arg =
1478 // ownedPlusSharedDomainMap); Teuchos::RCP's converting constructor is an unconstrained
1479 // template, so an RCP<MapType> is equally "convertible" to either as far as overload
1480 // resolution is concerned. Typing the locals as RCP<const MapType> binds them to V2's
1481 // domain-map parameter with no conversion at all, resolving the ambiguity in V2's favor.
1482 RCP<const MapType> ownedRowMap = getMap(i);
1483 RCP<const MapType> ownedPlusSharedRowMap = getGhostedMap(i);
1484 RCP<const MapType> ownedDomainMap = getMap(j);
1485 RCP<const MapType> ownedPlusSharedDomainMap = getGhostedMap(j);
1486
1487 RCP<const GlobalIndexer> rowProvider = getGlobalIndexer(i);
1488 RCP<const GlobalIndexer> colProvider = getGlobalIndexer(j);
1489
1490 std::vector<std::string> elementBlockIds;
1491 gidProviders_[0]->getElementBlockIds(elementBlockIds); // each sub provider "should" have
1492 // the same element blocks
1493
1494 // count entries per owned+shared row, exactly as buildTpetraGhostedGraph() does
1495 std::vector<size_t> nEntriesPerRow(ownedPlusSharedRowMap->getLocalNumElements(),0);
1496
1497 std::vector<std::string>::const_iterator blockItr;
1498 for(blockItr=elementBlockIds.begin();blockItr!=elementBlockIds.end();++blockItr) {
1499 const std::vector<LocalOrdinalT> & elements = gidProviders_[0]->getElementBlock(*blockItr);
1500
1501 std::vector<GlobalOrdinalT> row_gids;
1502 std::vector<GlobalOrdinalT> col_gids;
1503
1504 for(std::size_t elmt=0;elmt<elements.size();elmt++) {
1505 rowProvider->getElementGIDs(elements[elmt],row_gids);
1506 colProvider->getElementGIDs(elements[elmt],col_gids);
1507 for(std::size_t row=0;row<row_gids.size();row++) {
1508 LocalOrdinalT lid = ownedPlusSharedRowMap->getLocalElement(row_gids[row]);
1509 nEntriesPerRow[lid] += col_gids.size();
1510 }
1511 }
1512 }
1513
1514 size_t maxNumRowEntries = 0;
1515 for(std::size_t r=0;r<nEntriesPerRow.size();r++)
1516 maxNumRowEntries = std::max(maxNumRowEntries,nEntriesPerRow[r]);
1517
1518 RCP<FECrsGraphType> feGraph = rcp(new FECrsGraphType(
1519 ownedRowMap, ownedPlusSharedRowMap, maxNumRowEntries,
1520 ownedPlusSharedDomainMap,
1521 Teuchos::null,
1522 ownedDomainMap));
1523
1524 // Panzer's DOFManager does not guarantee a locally owned element has an owned dof, so
1525 // Tpetra's debug-only check for that is too strict here; the cost is at most a structurally
1526 // empty column. Must be set after construction -- the ctor's validator rejects the option.
1527 {
1528 Teuchos::RCP<Teuchos::ParameterList> feGraphParams = Teuchos::parameterList();
1529 feGraphParams->set("Check Col GIDs In At Least One Owned Row",false);
1530 feGraph->setParameterList(feGraphParams);
1531 }
1532
1533 feGraph->beginAssembly();
1534 for(blockItr=elementBlockIds.begin();blockItr!=elementBlockIds.end();++blockItr) {
1535 const std::vector<LocalOrdinalT> & elements = gidProviders_[0]->getElementBlock(*blockItr);
1536
1537 std::vector<GlobalOrdinalT> row_gids;
1538 std::vector<GlobalOrdinalT> col_gids;
1539
1540 for(std::size_t elmt=0;elmt<elements.size();elmt++) {
1541 rowProvider->getElementGIDs(elements[elmt],row_gids);
1542 colProvider->getElementGIDs(elements[elmt],col_gids);
1543 for(std::size_t row=0;row<row_gids.size();row++)
1544 feGraph->insertGlobalIndices(row_gids[row],col_gids);
1545 }
1546 }
1547 feGraph->endAssembly();
1548
1549 return feGraph;
1550}
1551
1552template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1553Teuchos::RCP<typename BlockedTpetraLinearObjFactory<Traits,ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT>::FECrsGraphType>
1555getFEGraph(int i,int j) const
1556{
1557 TEUCHOS_TEST_FOR_EXCEPTION(!useFEAssembly_,std::logic_error,
1558 "BlockedTpetraLinearObjFactory::getFEGraph: This factory was not constructed with "
1559 "FE assembly enabled.");
1560
1561 typedef std::unordered_map<std::pair<int,int>,Teuchos::RCP<FECrsGraphType>,panzer::pair_hash> FEGraphMap;
1562
1563 typename FEGraphMap::const_iterator itr = feGraphs_.find(std::make_pair(i,j));
1564 if(itr!=feGraphs_.end())
1565 return itr->second;
1566
1567 Teuchos::RCP<FECrsGraphType> graph = buildFEGraph(i,j);
1568 feGraphs_[std::make_pair(i,j)] = graph;
1569 return graph;
1570}
1571
1572template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1573Teuchos::RCP<typename BlockedTpetraLinearObjFactory<Traits,ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT>::FECrsMatrixType>
1575getFEMatrix(int i,int j) const
1576{
1577 TEUCHOS_TEST_FOR_EXCEPTION(!useFEAssembly_,std::logic_error,
1578 "BlockedTpetraLinearObjFactory::getFEMatrix: This factory was not constructed with "
1579 "FE assembly enabled.");
1580
1581 // A fresh matrix per call, like the classic getTpetraMatrix(i,j). The per-block graph
1582 // behind it is shared and cached, so this is only the values allocation.
1583 return Teuchos::rcp(new FECrsMatrixType(getFEGraph(i,j)));
1584}
1585
1586template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1587Teuchos::RCP<Tpetra::CrsMatrix<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT> >
1589getTpetraMatrix(int i,int j) const
1590{
1591 // In FE mode hand back an FE matrix. It is fill-complete over (getMap(j),getMap(i)) once
1592 // endAssembly() has run, which is what callers of the "owned" matrix expect. The ghosted
1593 // container borrows the blocked operator built from these at beginFill().
1594 if(useFEAssembly_)
1595 return getFEMatrix(i,j);
1596
1597 Teuchos::RCP<const MapType> map_i = getMap(i);
1598 Teuchos::RCP<const MapType> map_j = getMap(j);
1599
1600 Teuchos::RCP<const CrsGraphType> tGraph = getGraph(i,j);
1601 Teuchos::RCP<CrsMatrixType> mat = Teuchos::rcp(new CrsMatrixType(tGraph));
1602 mat->fillComplete(map_j,map_i);
1603
1604 return mat;
1605}
1606
1607template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1608Teuchos::RCP<Tpetra::CrsMatrix<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT> >
1610getGhostedTpetraMatrix(int i,int j) const
1611{
1612 // There is no separate ghosted matrix under FE assembly: a ghosted container borrows the
1613 // owned container's blocked operator at beginFill(), whose FE blocks already span the
1614 // ghosted maps in their owned+shared view. Returning a standalone matrix here would look
1615 // usable but silently drop every ghost contribution, so refuse instead.
1616 TEUCHOS_TEST_FOR_EXCEPTION(useFEAssembly_,std::logic_error,
1617 "BlockedTpetraLinearObjFactory::getGhostedTpetraMatrix: not available under FE "
1618 "assembly. The ghosted container shares the owned container's operator, which is "
1619 "connected by beginFill(ghosted,owned); use getFEMatrix(i,j) to allocate a block.");
1620
1621 Teuchos::RCP<const MapType> map_i = getGhostedMap(i);
1622 Teuchos::RCP<const MapType> map_j = getGhostedMap(j);
1623
1624 Teuchos::RCP<const CrsGraphType> tGraph = getGhostedGraph(i,j);
1625 Teuchos::RCP<CrsMatrixType> mat = Teuchos::rcp(new CrsMatrixType(tGraph));
1626 mat->fillComplete(getMap(j),getMap(i));
1627
1628 return mat;
1629}
1630
1631template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1632Teuchos::RCP<Tpetra::Vector<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT> >
1634getTpetraDomainVector(int i) const
1635{
1636 Teuchos::RCP<const MapType> tMap = getColMap(i);
1637 return Teuchos::rcp(new VectorType(tMap));
1638}
1639
1640template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1641Teuchos::RCP<Tpetra::Vector<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT> >
1644{
1645 Teuchos::RCP<const MapType> tMap = getGhostedColMap(i);
1646 return Teuchos::rcp(new VectorType(tMap));
1647}
1648
1649template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1650Teuchos::RCP<Tpetra::Vector<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT> >
1652getTpetraRangeVector(int i) const
1653{
1654 Teuchos::RCP<const MapType> tMap = getMap(i);
1655 return Teuchos::rcp(new VectorType(tMap));
1656}
1657
1658template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1659Teuchos::RCP<Tpetra::Vector<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT> >
1661getGhostedTpetraRangeVector(int i) const
1662{
1663 Teuchos::RCP<const MapType> tMap = getGhostedMap(i);
1664 return Teuchos::rcp(new VectorType(tMap));
1665}
1666
1667template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1668int
1674
1675template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1676int
1678getBlockColCount() const
1679{
1680 return hasColProvider_ ? colGidProviders_.size() : gidProviders_.size();
1681}
1682
1683namespace blocked_tpetra_lof_detail {
1684
1688 template <typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1689 Teuchos::RCP<Tpetra::CrsMatrix<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT> >
1690 getBlockAsCrsMatrix(Thyra::PhysicallyBlockedLinearOpBase<ScalarT> & Amat,int i,int j)
1691 {
1692 using Teuchos::RCP;
1693 using Teuchos::rcp_dynamic_cast;
1694
1695 RCP<Thyra::LinearOpBase<ScalarT> > block = Amat.getNonconstBlock(i,j);
1696 if(block==Teuchos::null)
1697 return Teuchos::null;
1698
1699 RCP<Tpetra::Operator<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT> > t_block =
1700 rcp_dynamic_cast<Thyra::TpetraLinearOp<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT> >(block,true)->getTpetraOperator();
1701
1702 return rcp_dynamic_cast<Tpetra::CrsMatrix<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT> >(t_block,true);
1703 }
1704
1705}
1706
1707// In FE mode the fill lifecycle is driven here rather than delegated to the container.
1708//
1709// The container's beginFill()/endFill() call the plain, inherited CrsMatrix::resumeFill() /
1710// fillComplete() on each block. For an FECrsMatrix that would silently bypass its owned /
1711// owned+shared state machine, so ghost-row contributions would never be migrated to the
1712// owned rows. The factory is the right place for the FE version: it is a single object
1713// shared by both containers and it already owns the per-block matrix cache, whereas the
1714// container is not templated on Traits and has no back-pointer to the factory.
1715template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1717beginFill(LinearObjContainer & ghostContainer,const LinearObjContainer & container) const
1718{
1719 // Under FE assembly the ghosted container carries no operator of its own: hand it the
1720 // owned container's, so local assembly writes into the owned+shared view of the very
1721 // blocks endAssembly() will migrate. Doing this here rather than when the container is
1722 // built is what lets the Jacobian be whichever operator the caller supplies --
1723 // panzer::ModelEvaluator sets the owned container's operator from W_out on every
1724 // evaluation, so it is not known any earlier, and a factory-cached one would alias every
1725 // W_out to the first.
1726 if(useFEAssembly_) {
1727 const BTLOC & ownedLoc = Teuchos::dyn_cast<const BTLOC>(container);
1728 if(ownedLoc.get_A()!=Teuchos::null)
1729 Teuchos::dyn_cast<BTLOC>(ghostContainer).set_A(ownedLoc.get_A());
1730 }
1731
1732 beginFill(ghostContainer);
1733}
1734
1735template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1737beginFill(LinearObjContainer & loc) const
1738{
1739 using Teuchos::RCP;
1740 using Teuchos::rcp_dynamic_cast;
1741 using Thyra::PhysicallyBlockedLinearOpBase;
1742
1743 BTLOC & tloc = Teuchos::dyn_cast<BTLOC>(loc);
1744 if(tloc.get_A()==Teuchos::null)
1745 return;
1746
1747 if(!useFEAssembly_) {
1748 tloc.beginFill();
1749 return;
1750 }
1751
1752 RCP<PhysicallyBlockedLinearOpBase<ScalarT> > Amat
1753 = rcp_dynamic_cast<PhysicallyBlockedLinearOpBase<ScalarT> >(tloc.get_A(),true);
1754
1755 const int blockDim = static_cast<int>(gidProviders_.size());
1756 for(int i=0;i<blockDim;i++) {
1757 for(int j=0;j<blockDim;j++) {
1758 RCP<CrsMatrixType> mat
1759 = blocked_tpetra_lof_detail::getBlockAsCrsMatrix<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT>(*Amat,i,j);
1760 if(mat==Teuchos::null)
1761 continue;
1762
1763 RCP<FECrsMatrixType> feMat = rcp_dynamic_cast<FECrsMatrixType>(mat);
1764 if(feMat==Teuchos::null) {
1765 // not an FE block (should not happen in FE mode, but stay well behaved)
1766 mat->resumeFill();
1767 continue;
1768 }
1769
1770 // The owned and ghosted containers hold the SAME matrix per block, and
1771 // AssemblyEngine::evaluate() calls beginFill() on both. FECrsMatrix::beginAssembly()
1772 // asserts its fill state is "closed", so the second call would throw; tracking which
1773 // matrix already has an assembly open per block collapses the pair into the single
1774 // begin the FE state machine expects. This cannot be asked of the matrix directly --
1775 // see feAssemblyOpenOn_.
1776 const std::pair<int,int> key(i,j);
1777 typename std::unordered_map<std::pair<int,int>,const FECrsMatrixType *,panzer::pair_hash>::const_iterator
1778 openItr = feAssemblyOpenOn_.find(key);
1779 if(openItr!=feAssemblyOpenOn_.end() && openItr->second==feMat.get())
1780 continue;
1781
1782 feMat->beginAssembly();
1783
1784 // Zero the block to start the assembly. The owned+shared view is active here, so this
1785 // is the one point where a single call reaches every row: between assemblies the
1786 // matrix rests in its OWNED view, whose values alias only the leading chunk of the
1787 // owned+shared array (see Tpetra_FECrsMatrix_def.hpp, "we'll grab the first chunk of
1788 // the Owned+Shared matrix's values array"), so a caller's setAllToScalar never touches
1789 // the ghost rows. endAssembly() does not clear them either, being a combining
1790 // self-export that leaves its source untouched, so without this the next assembly sums
1791 // onto the previous one's ghost contributions and inflates every shared-interface dof.
1792 feMat->setAllToScalar(0.0);
1793
1794 feAssemblyOpenOn_[key] = feMat.get();
1795 }
1796 }
1797}
1798
1799template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1801endFill(LinearObjContainer & ghostContainer,const LinearObjContainer & container) const
1802{
1803 endFill(ghostContainer);
1804
1805 // Give back what beginFill(ghostContainer,container) lent -- see the flat factory for why
1806 // this is guarded on identity rather than just on useFEAssembly_.
1807 if(useFEAssembly_) {
1808 BTLOC & ghostedLoc = Teuchos::dyn_cast<BTLOC>(ghostContainer);
1809 const BTLOC & ownedLoc = Teuchos::dyn_cast<const BTLOC>(container);
1810 if(ghostedLoc.get_A()!=Teuchos::null && ghostedLoc.get_A().get()==ownedLoc.get_A().get())
1811 ghostedLoc.set_A(Teuchos::null);
1812 }
1813}
1814
1815template <typename Traits,typename ScalarT,typename LocalOrdinalT,typename GlobalOrdinalT,typename NodeT>
1817endFill(LinearObjContainer & loc) const
1818{
1819 using Teuchos::RCP;
1820 using Teuchos::rcp_dynamic_cast;
1821 using Thyra::PhysicallyBlockedLinearOpBase;
1822
1823 BTLOC & tloc = Teuchos::dyn_cast<BTLOC>(loc);
1824 if(tloc.get_A()==Teuchos::null)
1825 return;
1826
1827 if(!useFEAssembly_) {
1828 tloc.endFill();
1829 return;
1830 }
1831
1832 RCP<PhysicallyBlockedLinearOpBase<ScalarT> > Amat
1833 = rcp_dynamic_cast<PhysicallyBlockedLinearOpBase<ScalarT> >(tloc.get_A(),true);
1834
1835 const int blockDim = static_cast<int>(gidProviders_.size());
1836 for(int i=0;i<blockDim;i++) {
1837 for(int j=0;j<blockDim;j++) {
1838 RCP<CrsMatrixType> mat
1839 = blocked_tpetra_lof_detail::getBlockAsCrsMatrix<ScalarT,LocalOrdinalT,GlobalOrdinalT,NodeT>(*Amat,i,j);
1840 if(mat==Teuchos::null)
1841 continue;
1842
1843 RCP<FECrsMatrixType> feMat = rcp_dynamic_cast<FECrsMatrixType>(mat);
1844 if(feMat==Teuchos::null) {
1845 mat->fillComplete(getMap(j),getMap(i));
1846 continue;
1847 }
1848
1849 // See beginFill(): must go through endAssembly(), not the plain fillComplete(), so the
1850 // owned+shared -> owned cross-rank merge happens. This single endAssembly() IS the
1851 // ghost->global migration for this block, which is why ghostToGlobalThyraMatrix()
1852 // skips the export for shared FE blocks.
1853 //
1854 // Mirror of the beginFill() guard: endFill() is likewise called on both containers
1855 // holding the same matrix, and endAssembly() asserts its fill state is "open", so only
1856 // the first call may run it.
1857 const std::pair<int,int> key(i,j);
1858 typename std::unordered_map<std::pair<int,int>,const FECrsMatrixType *,panzer::pair_hash>::const_iterator
1859 openItr = feAssemblyOpenOn_.find(key);
1860 if(openItr!=feAssemblyOpenOn_.end() && openItr->second==feMat.get()) {
1861 feMat->endAssembly();
1862 feAssemblyOpenOn_.erase(key);
1863 }
1864 }
1865 }
1866}
1867
1868}
1869
1870#endif // __Panzer_BlockedTpetraLinearObjFactory_impl_hpp__
PHX::MDField< ScalarT, panzer::Cell, panzer::IP > result
A field that will be used to build up the result of the integral we're performing.
void set_A(const Teuchos::RCP< CrsMatrixType > &in)
virtual Teuchos::RCP< const MapType > buildColTpetraGhostedMap(int i) const
Teuchos::RCP< FECrsGraphType > getFEGraph(int i, int j) const
Get the (cached) FE graph for block (i,j), built via the Tpetra::FECrsGraph "V2" ctor.
Teuchos::RCP< FECrsMatrixType > getFEMatrix(int i, int j) const
Get the (cached) FECrsMatrix for block (i,j).
Teuchos::RCP< Thyra::VectorBase< ScalarT > > getThyraRangeVector() const
Get a range vector.
virtual void adjustForDirichletConditions(const LinearObjContainer &localBCRows, const LinearObjContainer &globalBCRows, LinearObjContainer &ghostedObjs, bool zeroVectorRows=false, bool adjustX=false) const
virtual void applyDirichletBCs(const LinearObjContainer &counter, LinearObjContainer &result) const
std::vector< Teuchos::RCP< const GlobalIndexer > > gidProviders_
void initializeGhostedContainer(int, LinearObjContainer &loc) const
Teuchos::RCP< Thyra::VectorBase< ScalarT > > getThyraDomainVector() const
Get a domain vector.
virtual void endFill(LinearObjContainer &loc) const
Close a container after filling. Takes either a ghosted or an owned container.
virtual Teuchos::RCP< FECrsGraphType > buildFEGraph(int i, int j) const
Teuchos::RCP< const Thyra::VectorSpaceBase< ScalarT > > getGhostedThyraDomainSpace() const
Get the domain vector space (x and dxdt)
std::vector< Teuchos::RCP< const GlobalIndexer > > colGidProviders_
Teuchos::RCP< const BlockedDOFManager > blockedDOFManager_
virtual Teuchos::RCP< const CrsGraphType > getGhostedGraph(int i, int j) const
get the ghosted graph of the crs matrix
virtual Teuchos::RCP< const MapType > getMap(int i) const
get the map from the matrix
Teuchos::RCP< const GlobalIndexer > getColGlobalIndexer(int i) const
virtual Teuchos::RCP< const ImportType > getGhostedColImport(int i) const
virtual void ghostToGlobalContainer(const LinearObjContainer &ghostContainer, LinearObjContainer &container, int) const
virtual void globalToGhostContainer(const LinearObjContainer &container, LinearObjContainer &ghostContainer, int) const
BlockedTpetraLinearObjFactory(const Teuchos::RCP< const Teuchos::MpiComm< int > > &comm, const Teuchos::RCP< const BlockedDOFManager > &gidProvider, bool useFEAssembly=false)
virtual Teuchos::RCP< const MapType > buildTpetraGhostedMap(int i) const
void globalToGhostThyraVector(const Teuchos::RCP< const Thyra::VectorBase< ScalarT > > &in, const Teuchos::RCP< Thyra::VectorBase< ScalarT > > &out, bool col) const
virtual Teuchos::RCP< const MapType > buildTpetraMap(int i) const
virtual Teuchos::RCP< const CrsGraphType > buildTpetraGraph(int i, int j) const
Teuchos::RCP< VectorType > getGhostedTpetraRangeVector(int i) const
Teuchos::RCP< const Thyra::VectorSpaceBase< ScalarT > > getThyraDomainSpace() const
Get the domain vector space (x and dxdt)
virtual Teuchos::RCP< const MapType > getColMap(int i) const
Teuchos::RCP< Thyra::VectorBase< ScalarT > > getGhostedThyraRangeVector() const
Get a range vector.
Tpetra::CrsGraph< LocalOrdinalT, GlobalOrdinalT, NodeT > CrsGraphType
Tpetra::Export< LocalOrdinalT, GlobalOrdinalT, NodeT > ExportType
virtual Teuchos::RCP< const CrsGraphType > buildTpetraGhostedGraph(int i, int j) const
virtual Teuchos::RCP< const MapType > getGhostedMap(int i) const
get the ghosted map from the matrix
Tpetra::Vector< ScalarT, LocalOrdinalT, GlobalOrdinalT, NodeT > VectorType
Teuchos::RCP< VectorType > getGhostedTpetraDomainVector(int i) const
Teuchos::RCP< const Thyra::VectorSpaceBase< ScalarT > > getGhostedThyraRangeSpace() const
Get the range vector space (f)
void makeRoomForBlocks(std::size_t blockCnt, std::size_t colBlockCnt=0)
Allocate the space in the std::vector objects so we can fill with appropriate Tpetra data.
bool useFEAssembly() const
True if this factory was constructed with FE assembly enabled.
void ghostToGlobalTpetraVector(int i, const VectorType &in, VectorType &out, bool col) const
void globalToGhostTpetraVector(int i, const VectorType &in, VectorType &out, bool col) const
virtual void beginFill(LinearObjContainer &loc) const
Open a container for filling. Takes either a ghosted or an owned container.
Teuchos::RCP< CrsMatrixType > getTpetraMatrix(int i, int j) const
virtual Teuchos::RCP< LinearObjContainer > buildGhostedLinearObjContainer() const
virtual Teuchos::RCP< LinearObjContainer > buildLinearObjContainer() const
Tpetra::FECrsGraph< LocalOrdinalT, GlobalOrdinalT, NodeT > FECrsGraphType
void addExcludedPair(int rowBlock, int colBlock)
exclude a block pair from the matrix
void ghostToGlobalThyraMatrix(const Thyra::LinearOpBase< ScalarT > &in, Thyra::LinearOpBase< ScalarT > &out) const
void ghostToGlobalTpetraMatrix(int blockRow, const CrsMatrixType &in, CrsMatrixType &out) const
Tpetra::CrsMatrix< ScalarT, LocalOrdinalT, GlobalOrdinalT, NodeT > CrsMatrixType
Tpetra::Map< LocalOrdinalT, GlobalOrdinalT, NodeT > MapType
Teuchos::RCP< const Thyra::VectorSpaceBase< ScalarT > > getThyraRangeSpace() const
Get the range vector space (f)
static void splitIntoBlocks(const Teuchos::RCP< const GlobalIndexer > &ugi, Teuchos::RCP< const BlockedDOFManager > &blocked, std::vector< Teuchos::RCP< const GlobalIndexer > > &blocks)
virtual Teuchos::RCP< const MapType > buildColTpetraMap(int i) const
Teuchos::RCP< CrsMatrixType > getGhostedTpetraMatrix(int i, int j) const
virtual Teuchos::RCP< const ImportType > getGhostedImport(int i) const
get importer for converting an overalapped object to a "normal" object
Tpetra::Import< LocalOrdinalT, GlobalOrdinalT, NodeT > ImportType
Teuchos::RCP< const BlockedDOFManager > colBlockedDOFManager_
void addExcludedPairs(const std::vector< std::pair< int, int > > &exPairs)
exclude a vector of pairs from the matrix
virtual Teuchos::RCP< const MapType > getGhostedColMap(int i) const
virtual Teuchos::RCP< const ExportType > getGhostedColExport(int i) const
void ghostToGlobalThyraVector(const Teuchos::RCP< const Thyra::VectorBase< ScalarT > > &in, const Teuchos::RCP< Thyra::VectorBase< ScalarT > > &out, bool col) const
Tpetra::FECrsMatrix< ScalarT, LocalOrdinalT, GlobalOrdinalT, NodeT > FECrsMatrixType
virtual Teuchos::RCP< const ExportType > getGhostedExport(int j) const
get exporter for converting an overalapped object to a "normal" object
Teuchos::RCP< Thyra::LinearOpBase< ScalarT > > getThyraMatrix() const
Get a Thyra operator.
Teuchos::RCP< Thyra::VectorBase< ScalarT > > getGhostedThyraDomainVector() const
Get a domain vector.
virtual Teuchos::RCP< const CrsGraphType > getGraph(int i, int j) const
get the graph of the crs matrix
Teuchos::RCP< const panzer::BlockedDOFManager > getGlobalIndexer() const
Teuchos::RCP< Thyra::BlockedLinearOpBase< ScalarT > > getGhostedThyraMatrix() const
Get a Thyra operator.
This class encapsulates the needs of a gather operation to do a halo exchange for blocked vectors.
This class provides a boundary exchange communication mechanism for vectors.
Abstract, linear-algebra-library-agnostic container for the vectors and matrix used in a nonlinear/tr...
void buildGatherScatterEvaluators(const BuilderT &builder)
Teuchos::RCP< Tpetra::CrsMatrix< ScalarT, LocalOrdinalT, GlobalOrdinalT, NodeT > > getBlockAsCrsMatrix(Thyra::PhysicallyBlockedLinearOpBase< ScalarT > &Amat, int i, int j)
Panzer's specialization of the Phalanx traits class.
A hash functor for std::pair, combining the hashes of both elements via hash_combine()....