Panzer  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Panzer_ScatterResidual_BlockedTpetra_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_SCATTER_RESIDUAL_BLOCKEDEPETRA_IMPL_HPP
12 #define PANZER_SCATTER_RESIDUAL_BLOCKEDEPETRA_IMPL_HPP
13 
14 #include "Teuchos_RCP.hpp"
15 #include "Teuchos_Assert.hpp"
16 
17 #include "Phalanx_DataLayout.hpp"
18 
19 #include "Panzer_GlobalIndexer.hpp"
21 #include "Panzer_PureBasis.hpp"
24 #include "Panzer_HashUtils.hpp"
27 
28 #include "Thyra_ProductVectorBase.hpp"
29 #include "Thyra_BlockedLinearOpBase.hpp"
30 #include "Thyra_TpetraVector.hpp"
31 #include "Thyra_TpetraLinearOp.hpp"
32 #include "Tpetra_CrsMatrix.hpp"
33 #include "KokkosSparse_CrsMatrix.hpp"
34 
35 #include "Phalanx_DataLayout_MDALayout.hpp"
36 
37 #include "Teuchos_FancyOStream.hpp"
38 
39 template <typename EvalT,typename TRAITS,typename LO,typename GO,typename NodeT>
42  const Teuchos::ParameterList& p)
43 {
44  std::string scatterName = p.get<std::string>("Scatter Name");
45  Teuchos::RCP<PHX::FieldTag> scatterHolder =
47 
48  // get names to be evaluated
49  const std::vector<std::string>& names =
50  *(p.get< Teuchos::RCP< std::vector<std::string> > >("Dependent Names"));
51 
53  p.get< Teuchos::RCP<const panzer::PureBasis> >("Basis")->functional;
54 
55  // build the vector of fields that this is dependent on
56  for (std::size_t eq = 0; eq < names.size(); ++eq) {
58 
59  // tell the field manager that we depend on this field
60  this->addDependentField(field.fieldTag());
61  }
62 
63  // this is what this evaluator provides
64  this->addEvaluatedField(*scatterHolder);
65 
66  this->setName(scatterName+" Scatter Residual");
67 }
68 
69 // **********************************************************************
70 // Specialization: Residual
71 // **********************************************************************
72 
73 template <typename TRAITS,typename LO,typename GO,typename NodeT>
76  const Teuchos::ParameterList& p)
77  : globalIndexer_(indexer)
78  , globalDataKey_("Residual Scatter Container")
79 {
80  std::string scatterName = p.get<std::string>("Scatter Name");
81  scatterHolder_ =
83 
84  // get names to be evaluated
85  const std::vector<std::string>& names =
86  *(p.get< Teuchos::RCP< std::vector<std::string> > >("Dependent Names"));
87 
88  // grab map from evaluated names to field names
89  fieldMap_ = p.get< Teuchos::RCP< std::map<std::string,std::string> > >("Dependent Map");
90 
92  p.get< Teuchos::RCP<const panzer::PureBasis> >("Basis")->functional;
93 
94  // build the vector of fields that this is dependent on
95  scatterFields_.resize(names.size());
96  for (std::size_t eq = 0; eq < names.size(); ++eq) {
97  scatterFields_[eq] = PHX::MDField<const ScalarT,Cell,NODE>(names[eq],dl);
98 
99  // tell the field manager that we depend on this field
100  this->addDependentField(scatterFields_[eq]);
101  }
102 
103  // this is what this evaluator provides
104  this->addEvaluatedField(*scatterHolder_);
105 
106  if (p.isType<std::string>("Global Data Key"))
107  globalDataKey_ = p.get<std::string>("Global Data Key");
108 
109  this->setName(scatterName+" Scatter Residual");
110 }
111 
112 // **********************************************************************
113 template <typename TRAITS,typename LO,typename GO,typename NodeT>
115 postRegistrationSetup(typename TRAITS::SetupData d,
116  PHX::FieldManager<TRAITS>& /* fm */)
117 {
118  const Workset & workset_0 = (*d.worksets_)[0];
119  const std::string blockId = this->wda(workset_0).block_id;
120 
121  fieldIds_.resize(scatterFields_.size());
122  fieldOffsets_.resize(scatterFields_.size());
123  fieldGlobalIndexers_.resize(scatterFields_.size());
124  productVectorBlockIndex_.resize(scatterFields_.size());
125  int maxElementBlockGIDCount = -1;
126  for(std::size_t fd=0; fd < scatterFields_.size(); ++fd) {
127  const std::string fieldName = fieldMap_->find(scatterFields_[fd].fieldTag().name())->second;
128  const int globalFieldNum = globalIndexer_->getFieldNum(fieldName); // Field number in the aggregate BlockDOFManager
129  productVectorBlockIndex_[fd] = globalIndexer_->getFieldBlock(globalFieldNum);
130  fieldGlobalIndexers_[fd] = globalIndexer_->getFieldDOFManagers()[productVectorBlockIndex_[fd]];
131  fieldIds_[fd] = fieldGlobalIndexers_[fd]->getFieldNum(fieldName); // Field number in the sub-global-indexer
132 
133  const std::vector<int>& offsets = fieldGlobalIndexers_[fd]->getGIDFieldOffsets(blockId,fieldIds_[fd]);
134  fieldOffsets_[fd] = PHX::View<int*>("ScatterResidual_BlockedTpetra(Residual):fieldOffsets",offsets.size());
135  auto hostOffsets = Kokkos::create_mirror_view(fieldOffsets_[fd]);
136  for (std::size_t i=0; i < offsets.size(); ++i)
137  hostOffsets(i) = offsets[i];
138  Kokkos::deep_copy(fieldOffsets_[fd], hostOffsets);
139 
140  maxElementBlockGIDCount = std::max(fieldGlobalIndexers_[fd]->getElementBlockGIDCount(blockId),maxElementBlockGIDCount);
141  }
142 
143  // We will use one workset lid view for all fields, but has to be
144  // sized big enough to hold the largest elementBlockGIDCount in the
145  // ProductVector.
146  worksetLIDs_ = PHX::View<LO**>("ScatterResidual_BlockedTpetra(Residual):worksetLIDs",
147  scatterFields_[0].extent(0),
148  maxElementBlockGIDCount);
149 }
150 
151 // **********************************************************************
152 template <typename TRAITS,typename LO,typename GO,typename NodeT>
154 preEvaluate(typename TRAITS::PreEvalData d)
155 {
156  using Teuchos::RCP;
157  using Teuchos::rcp_dynamic_cast;
158 
159  // extract linear object container
160  blockedContainer_ = rcp_dynamic_cast<const ContainerType>(d.gedc->getDataObject(globalDataKey_));
161 
162  if(blockedContainer_==Teuchos::null) {
163  RCP<const LOCPair_GlobalEvaluationData> gdata = rcp_dynamic_cast<const LOCPair_GlobalEvaluationData>(d.gedc->getDataObject(globalDataKey_),true);
164  blockedContainer_ = rcp_dynamic_cast<const ContainerType>(gdata->getGhostedLOC());
165  }
166 }
167 
168 // **********************************************************************
169 template <typename TRAITS,typename LO,typename GO,typename NodeT>
171 evaluateFields(typename TRAITS::EvalData workset)
172 {
173  using Teuchos::RCP;
174  using Teuchos::rcp_dynamic_cast;
175  using Thyra::VectorBase;
177 
178  const auto& localCellIds = this->wda(workset).cell_local_ids_k;
179  const RCP<ProductVectorBase<double>> thyraBlockResidual = rcp_dynamic_cast<ProductVectorBase<double> >(blockedContainer_->get_f(),true);
180 
181  // Loop over scattered fields
182  int currentWorksetLIDSubBlock = -1;
183  for (std::size_t fieldIndex = 0; fieldIndex < scatterFields_.size(); fieldIndex++) {
184  // workset LIDs only change for different sub blocks
185  if (productVectorBlockIndex_[fieldIndex] != currentWorksetLIDSubBlock) {
186  fieldGlobalIndexers_[fieldIndex]->getElementLIDs(localCellIds,worksetLIDs_);
187  currentWorksetLIDSubBlock = productVectorBlockIndex_[fieldIndex];
188  }
189 
190  auto& tpetraResidual = *((rcp_dynamic_cast<Thyra::TpetraVector<RealType,LO,GO,NodeT>>(thyraBlockResidual->getNonconstVectorBlock(productVectorBlockIndex_[fieldIndex]),true))->getTpetraVector());
191  const auto& kokkosResidual = tpetraResidual.getLocalViewDevice(Tpetra::Access::ReadWrite);
192 
193  // Class data fields for lambda capture
194  const auto& fieldOffsets = fieldOffsets_[fieldIndex];
195  const auto& worksetLIDs = worksetLIDs_;
196  const auto& fieldValues = scatterFields_[fieldIndex].get_static_view();
197 
198  Kokkos::parallel_for(Kokkos::RangePolicy<PHX::Device>(0,workset.num_cells), KOKKOS_LAMBDA (const int& cell) {
199  for(int basis=0; basis < static_cast<int>(fieldOffsets.size()); ++basis) {
200  const int lid = worksetLIDs(cell,fieldOffsets(basis));
201  Kokkos::atomic_add(&kokkosResidual(lid,0), fieldValues(cell,basis));
202  }
203  });
204  }
205 }
206 
207 // **********************************************************************
208 // Specialization: Jacobian
209 // **********************************************************************
210 
211 template <typename TRAITS,typename LO,typename GO,typename NodeT>
214  const Teuchos::ParameterList& p)
215  : globalIndexer_(indexer)
216  , globalDataKey_("Residual Scatter Container")
217 {
218  std::string scatterName = p.get<std::string>("Scatter Name");
219  scatterHolder_ =
221 
222  // get names to be evaluated
223  const std::vector<std::string>& names =
224  *(p.get< Teuchos::RCP< std::vector<std::string> > >("Dependent Names"));
225 
226  // grab map from evaluated names to field names
227  fieldMap_ = p.get< Teuchos::RCP< std::map<std::string,std::string> > >("Dependent Map");
228 
230  p.get< Teuchos::RCP<const panzer::PureBasis> >("Basis")->functional;
231 
232  // build the vector of fields that this is dependent on
233  scatterFields_.resize(names.size());
234  for (std::size_t eq = 0; eq < names.size(); ++eq) {
235  scatterFields_[eq] = PHX::MDField<const ScalarT,Cell,NODE>(names[eq],dl);
236 
237  // tell the field manager that we depend on this field
238  this->addDependentField(scatterFields_[eq]);
239  }
240 
241  // this is what this evaluator provides
242  this->addEvaluatedField(*scatterHolder_);
243 
244  if (p.isType<std::string>("Global Data Key"))
245  globalDataKey_ = p.get<std::string>("Global Data Key");
246 
247  this->setName(scatterName+" Scatter Residual (Jacobian)");
248 }
249 
250 // **********************************************************************
251 template <typename TRAITS,typename LO,typename GO,typename NodeT>
253 postRegistrationSetup(typename TRAITS::SetupData d,
254  PHX::FieldManager<TRAITS>& /* fm */)
255 {
256  const Workset & workset_0 = (*d.worksets_)[0];
257  const std::string blockId = this->wda(workset_0).block_id;
258 
259  fieldIds_.resize(scatterFields_.size());
260  fieldOffsets_.resize(scatterFields_.size());
261  productVectorBlockIndex_.resize(scatterFields_.size());
262  for (std::size_t fd=0; fd < scatterFields_.size(); ++fd) {
263  const std::string fieldName = fieldMap_->find(scatterFields_[fd].fieldTag().name())->second;
264  const int globalFieldNum = globalIndexer_->getFieldNum(fieldName); // Field number in the aggregate BlockDOFManager
265  productVectorBlockIndex_[fd] = globalIndexer_->getFieldBlock(globalFieldNum);
266  const auto& fieldGlobalIndexer = globalIndexer_->getFieldDOFManagers()[productVectorBlockIndex_[fd]];
267  fieldIds_[fd] = fieldGlobalIndexer->getFieldNum(fieldName); // Field number in the sub-global-indexer
268 
269  const std::vector<int>& offsets = globalIndexer_->getGIDFieldOffsets(blockId,globalFieldNum);
270  fieldOffsets_[fd] = PHX::View<int*>("ScatterResidual_BlockedTpetra(Jacobian):fieldOffsets",offsets.size());
271  auto hostOffsets = Kokkos::create_mirror_view(fieldOffsets_[fd]);
272  for (std::size_t i=0; i < offsets.size(); ++i)
273  hostOffsets(i) = offsets[i];
274  Kokkos::deep_copy(fieldOffsets_[fd], hostOffsets);
275  }
276 
277  // This is sized differently than the Residual implementation since
278  // we need the LIDs for all sub-blocks, not just the single
279  // sub-block for the field residual scatter.
280  int elementBlockGIDCount = 0;
281  for (const auto& blockDOFMgr : globalIndexer_->getFieldDOFManagers())
282  elementBlockGIDCount += blockDOFMgr->getElementBlockGIDCount(blockId);
283 
285  "ScatterResidual_BlockedTpetra(Jacobian):worksetLIDs_",
286  scatterFields_[0].extent(0), elementBlockGIDCount );
287 
288  // Compute the block offsets
289  const auto& blockGlobalIndexers = globalIndexer_->getFieldDOFManagers();
290  const int numBlocks = static_cast<int>(globalIndexer_->getFieldDOFManagers().size());
291  blockOffsets_ = PHX::View<LO*>("ScatterResidual_BlockedTpetra(Jacobian):blockOffsets_",
292  numBlocks+1); // Number of fields, plus a sentinel
293  const auto hostBlockOffsets = Kokkos::create_mirror_view(blockOffsets_);
294  for (int blk=0;blk<numBlocks;blk++) {
295  int blockOffset = globalIndexer_->getBlockGIDOffset(blockId,blk);
296  hostBlockOffsets(blk) = blockOffset;
297  }
298  hostBlockOffsets(numBlocks) = hostBlockOffsets(numBlocks-1) + blockGlobalIndexers[blockGlobalIndexers.size()-1]->getElementBlockGIDCount(blockId);
299  Kokkos::deep_copy(blockOffsets_,hostBlockOffsets);
300 
301  // Make sure the that derivative dimension in the evaluate call is large
302  // enough to hold all derivatives for each sub block load
303  int max_blockDerivativeSize = 0;
304  for (int blk=0;blk<numBlocks;blk++) {
305  const int blockDerivativeSize = hostBlockOffsets(blk+1) - hostBlockOffsets(blk);
306  if ( blockDerivativeSize > max_blockDerivativeSize )
307  max_blockDerivativeSize = blockDerivativeSize;
308  }
309  workset_vals_ = Kokkos::View<typename Sacado::ScalarType<ScalarT>::type**, Kokkos::LayoutRight, PHX::Device>(
310  "ScatterResidual_BlockedTpetra(Jacobian):workset_vals_",
311  scatterFields_[0].extent(0), max_blockDerivativeSize );
312 }
313 
314 // **********************************************************************
315 template <typename TRAITS,typename LO,typename GO,typename NodeT>
317 preEvaluate(typename TRAITS::PreEvalData d)
318 {
319  using Teuchos::RCP;
320  using Teuchos::rcp_dynamic_cast;
321 
322  // extract linear object container
323  blockedContainer_ = rcp_dynamic_cast<const ContainerType>(d.gedc->getDataObject(globalDataKey_));
324 
325  if(blockedContainer_==Teuchos::null) {
326  RCP<const LOCPair_GlobalEvaluationData> gdata = rcp_dynamic_cast<const LOCPair_GlobalEvaluationData>(d.gedc->getDataObject(globalDataKey_),true);
327  blockedContainer_ = rcp_dynamic_cast<const ContainerType>(gdata->getGhostedLOC());
328  }
329 }
330 
331 // **********************************************************************
332 template <typename TRAITS,typename LO,typename GO,typename NodeT>
334 evaluateFields(typename TRAITS::EvalData workset)
335 {
336  using Teuchos::RCP;
337  using Teuchos::rcp_dynamic_cast;
338  using Thyra::VectorBase;
341 
342  const auto& localCellIds = this->wda(workset).cell_local_ids_k;
343 
344  const int numFieldBlocks = globalIndexer_->getNumFieldBlocks();
345  const RCP<const ContainerType> blockedContainer = blockedContainer_;
346  const RCP<ProductVectorBase<double>> thyraBlockResidual = rcp_dynamic_cast<ProductVectorBase<double> >(blockedContainer_->get_f());
347  const bool haveResidual = Teuchos::nonnull(thyraBlockResidual);
348  const RCP<BlockedLinearOpBase<double>> Jac = rcp_dynamic_cast<BlockedLinearOpBase<double> >(blockedContainer_->get_A(),true);
349 
350  // Get the local data for the sub-block crs matrices. First allocate
351  // on host and then deep_copy to device. The sub-blocks are
352  // unmanaged since they are allocated and ref counted separately on
353  // host.
354  using LocalMatrixType = KokkosSparse::CrsMatrix<double,LO,PHX::Device,Kokkos::MemoryTraits<Kokkos::Unmanaged>, size_t>;
355  typename PHX::View<LocalMatrixType**>::HostMirror
356  hostJacTpetraBlocks("panzer::ScatterResidual_BlockTpetra<Jacobian>::hostJacTpetraBlocks", numFieldBlocks,numFieldBlocks);
357 
358  PHX::View<int**> blockExistsInJac = PHX::View<int**>("blockExistsInJac_",numFieldBlocks,numFieldBlocks);
359  auto hostBlockExistsInJac = Kokkos::create_mirror_view(blockExistsInJac);
360 
361  for (int row=0; row < numFieldBlocks; ++row) {
362  for (int col=0; col < numFieldBlocks; ++col) {
363  const auto thyraTpetraOperator = rcp_dynamic_cast<Thyra::TpetraLinearOp<double,LO,GO,NodeT>>(Jac->getNonconstBlock(row,col),false);
364  if (nonnull(thyraTpetraOperator)) {
365 
366  // HACK to enforce views in the CrsGraph to be
367  // Unmanaged. Passing in the MemoryTrait<Unmanaged> doesn't
368  // work as the CrsGraph in the CrsMatrix ignores the
369  // MemoryTrait. Need to use the runtime constructor by passing
370  // in points to ensure Unmanaged. See:
371  // https://github.com/kokkos/kokkos/issues/1581
372 
373  // These two lines are the original code we can revert to when #1581 is fixed.
374  // const auto crsMatrix = rcp_dynamic_cast<Tpetra::CrsMatrix<double,LO,GO,NodeT>>(thyraTpetraOperator->getTpetraOperator(),true);
375  // new (&hostJacTpetraBlocks(row,col)) KokkosSparse::CrsMatrix<double,LO,PHX::Device,Kokkos::MemoryTraits<Kokkos::Unmanaged>> (crsMatrix->getLocalMatrix());
376 
377  // Instead do this
378  {
379  // Grab the local managed matrix and graph
380  const auto tpetraCrsMatrix = rcp_dynamic_cast<Tpetra::CrsMatrix<double,LO,GO,NodeT>>(thyraTpetraOperator->getTpetraOperator(),true);
381  const auto managedMatrix = tpetraCrsMatrix->getLocalMatrixDevice();
382  const auto managedGraph = managedMatrix.graph;
383 
384  // Create runtime unmanaged versions
385  using StaticCrsGraphType = typename LocalMatrixType::StaticCrsGraphType;
386  StaticCrsGraphType unmanagedGraph;
387  unmanagedGraph.entries = typename StaticCrsGraphType::entries_type(managedGraph.entries.data(),managedGraph.entries.extent(0));
388  unmanagedGraph.row_map = typename StaticCrsGraphType::row_map_type(managedGraph.row_map.data(),managedGraph.row_map.extent(0));
389  unmanagedGraph.row_block_offsets = typename StaticCrsGraphType::row_block_type(managedGraph.row_block_offsets.data(),managedGraph.row_block_offsets.extent(0));
390 
391  typename LocalMatrixType::values_type unmanagedValues(managedMatrix.values.data(),managedMatrix.values.extent(0));
392  LocalMatrixType unmanagedMatrix(managedMatrix.values.label(), managedMatrix.numCols(), unmanagedValues, unmanagedGraph);
393  new (&hostJacTpetraBlocks(row,col)) LocalMatrixType(unmanagedMatrix);
394  }
395 
396  hostBlockExistsInJac(row,col) = 1;
397  }
398  else {
399  hostBlockExistsInJac(row,col) = 0;
400  }
401  }
402  }
403  typename PHX::View<LocalMatrixType**>
404  jacTpetraBlocks("panzer::ScatterResidual_BlockedTpetra<Jacobian>::jacTpetraBlocks",numFieldBlocks,numFieldBlocks);
405  Kokkos::deep_copy(jacTpetraBlocks,hostJacTpetraBlocks);
406  Kokkos::deep_copy(blockExistsInJac,hostBlockExistsInJac);
407 
408  // worksetLIDs is larger for Jacobian than Residual fill. Need the
409  // entire set of field offsets for derivative indexing no matter
410  // which block row you are scattering. The residual only needs the
411  // lids for the sub-block that it is scattering to. The subviews
412  // below are to offset the LID blocks correctly.
413  const auto& globalIndexers = globalIndexer_->getFieldDOFManagers();
414  auto blockOffsets_h = Kokkos::create_mirror_view(blockOffsets_);
415  Kokkos::deep_copy(blockOffsets_h, blockOffsets_);
416  for (size_t block=0; block < globalIndexers.size(); ++block) {
417  const auto subviewOfBlockLIDs = Kokkos::subview(worksetLIDs_,Kokkos::ALL(), std::make_pair(blockOffsets_h(block),blockOffsets_h(block+1)));
418  globalIndexers[block]->getElementLIDs(localCellIds,subviewOfBlockLIDs);
419  }
420 
421  // Loop over scattered fields
422  for (std::size_t fieldIndex = 0; fieldIndex < scatterFields_.size(); fieldIndex++) {
423 
424  const int blockRowIndex = productVectorBlockIndex_[fieldIndex];
425  typename Tpetra::Vector<double,LO,GO,PHX::Device>::dual_view_type::t_dev kokkosResidual;
426  if (haveResidual) {
427  auto& tpetraResidual = *((rcp_dynamic_cast<Thyra::TpetraVector<RealType,LO,GO,NodeT>>(thyraBlockResidual->getNonconstVectorBlock(blockRowIndex),true))->getTpetraVector());
428  kokkosResidual = tpetraResidual.getLocalViewDevice(Tpetra::Access::ReadWrite);
429  }
430 
431  // Class data fields for lambda capture
432  const PHX::View<const int*> fieldOffsets = fieldOffsets_[fieldIndex];
433  const PHX::View<const ScalarT**> fieldValues = scatterFields_[fieldIndex].get_static_view();
434  const PHX::View<const LO*> blockOffsets = blockOffsets_;
435 
436  const Kokkos::View<const LO**, Kokkos::LayoutRight, PHX::Device> worksetLIDs = worksetLIDs_;
437  Kokkos::View<typename Sacado::ScalarType<ScalarT>::type**, Kokkos::LayoutRight, PHX::Device> workset_vals = workset_vals_;
438 
439  Kokkos::parallel_for(Kokkos::RangePolicy<PHX::Device>(0,workset.num_cells), KOKKOS_LAMBDA (const int& cell) {
440  for(int basis=0; basis < static_cast<int>(fieldOffsets.size()); ++basis) {
442  typename FieldType::array_type::reference_type tmpFieldVal = fieldValues(cell,basis);
443  const int rowLID = worksetLIDs(cell,fieldOffsets(basis));
444 
445  if (haveResidual)
446  Kokkos::atomic_add(&kokkosResidual(rowLID,0), tmpFieldVal.val());
447 
448  for (int blockColIndex=0; blockColIndex < numFieldBlocks; ++blockColIndex) {
449  if (blockExistsInJac(blockRowIndex,blockColIndex)) {
450  const int start = blockOffsets(blockColIndex);
451  const int stop = blockOffsets(blockColIndex+1);
452  const int sensSize = stop-start;
453 
454  for (int i=0; i < sensSize; ++i)
455  workset_vals(cell,i) = tmpFieldVal.fastAccessDx(start+i);
456 
457  jacTpetraBlocks(blockRowIndex,blockColIndex).sumIntoValues(rowLID, &worksetLIDs(cell,start), sensSize, &workset_vals(cell,0), true,true);
458  }
459  }
460  }
461  });
462 
463  }
464 
465  // Placement delete on view of matrices
466  for (int row=0; row < numFieldBlocks; ++row) {
467  for (int col=0; col < numFieldBlocks; ++col) {
468  if (hostBlockExistsInJac(row,col)) {
469  hostJacTpetraBlocks(row,col).~CrsMatrix();
470  }
471  }
472  }
473 
474 }
475 
476 // **********************************************************************
477 // Specialization: Tangent
478 // **********************************************************************
479 
480 template <typename TRAITS,typename LO,typename GO,typename NodeT>
483  const Teuchos::ParameterList& p)
484  : globalIndexer_(indexer)
485  , globalDataKey_("Residual Scatter Container")
486 {
487  std::string scatterName = p.get<std::string>("Scatter Name");
488  scatterHolder_ =
490 
491  // get names to be evaluated
492  const std::vector<std::string>& names =
493  *(p.get< Teuchos::RCP< std::vector<std::string> > >("Dependent Names"));
494 
495  // grab map from evaluated names to field names
496  fieldMap_ = p.get< Teuchos::RCP< std::map<std::string,std::string> > >("Dependent Map");
497 
499  p.get< Teuchos::RCP<const panzer::PureBasis> >("Basis")->functional;
500 
501  // build the vector of fields that this is dependent on
502  scatterFields_.resize(names.size());
503  for (std::size_t eq = 0; eq < names.size(); ++eq) {
504  scatterFields_[eq] = PHX::MDField<const ScalarT,Cell,NODE>(names[eq],dl);
505 
506  // tell the field manager that we depend on this field
507  this->addDependentField(scatterFields_[eq]);
508  }
509 
510  // this is what this evaluator provides
511  this->addEvaluatedField(*scatterHolder_);
512 
513  if (p.isType<std::string>("Global Data Key"))
514  globalDataKey_ = p.get<std::string>("Global Data Key");
515 
516  this->setName(scatterName+" Scatter Residual (Tangent)");
517 }
518 
519 // **********************************************************************
520 template <typename TRAITS,typename LO,typename GO,typename NodeT>
522 postRegistrationSetup(typename TRAITS::SetupData d,
523  PHX::FieldManager<TRAITS>& /* fm */)
524 {
525  const Workset & workset_0 = (*d.worksets_)[0];
526  const std::string blockId = this->wda(workset_0).block_id;
527 
528  fieldIds_.resize(scatterFields_.size());
529  fieldOffsets_.resize(scatterFields_.size());
530  fieldGlobalIndexers_.resize(scatterFields_.size());
531  productVectorBlockIndex_.resize(scatterFields_.size());
532  int maxElementBlockGIDCount = -1;
533  for(std::size_t fd=0; fd < scatterFields_.size(); ++fd) {
534  const std::string fieldName = fieldMap_->find(scatterFields_[fd].fieldTag().name())->second;
535  const int globalFieldNum = globalIndexer_->getFieldNum(fieldName); // Field number in the aggregate BlockDOFManager
536  productVectorBlockIndex_[fd] = globalIndexer_->getFieldBlock(globalFieldNum);
537  fieldGlobalIndexers_[fd] = globalIndexer_->getFieldDOFManagers()[productVectorBlockIndex_[fd]];
538  fieldIds_[fd] = fieldGlobalIndexers_[fd]->getFieldNum(fieldName); // Field number in the sub-global-indexer
539 
540  const std::vector<int>& offsets = fieldGlobalIndexers_[fd]->getGIDFieldOffsets(blockId,fieldIds_[fd]);
541  fieldOffsets_[fd] = PHX::View<int*>("ScatterResidual_BlockedTpetra(Tangent):fieldOffsets",offsets.size());
542  auto hostOffsets = Kokkos::create_mirror_view(fieldOffsets_[fd]);
543  for (std::size_t i=0; i < offsets.size(); ++i)
544  hostOffsets(i) = offsets[i];
545  Kokkos::deep_copy(fieldOffsets_[fd], hostOffsets);
546 
547  maxElementBlockGIDCount = std::max(fieldGlobalIndexers_[fd]->getElementBlockGIDCount(blockId),maxElementBlockGIDCount);
548  }
549 
550  // We will use one workset lid view for all fields, but has to be
551  // sized big enough to hold the largest elementBlockGIDCount in the
552  // ProductVector.
553  worksetLIDs_ = PHX::View<LO**>("ScatterResidual_BlockedTpetra(Tangent):worksetLIDs",
554  scatterFields_[0].extent(0),
555  maxElementBlockGIDCount);
556 }
557 
558 // **********************************************************************
559 template <typename TRAITS,typename LO,typename GO,typename NodeT>
561 preEvaluate(typename TRAITS::PreEvalData d)
562 {
563  using Teuchos::RCP;
564  using Teuchos::rcp_dynamic_cast;
566 
567  // this is the list of parameters and their names that this scatter has to account for
568  std::vector<std::string> activeParameters =
569  rcp_dynamic_cast<ParameterList_GlobalEvaluationData>(d.gedc->getDataObject("PARAMETER_NAMES"))->getActiveParameters();
570 
571  const int numBlocks = static_cast<int>(globalIndexer_->getFieldDOFManagers().size());
572 
573  dfdpFieldsVoV_.initialize("ScatterResidual_Tpetra<Tangent>::dfdpFieldsVoV_",activeParameters.size(),numBlocks);
574 
575  for(std::size_t i=0;i<activeParameters.size();i++) {
576  RCP<ContainerType> paramBlockedContainer = rcp_dynamic_cast<ContainerType>(d.gedc->getDataObject(activeParameters[i]),true);
577  RCP<ProductVectorBase<double>> productVector =
578  rcp_dynamic_cast<ProductVectorBase<double>>(paramBlockedContainer->get_f(),true);
579  for(int j=0;j<numBlocks;j++) {
580  auto& tpetraBlock = *((rcp_dynamic_cast<Thyra::TpetraVector<RealType,LO,GO,NodeT>>(productVector->getNonconstVectorBlock(j),true))->getTpetraVector());
581  const auto& dfdp_view = tpetraBlock.getLocalViewDevice(Tpetra::Access::ReadWrite);
582  dfdpFieldsVoV_.addView(dfdp_view,i,j);
583  }
584  }
585 
586  dfdpFieldsVoV_.syncHostToDevice();
587 
588  // extract linear object container
589  blockedContainer_ = rcp_dynamic_cast<const ContainerType>(d.gedc->getDataObject(globalDataKey_));
590 
591  if(blockedContainer_==Teuchos::null) {
592  RCP<const LOCPair_GlobalEvaluationData> gdata = rcp_dynamic_cast<const LOCPair_GlobalEvaluationData>(d.gedc->getDataObject(globalDataKey_),true);
593  blockedContainer_ = rcp_dynamic_cast<const ContainerType>(gdata->getGhostedLOC());
594  }
595 }
596 
597 // **********************************************************************
598 template <typename TRAITS,typename LO,typename GO,typename NodeT>
600 evaluateFields(typename TRAITS::EvalData workset)
601 {
602  using Teuchos::RCP;
603  using Teuchos::rcp_dynamic_cast;
604  using Thyra::VectorBase;
606 
607  const auto& localCellIds = this->wda(workset).cell_local_ids_k;
608  const RCP<ProductVectorBase<double>> thyraBlockResidual = rcp_dynamic_cast<ProductVectorBase<double> >(blockedContainer_->get_f(),true);
609 
610  // Loop over scattered fields
611  int currentWorksetLIDSubBlock = -1;
612  for (std::size_t fieldIndex = 0; fieldIndex < scatterFields_.size(); fieldIndex++) {
613  // workset LIDs only change for different sub blocks
614  if (productVectorBlockIndex_[fieldIndex] != currentWorksetLIDSubBlock) {
615  fieldGlobalIndexers_[fieldIndex]->getElementLIDs(localCellIds,worksetLIDs_);
616  currentWorksetLIDSubBlock = productVectorBlockIndex_[fieldIndex];
617  }
618 
619  auto& tpetraResidual = *((rcp_dynamic_cast<Thyra::TpetraVector<RealType,LO,GO,NodeT>>(thyraBlockResidual->getNonconstVectorBlock(productVectorBlockIndex_[fieldIndex]),true))->getTpetraVector());
620  const auto& kokkosResidual = tpetraResidual.getLocalViewDevice(Tpetra::Access::ReadWrite);
621 
622  // Class data fields for lambda capture
623  const auto& fieldOffsets = fieldOffsets_[fieldIndex];
624  const auto& worksetLIDs = worksetLIDs_;
625  const auto& fieldValues = scatterFields_[fieldIndex].get_static_view();
626  const auto& tangentFieldsDevice = dfdpFieldsVoV_.getViewDevice();
627  const auto& kokkosTangents = Kokkos::subview(tangentFieldsDevice,Kokkos::ALL(),productVectorBlockIndex_[fieldIndex]);
628  const double num_params = Kokkos::dimension_scalar(fieldValues)-1;
629 
630  Kokkos::parallel_for(Kokkos::RangePolicy<PHX::Device>(0,workset.num_cells), KOKKOS_LAMBDA (const int& cell) {
631  for(int basis=0; basis < static_cast<int>(fieldOffsets.size()); ++basis) {
632  const int lid = worksetLIDs(cell,fieldOffsets(basis));
633  Kokkos::atomic_add(&kokkosResidual(lid,0), fieldValues(cell,basis).val());
634  for(int i_param=0; i_param<num_params; i_param++)
635  kokkosTangents(i_param)(lid,0) += fieldValues(cell,basis).fastAccessDx(i_param);
636  }
637  });
638  }
639 }
640 
641 // **********************************************************************
642 
643 #endif
T & get(const std::string &name, T def_value)
bool nonnull(const std::shared_ptr< T > &p)
void postRegistrationSetup(typename TRAITS::SetupData, PHX::FieldManager< TRAITS > &)
ScatterResidual_BlockedTpetra(const Teuchos::RCP< const BlockedDOFManager > &)
FieldType
The type of discretization to use for a field pattern.
Teuchos::RCP< LinearObjContainer > getGhostedLOC() const
PHX::View< const int * > offsets
TEUCHOS_DEPRECATED RCP< T > rcp(T *p, Dealloc_T dealloc, bool owns_mem)
virtual void preEvaluate(typename Traits::PreEvalData d)=0
bool nonnull(const boost::shared_ptr< T > &p)
std::string block_id
DEPRECATED - use: getElementBlock()
PHX::MDField< ScalarT, panzer::Cell, panzer::BASIS > field
A field to which we&#39;ll contribute, or in which we&#39;ll store, the result of computing this integral...
bool isType(const std::string &name) const