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"
26 
27 #include "Thyra_ProductVectorBase.hpp"
28 #include "Thyra_BlockedLinearOpBase.hpp"
29 #include "Thyra_TpetraVector.hpp"
30 #include "Thyra_TpetraLinearOp.hpp"
31 #include "Tpetra_CrsMatrix.hpp"
32 #include "KokkosSparse_CrsMatrix.hpp"
33 
34 #include "Phalanx_DataLayout_MDALayout.hpp"
35 
36 #include "Teuchos_FancyOStream.hpp"
37 
38 template <typename EvalT,typename TRAITS,typename LO,typename GO,typename NodeT>
41  const Teuchos::ParameterList& p)
42 {
43  std::string scatterName = p.get<std::string>("Scatter Name");
44  Teuchos::RCP<PHX::FieldTag> scatterHolder =
46 
47  // get names to be evaluated
48  const std::vector<std::string>& names =
49  *(p.get< Teuchos::RCP< std::vector<std::string> > >("Dependent Names"));
50 
52  p.get< Teuchos::RCP<const panzer::PureBasis> >("Basis")->functional;
53 
54  // build the vector of fields that this is dependent on
55  for (std::size_t eq = 0; eq < names.size(); ++eq) {
57 
58  // tell the field manager that we depend on this field
59  this->addDependentField(field.fieldTag());
60  }
61 
62  // this is what this evaluator provides
63  this->addEvaluatedField(*scatterHolder);
64 
65  this->setName(scatterName+" Scatter Residual");
66 }
67 
68 // **********************************************************************
69 // Specialization: Residual
70 // **********************************************************************
71 
72 template <typename TRAITS,typename LO,typename GO,typename NodeT>
75  const Teuchos::ParameterList& p)
76  : globalIndexer_(indexer)
77  , globalDataKey_("Residual Scatter Container")
78 {
79  std::string scatterName = p.get<std::string>("Scatter Name");
80  scatterHolder_ =
82 
83  // get names to be evaluated
84  const std::vector<std::string>& names =
85  *(p.get< Teuchos::RCP< std::vector<std::string> > >("Dependent Names"));
86 
87  // grab map from evaluated names to field names
88  fieldMap_ = p.get< Teuchos::RCP< std::map<std::string,std::string> > >("Dependent Map");
89 
91  p.get< Teuchos::RCP<const panzer::PureBasis> >("Basis")->functional;
92 
93  // build the vector of fields that this is dependent on
94  scatterFields_.resize(names.size());
95  for (std::size_t eq = 0; eq < names.size(); ++eq) {
96  scatterFields_[eq] = PHX::MDField<const ScalarT,Cell,NODE>(names[eq],dl);
97 
98  // tell the field manager that we depend on this field
99  this->addDependentField(scatterFields_[eq]);
100  }
101 
102  // this is what this evaluator provides
103  this->addEvaluatedField(*scatterHolder_);
104 
105  if (p.isType<std::string>("Global Data Key"))
106  globalDataKey_ = p.get<std::string>("Global Data Key");
107 
108  this->setName(scatterName+" Scatter Residual");
109 }
110 
111 // **********************************************************************
112 template <typename TRAITS,typename LO,typename GO,typename NodeT>
114 postRegistrationSetup(typename TRAITS::SetupData d,
115  PHX::FieldManager<TRAITS>& /* fm */)
116 {
117  const Workset & workset_0 = (*d.worksets_)[0];
118  const std::string blockId = this->wda(workset_0).block_id;
119 
120  fieldIds_.resize(scatterFields_.size());
121  fieldOffsets_.resize(scatterFields_.size());
122  fieldGlobalIndexers_.resize(scatterFields_.size());
123  productVectorBlockIndex_.resize(scatterFields_.size());
124  int maxElementBlockGIDCount = -1;
125  for(std::size_t fd=0; fd < scatterFields_.size(); ++fd) {
126  const std::string fieldName = fieldMap_->find(scatterFields_[fd].fieldTag().name())->second;
127  const int globalFieldNum = globalIndexer_->getFieldNum(fieldName); // Field number in the aggregate BlockDOFManager
128  productVectorBlockIndex_[fd] = globalIndexer_->getFieldBlock(globalFieldNum);
129  fieldGlobalIndexers_[fd] = globalIndexer_->getFieldDOFManagers()[productVectorBlockIndex_[fd]];
130  fieldIds_[fd] = fieldGlobalIndexers_[fd]->getFieldNum(fieldName); // Field number in the sub-global-indexer
131 
132  const std::vector<int>& offsets = fieldGlobalIndexers_[fd]->getGIDFieldOffsets(blockId,fieldIds_[fd]);
133  fieldOffsets_[fd] = PHX::View<int*>("ScatterResidual_BlockedTpetra(Residual):fieldOffsets",offsets.size());
134  auto hostOffsets = Kokkos::create_mirror_view(fieldOffsets_[fd]);
135  for (std::size_t i=0; i < offsets.size(); ++i)
136  hostOffsets(i) = offsets[i];
137  Kokkos::deep_copy(fieldOffsets_[fd], hostOffsets);
138 
139  maxElementBlockGIDCount = std::max(fieldGlobalIndexers_[fd]->getElementBlockGIDCount(blockId),maxElementBlockGIDCount);
140  }
141 
142  // We will use one workset lid view for all fields, but has to be
143  // sized big enough to hold the largest elementBlockGIDCount in the
144  // ProductVector.
145  worksetLIDs_ = PHX::View<LO**>("ScatterResidual_BlockedTpetra(Residual):worksetLIDs",
146  scatterFields_[0].extent(0),
147  maxElementBlockGIDCount);
148 }
149 
150 // **********************************************************************
151 template <typename TRAITS,typename LO,typename GO,typename NodeT>
153 preEvaluate(typename TRAITS::PreEvalData d)
154 {
155  using Teuchos::RCP;
156  using Teuchos::rcp_dynamic_cast;
157 
158  // extract linear object container
159  blockedContainer_ = rcp_dynamic_cast<const ContainerType>(d.gedc->getDataObject(globalDataKey_));
160 
161  if(blockedContainer_==Teuchos::null) {
162  RCP<const LOCPair_GlobalEvaluationData> gdata = rcp_dynamic_cast<const LOCPair_GlobalEvaluationData>(d.gedc->getDataObject(globalDataKey_),true);
163  blockedContainer_ = rcp_dynamic_cast<const ContainerType>(gdata->getGhostedLOC());
164  }
165 }
166 
167 // **********************************************************************
168 template <typename TRAITS,typename LO,typename GO,typename NodeT>
170 evaluateFields(typename TRAITS::EvalData workset)
171 {
172  using Teuchos::RCP;
173  using Teuchos::rcp_dynamic_cast;
174  using Thyra::VectorBase;
176 
177  const auto& localCellIds = this->wda(workset).cell_local_ids_k;
178  const RCP<ProductVectorBase<double>> thyraBlockResidual = rcp_dynamic_cast<ProductVectorBase<double> >(blockedContainer_->get_f(),true);
179 
180  // Loop over scattered fields
181  int currentWorksetLIDSubBlock = -1;
182  for (std::size_t fieldIndex = 0; fieldIndex < scatterFields_.size(); fieldIndex++) {
183  // workset LIDs only change for different sub blocks
184  if (productVectorBlockIndex_[fieldIndex] != currentWorksetLIDSubBlock) {
185  fieldGlobalIndexers_[fieldIndex]->getElementLIDs(localCellIds,worksetLIDs_);
186  currentWorksetLIDSubBlock = productVectorBlockIndex_[fieldIndex];
187  }
188 
189  auto& tpetraResidual = *((rcp_dynamic_cast<Thyra::TpetraVector<RealType,LO,GO,NodeT>>(thyraBlockResidual->getNonconstVectorBlock(productVectorBlockIndex_[fieldIndex]),true))->getTpetraVector());
190  const auto& kokkosResidual = tpetraResidual.getLocalViewDevice(Tpetra::Access::ReadWrite);
191 
192  // Class data fields for lambda capture
193  const auto& fieldOffsets = fieldOffsets_[fieldIndex];
194  const auto& worksetLIDs = worksetLIDs_;
195  const auto& fieldValues = scatterFields_[fieldIndex].get_static_view();
196 
197  Kokkos::parallel_for(Kokkos::RangePolicy<PHX::Device>(0,workset.num_cells), KOKKOS_LAMBDA (const int& cell) {
198  for(int basis=0; basis < static_cast<int>(fieldOffsets.size()); ++basis) {
199  const int lid = worksetLIDs(cell,fieldOffsets(basis));
200  Kokkos::atomic_add(&kokkosResidual(lid,0), fieldValues(cell,basis));
201  }
202  });
203  }
204 }
205 
206 // **********************************************************************
207 // Specialization: Jacobian
208 // **********************************************************************
209 
210 template <typename TRAITS,typename LO,typename GO,typename NodeT>
213  const Teuchos::ParameterList& p)
214  : globalIndexer_(indexer)
215  , globalDataKey_("Residual Scatter Container")
216 {
217  std::string scatterName = p.get<std::string>("Scatter Name");
218  scatterHolder_ =
220 
221  // get names to be evaluated
222  const std::vector<std::string>& names =
223  *(p.get< Teuchos::RCP< std::vector<std::string> > >("Dependent Names"));
224 
225  // grab map from evaluated names to field names
226  fieldMap_ = p.get< Teuchos::RCP< std::map<std::string,std::string> > >("Dependent Map");
227 
229  p.get< Teuchos::RCP<const panzer::PureBasis> >("Basis")->functional;
230 
231  // build the vector of fields that this is dependent on
232  scatterFields_.resize(names.size());
233  for (std::size_t eq = 0; eq < names.size(); ++eq) {
234  scatterFields_[eq] = PHX::MDField<const ScalarT,Cell,NODE>(names[eq],dl);
235 
236  // tell the field manager that we depend on this field
237  this->addDependentField(scatterFields_[eq]);
238  }
239 
240  // this is what this evaluator provides
241  this->addEvaluatedField(*scatterHolder_);
242 
243  if (p.isType<std::string>("Global Data Key"))
244  globalDataKey_ = p.get<std::string>("Global Data Key");
245 
246  this->setName(scatterName+" Scatter Residual (Jacobian)");
247 }
248 
249 // **********************************************************************
250 template <typename TRAITS,typename LO,typename GO,typename NodeT>
252 postRegistrationSetup(typename TRAITS::SetupData d,
253  PHX::FieldManager<TRAITS>& /* fm */)
254 {
255  const Workset & workset_0 = (*d.worksets_)[0];
256  const std::string blockId = this->wda(workset_0).block_id;
257 
258  fieldIds_.resize(scatterFields_.size());
259  fieldOffsets_.resize(scatterFields_.size());
260  productVectorBlockIndex_.resize(scatterFields_.size());
261  for (std::size_t fd=0; fd < scatterFields_.size(); ++fd) {
262  const std::string fieldName = fieldMap_->find(scatterFields_[fd].fieldTag().name())->second;
263  const int globalFieldNum = globalIndexer_->getFieldNum(fieldName); // Field number in the aggregate BlockDOFManager
264  productVectorBlockIndex_[fd] = globalIndexer_->getFieldBlock(globalFieldNum);
265  const auto& fieldGlobalIndexer = globalIndexer_->getFieldDOFManagers()[productVectorBlockIndex_[fd]];
266  fieldIds_[fd] = fieldGlobalIndexer->getFieldNum(fieldName); // Field number in the sub-global-indexer
267 
268  const std::vector<int>& offsets = globalIndexer_->getGIDFieldOffsets(blockId,globalFieldNum);
269  fieldOffsets_[fd] = PHX::View<int*>("ScatterResidual_BlockedTpetra(Jacobian):fieldOffsets",offsets.size());
270  auto hostOffsets = Kokkos::create_mirror_view(fieldOffsets_[fd]);
271  for (std::size_t i=0; i < offsets.size(); ++i)
272  hostOffsets(i) = offsets[i];
273  Kokkos::deep_copy(fieldOffsets_[fd], hostOffsets);
274  }
275 
276  // This is sized differently than the Residual implementation since
277  // we need the LIDs for all sub-blocks, not just the single
278  // sub-block for the field residual scatter.
279  int elementBlockGIDCount = 0;
280  for (const auto& blockDOFMgr : globalIndexer_->getFieldDOFManagers())
281  elementBlockGIDCount += blockDOFMgr->getElementBlockGIDCount(blockId);
282 
284  "ScatterResidual_BlockedTpetra(Jacobian):worksetLIDs_",
285  scatterFields_[0].extent(0), elementBlockGIDCount );
286 
287  // Compute the block offsets
288  const auto& blockGlobalIndexers = globalIndexer_->getFieldDOFManagers();
289  const int numBlocks = static_cast<int>(globalIndexer_->getFieldDOFManagers().size());
290  blockOffsets_ = PHX::View<LO*>("ScatterResidual_BlockedTpetra(Jacobian):blockOffsets_",
291  numBlocks+1); // Number of fields, plus a sentinel
292  const auto hostBlockOffsets = Kokkos::create_mirror_view(blockOffsets_);
293  for (int blk=0;blk<numBlocks;blk++) {
294  int blockOffset = globalIndexer_->getBlockGIDOffset(blockId,blk);
295  hostBlockOffsets(blk) = blockOffset;
296  }
297  hostBlockOffsets(numBlocks) = hostBlockOffsets(numBlocks-1) + blockGlobalIndexers[blockGlobalIndexers.size()-1]->getElementBlockGIDCount(blockId);
298  Kokkos::deep_copy(blockOffsets_,hostBlockOffsets);
299 
300  // Make sure the that derivative dimension in the evaluate call is large
301  // enough to hold all derivatives for each sub block load
302  int max_blockDerivativeSize = 0;
303  for (int blk=0;blk<numBlocks;blk++) {
304  const int blockDerivativeSize = hostBlockOffsets(blk+1) - hostBlockOffsets(blk);
305  if ( blockDerivativeSize > max_blockDerivativeSize )
306  max_blockDerivativeSize = blockDerivativeSize;
307  }
308  workset_vals_ = Kokkos::View<typename Sacado::ScalarType<ScalarT>::type**, Kokkos::LayoutRight, PHX::Device>(
309  "ScatterResidual_BlockedTpetra(Jacobian):workset_vals_",
310  scatterFields_[0].extent(0), max_blockDerivativeSize );
311 }
312 
313 // **********************************************************************
314 template <typename TRAITS,typename LO,typename GO,typename NodeT>
316 preEvaluate(typename TRAITS::PreEvalData d)
317 {
318  using Teuchos::RCP;
319  using Teuchos::rcp_dynamic_cast;
320 
321  // extract linear object container
322  blockedContainer_ = rcp_dynamic_cast<const ContainerType>(d.gedc->getDataObject(globalDataKey_));
323 
324  if(blockedContainer_==Teuchos::null) {
325  RCP<const LOCPair_GlobalEvaluationData> gdata = rcp_dynamic_cast<const LOCPair_GlobalEvaluationData>(d.gedc->getDataObject(globalDataKey_),true);
326  blockedContainer_ = rcp_dynamic_cast<const ContainerType>(gdata->getGhostedLOC());
327  }
328 }
329 
330 // **********************************************************************
331 template <typename TRAITS,typename LO,typename GO,typename NodeT>
333 evaluateFields(typename TRAITS::EvalData workset)
334 {
335  using Teuchos::RCP;
336  using Teuchos::rcp_dynamic_cast;
337  using Thyra::VectorBase;
340 
341  const auto& localCellIds = this->wda(workset).cell_local_ids_k;
342 
343  const int numFieldBlocks = globalIndexer_->getNumFieldBlocks();
344  const RCP<const ContainerType> blockedContainer = blockedContainer_;
345  const RCP<ProductVectorBase<double>> thyraBlockResidual = rcp_dynamic_cast<ProductVectorBase<double> >(blockedContainer_->get_f());
346  const bool haveResidual = Teuchos::nonnull(thyraBlockResidual);
347  const RCP<BlockedLinearOpBase<double>> Jac = rcp_dynamic_cast<BlockedLinearOpBase<double> >(blockedContainer_->get_A(),true);
348 
349  // Get the local data for the sub-block crs matrices. First allocate
350  // on host and then deep_copy to device. The sub-blocks are
351  // unmanaged since they are allocated and ref counted separately on
352  // host.
353  using LocalMatrixType = KokkosSparse::CrsMatrix<double,LO,PHX::Device,Kokkos::MemoryTraits<Kokkos::Unmanaged>, size_t>;
354  typename PHX::View<LocalMatrixType**>::HostMirror
355  hostJacTpetraBlocks("panzer::ScatterResidual_BlockTpetra<Jacobian>::hostJacTpetraBlocks", numFieldBlocks,numFieldBlocks);
356 
357  PHX::View<int**> blockExistsInJac = PHX::View<int**>("blockExistsInJac_",numFieldBlocks,numFieldBlocks);
358  auto hostBlockExistsInJac = Kokkos::create_mirror_view(blockExistsInJac);
359 
360  for (int row=0; row < numFieldBlocks; ++row) {
361  for (int col=0; col < numFieldBlocks; ++col) {
362  const auto thyraTpetraOperator = rcp_dynamic_cast<Thyra::TpetraLinearOp<double,LO,GO,NodeT>>(Jac->getNonconstBlock(row,col),false);
363  if (nonnull(thyraTpetraOperator)) {
364 
365  // HACK to enforce views in the CrsGraph to be
366  // Unmanaged. Passing in the MemoryTrait<Unmanaged> doesn't
367  // work as the CrsGraph in the CrsMatrix ignores the
368  // MemoryTrait. Need to use the runtime constructor by passing
369  // in points to ensure Unmanaged. See:
370  // https://github.com/kokkos/kokkos/issues/1581
371 
372  // These two lines are the original code we can revert to when #1581 is fixed.
373  // const auto crsMatrix = rcp_dynamic_cast<Tpetra::CrsMatrix<double,LO,GO,NodeT>>(thyraTpetraOperator->getTpetraOperator(),true);
374  // new (&hostJacTpetraBlocks(row,col)) KokkosSparse::CrsMatrix<double,LO,PHX::Device,Kokkos::MemoryTraits<Kokkos::Unmanaged>> (crsMatrix->getLocalMatrix());
375 
376  // Instead do this
377  {
378  // Grab the local managed matrix and graph
379  const auto tpetraCrsMatrix = rcp_dynamic_cast<Tpetra::CrsMatrix<double,LO,GO,NodeT>>(thyraTpetraOperator->getTpetraOperator(),true);
380  const auto managedMatrix = tpetraCrsMatrix->getLocalMatrixDevice();
381  const auto managedGraph = managedMatrix.graph;
382 
383  // Create runtime unmanaged versions
384  using StaticCrsGraphType = typename LocalMatrixType::StaticCrsGraphType;
385  StaticCrsGraphType unmanagedGraph;
386  unmanagedGraph.entries = typename StaticCrsGraphType::entries_type(managedGraph.entries.data(),managedGraph.entries.extent(0));
387  unmanagedGraph.row_map = typename StaticCrsGraphType::row_map_type(managedGraph.row_map.data(),managedGraph.row_map.extent(0));
388  unmanagedGraph.row_block_offsets = typename StaticCrsGraphType::row_block_type(managedGraph.row_block_offsets.data(),managedGraph.row_block_offsets.extent(0));
389 
390  typename LocalMatrixType::values_type unmanagedValues(managedMatrix.values.data(),managedMatrix.values.extent(0));
391  LocalMatrixType unmanagedMatrix(managedMatrix.values.label(), managedMatrix.numCols(), unmanagedValues, unmanagedGraph);
392  new (&hostJacTpetraBlocks(row,col)) LocalMatrixType(unmanagedMatrix);
393  }
394 
395  hostBlockExistsInJac(row,col) = 1;
396  }
397  else {
398  hostBlockExistsInJac(row,col) = 0;
399  }
400  }
401  }
402  typename PHX::View<LocalMatrixType**>
403  jacTpetraBlocks("panzer::ScatterResidual_BlockedTpetra<Jacobian>::jacTpetraBlocks",numFieldBlocks,numFieldBlocks);
404  Kokkos::deep_copy(jacTpetraBlocks,hostJacTpetraBlocks);
405  Kokkos::deep_copy(blockExistsInJac,hostBlockExistsInJac);
406 
407  // worksetLIDs is larger for Jacobian than Residual fill. Need the
408  // entire set of field offsets for derivative indexing no matter
409  // which block row you are scattering. The residual only needs the
410  // lids for the sub-block that it is scattering to. The subviews
411  // below are to offset the LID blocks correctly.
412  const auto& globalIndexers = globalIndexer_->getFieldDOFManagers();
413  auto blockOffsets_h = Kokkos::create_mirror_view(blockOffsets_);
414  Kokkos::deep_copy(blockOffsets_h, blockOffsets_);
415  for (size_t block=0; block < globalIndexers.size(); ++block) {
416  const auto subviewOfBlockLIDs = Kokkos::subview(worksetLIDs_,Kokkos::ALL(), std::make_pair(blockOffsets_h(block),blockOffsets_h(block+1)));
417  globalIndexers[block]->getElementLIDs(localCellIds,subviewOfBlockLIDs);
418  }
419 
420  // Loop over scattered fields
421  for (std::size_t fieldIndex = 0; fieldIndex < scatterFields_.size(); fieldIndex++) {
422 
423  const int blockRowIndex = productVectorBlockIndex_[fieldIndex];
424  typename Tpetra::Vector<double,LO,GO,PHX::Device>::dual_view_type::t_dev kokkosResidual;
425  if (haveResidual) {
426  auto& tpetraResidual = *((rcp_dynamic_cast<Thyra::TpetraVector<RealType,LO,GO,NodeT>>(thyraBlockResidual->getNonconstVectorBlock(blockRowIndex),true))->getTpetraVector());
427  kokkosResidual = tpetraResidual.getLocalViewDevice(Tpetra::Access::ReadWrite);
428  }
429 
430  // Class data fields for lambda capture
431  const PHX::View<const int*> fieldOffsets = fieldOffsets_[fieldIndex];
432  const PHX::View<const ScalarT**> fieldValues = scatterFields_[fieldIndex].get_static_view();
433  const PHX::View<const LO*> blockOffsets = blockOffsets_;
434 
435  const Kokkos::View<const LO**, Kokkos::LayoutRight, PHX::Device> worksetLIDs = worksetLIDs_;
436  Kokkos::View<typename Sacado::ScalarType<ScalarT>::type**, Kokkos::LayoutRight, PHX::Device> workset_vals = workset_vals_;
437 
438  Kokkos::parallel_for(Kokkos::RangePolicy<PHX::Device>(0,workset.num_cells), KOKKOS_LAMBDA (const int& cell) {
439  for(int basis=0; basis < static_cast<int>(fieldOffsets.size()); ++basis) {
441  typename FieldType::array_type::reference_type tmpFieldVal = fieldValues(cell,basis);
442  const int rowLID = worksetLIDs(cell,fieldOffsets(basis));
443 
444  if (haveResidual)
445  Kokkos::atomic_add(&kokkosResidual(rowLID,0), tmpFieldVal.val());
446 
447  for (int blockColIndex=0; blockColIndex < numFieldBlocks; ++blockColIndex) {
448  if (blockExistsInJac(blockRowIndex,blockColIndex)) {
449  const int start = blockOffsets(blockColIndex);
450  const int stop = blockOffsets(blockColIndex+1);
451  const int sensSize = stop-start;
452 
453  for (int i=0; i < sensSize; ++i)
454  workset_vals(cell,i) = tmpFieldVal.fastAccessDx(start+i);
455 
456  jacTpetraBlocks(blockRowIndex,blockColIndex).sumIntoValues(rowLID, &worksetLIDs(cell,start), sensSize, &workset_vals(cell,0), true,true);
457  }
458  }
459  }
460  });
461 
462  }
463 
464  // Placement delete on view of matrices
465  for (int row=0; row < numFieldBlocks; ++row) {
466  for (int col=0; col < numFieldBlocks; ++col) {
467  if (hostBlockExistsInJac(row,col)) {
468  hostJacTpetraBlocks(row,col).~CrsMatrix();
469  }
470  }
471  }
472 
473 }
474 
475 // **********************************************************************
476 
477 #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