Panzer Version of the Day
Loading...
Searching...
No Matches
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#include "Panzer_NodeType.hpp"
19
22#include "Panzer_PureBasis.hpp"
25#include "Panzer_HashUtils.hpp"
28
29#include "Thyra_ProductVectorBase.hpp"
30#include "Thyra_BlockedLinearOpBase.hpp"
31#include "Thyra_TpetraVector.hpp"
32#include "Thyra_TpetraLinearOp.hpp"
33#include "Tpetra_CrsMatrix.hpp"
34#include "KokkosSparse_CrsMatrix.hpp"
35
36#include "Phalanx_DataLayout_MDALayout.hpp"
37
38#include "Teuchos_FancyOStream.hpp"
39
40template <typename EvalT,typename TRAITS,typename LO,typename GO,typename NodeT>
42ScatterResidual_BlockedTpetra(const Teuchos::RCP<const BlockedDOFManager> & /* indexer */,
43 const Teuchos::ParameterList& p)
44{
45 std::string scatterName = p.get<std::string>("Scatter Name");
46 Teuchos::RCP<PHX::FieldTag> scatterHolder =
47 Teuchos::rcp(new PHX::Tag<ScalarT>(scatterName,Teuchos::rcp(new PHX::MDALayout<Dummy>(0))));
48
49 // get names to be evaluated
50 const std::vector<std::string>& names =
51 *(p.get< Teuchos::RCP< std::vector<std::string> > >("Dependent Names"));
52
53 Teuchos::RCP<PHX::DataLayout> dl =
54 p.get< Teuchos::RCP<const panzer::PureBasis> >("Basis")->functional;
55
56 // build the vector of fields that this is dependent on
57 for (std::size_t eq = 0; eq < names.size(); ++eq) {
58 PHX::MDField<const ScalarT,Cell,NODE> field = PHX::MDField<const ScalarT,Cell,NODE>(names[eq],dl);
59
60 // tell the field manager that we depend on this field
61 this->addDependentField(field.fieldTag());
62 }
63
64 // this is what this evaluator provides
65 this->addEvaluatedField(*scatterHolder);
66
67 this->setName(scatterName+" Scatter Residual");
68}
69
70// **********************************************************************
71// Specialization: Residual
72// **********************************************************************
73
74template <typename TRAITS,typename LO,typename GO,typename NodeT>
76ScatterResidual_BlockedTpetra(const Teuchos::RCP<const BlockedDOFManager> & indexer,
77 const Teuchos::ParameterList& p)
78 : globalIndexer_(indexer)
79 , globalDataKey_("Residual Scatter Container")
80{
81 std::string scatterName = p.get<std::string>("Scatter Name");
83 Teuchos::rcp(new PHX::Tag<ScalarT>(scatterName,Teuchos::rcp(new PHX::MDALayout<Dummy>(0))));
84
85 // get names to be evaluated
86 const std::vector<std::string>& names =
87 *(p.get< Teuchos::RCP< std::vector<std::string> > >("Dependent Names"));
88
89 // grab map from evaluated names to field names
90 fieldMap_ = p.get< Teuchos::RCP< std::map<std::string,std::string> > >("Dependent Map");
91
92 Teuchos::RCP<PHX::DataLayout> dl =
93 p.get< Teuchos::RCP<const panzer::PureBasis> >("Basis")->functional;
94
95 // build the vector of fields that this is dependent on
96 scatterFields_.resize(names.size());
97 for (std::size_t eq = 0; eq < names.size(); ++eq) {
98 scatterFields_[eq] = PHX::MDField<const ScalarT,Cell,NODE>(names[eq],dl);
99
100 // tell the field manager that we depend on this field
101 this->addDependentField(scatterFields_[eq]);
102 }
103
104 // this is what this evaluator provides
105 this->addEvaluatedField(*scatterHolder_);
106
107 if (p.isType<std::string>("Global Data Key"))
108 globalDataKey_ = p.get<std::string>("Global Data Key");
109
110 this->setName(scatterName+" Scatter Residual");
111}
112
113// **********************************************************************
114template <typename TRAITS,typename LO,typename GO,typename NodeT>
116postRegistrationSetup(typename TRAITS::SetupData d,
118{
119 const Workset & workset_0 = (*d.worksets_)[0];
120 const std::string blockId = this->wda(workset_0).block_id;
121
122 fieldIds_.resize(scatterFields_.size());
123 fieldOffsets_.resize(scatterFields_.size());
126 int maxElementBlockGIDCount = -1;
127 for(std::size_t fd=0; fd < scatterFields_.size(); ++fd) {
128 const std::string fieldName = fieldMap_->find(scatterFields_[fd].fieldTag().name())->second;
129 const int globalFieldNum = globalIndexer_->getFieldNum(fieldName); // Field number in the aggregate BlockDOFManager
130 productVectorBlockIndex_[fd] = globalIndexer_->getFieldBlock(globalFieldNum);
131 fieldGlobalIndexers_[fd] = globalIndexer_->getFieldDOFManagers()[productVectorBlockIndex_[fd]];
132 fieldIds_[fd] = fieldGlobalIndexers_[fd]->getFieldNum(fieldName); // Field number in the sub-global-indexer
133
134 const std::vector<int>& offsets = fieldGlobalIndexers_[fd]->getGIDFieldOffsets(blockId,fieldIds_[fd]);
135 fieldOffsets_[fd] = PHX::View<int*>("ScatterResidual_BlockedTpetra(Residual):fieldOffsets",offsets.size());
136 auto hostOffsets = Kokkos::create_mirror_view(fieldOffsets_[fd]);
137 for (std::size_t i=0; i < offsets.size(); ++i)
138 hostOffsets(i) = offsets[i];
139 Kokkos::deep_copy(fieldOffsets_[fd], hostOffsets);
140
141 maxElementBlockGIDCount = std::max(fieldGlobalIndexers_[fd]->getElementBlockGIDCount(blockId),maxElementBlockGIDCount);
142 }
143
144 // We will use one workset lid view for all fields, but has to be
145 // sized big enough to hold the largest elementBlockGIDCount in the
146 // ProductVector.
147 worksetLIDs_ = PHX::View<LO**>("ScatterResidual_BlockedTpetra(Residual):worksetLIDs",
148 scatterFields_[0].extent(0),
149 maxElementBlockGIDCount);
150}
151
152// **********************************************************************
153template <typename TRAITS,typename LO,typename GO,typename NodeT>
155preEvaluate(typename TRAITS::PreEvalData d)
156{
157 using Teuchos::RCP;
158 using Teuchos::rcp_dynamic_cast;
159
160 // extract linear object container
161 blockedContainer_ = rcp_dynamic_cast<const ContainerType>(d.gedc->getDataObject(globalDataKey_));
162
163 if(blockedContainer_==Teuchos::null) {
164 RCP<const LOCPair_GlobalEvaluationData> gdata = rcp_dynamic_cast<const LOCPair_GlobalEvaluationData>(d.gedc->getDataObject(globalDataKey_),true);
165 blockedContainer_ = rcp_dynamic_cast<const ContainerType>(gdata->getGhostedLOC());
166 }
167}
168
169// **********************************************************************
170template <typename TRAITS,typename LO,typename GO,typename NodeT>
172evaluateFields(typename TRAITS::EvalData workset)
173{
174 using Teuchos::RCP;
175 using Teuchos::rcp_dynamic_cast;
176 using Thyra::VectorBase;
178
179 const auto& localCellIds = this->wda(workset).cell_local_ids_k;
180 const RCP<ProductVectorBase<double>> thyraBlockResidual = rcp_dynamic_cast<ProductVectorBase<double> >(blockedContainer_->get_f(),true);
181
182 // Loop over scattered fields
183 int currentWorksetLIDSubBlock = -1;
184 for (std::size_t fieldIndex = 0; fieldIndex < scatterFields_.size(); fieldIndex++) {
185 // workset LIDs only change for different sub blocks
186 if (productVectorBlockIndex_[fieldIndex] != currentWorksetLIDSubBlock) {
187 fieldGlobalIndexers_[fieldIndex]->getElementLIDs(localCellIds,worksetLIDs_);
188 currentWorksetLIDSubBlock = productVectorBlockIndex_[fieldIndex];
189 }
190
191 auto& tpetraResidual = *((rcp_dynamic_cast<Thyra::TpetraVector<RealType,LO,GO,NodeT>>(thyraBlockResidual->getNonconstVectorBlock(productVectorBlockIndex_[fieldIndex]),true))->getTpetraVector());
192 const auto& kokkosResidual = tpetraResidual.getLocalViewDevice(Tpetra::Access::ReadWrite);
193
194 // Class data fields for lambda capture
195 const auto& fieldOffsets = fieldOffsets_[fieldIndex];
196 const auto& worksetLIDs = worksetLIDs_;
197 const auto& fieldValues = scatterFields_[fieldIndex].get_static_view();
198
199 Kokkos::parallel_for(Kokkos::RangePolicy<PHX::ExecutionSpace>(0,workset.num_cells), KOKKOS_LAMBDA (const int& cell) {
200 for(int basis=0; basis < static_cast<int>(fieldOffsets.size()); ++basis) {
201 const int lid = worksetLIDs(cell,fieldOffsets(basis));
202 Kokkos::atomic_add(&kokkosResidual(lid,0), fieldValues(cell,basis));
203 }
204 });
205 }
206}
207
208// **********************************************************************
209// Specialization: Jacobian
210// **********************************************************************
211
212template <typename TRAITS,typename LO,typename GO,typename NodeT>
214ScatterResidual_BlockedTpetra(const Teuchos::RCP<const BlockedDOFManager> & indexer,
215 const Teuchos::ParameterList& p)
216 : ScatterResidual_BlockedTpetra(indexer,std::vector<Teuchos::RCP<const panzer::GlobalIndexer> >(),p)
217{ }
218
219template <typename TRAITS,typename LO,typename GO,typename NodeT>
221ScatterResidual_BlockedTpetra(const Teuchos::RCP<const BlockedDOFManager> & indexer,
222 const std::vector<Teuchos::RCP<const panzer::GlobalIndexer> > & colIndexers,
223 const Teuchos::ParameterList& p)
224 : globalIndexer_(indexer)
225 , globalDataKey_("Residual Scatter Container")
226 , colGlobalIndexers_(colIndexers)
227 , hasColIndexers_(!colIndexers.empty())
228{
229 // When the columns are not indexed separately they are the rows, so resolve
230 // that here and let everything below read colGlobalIndexers_ unconditionally.
231 if(!hasColIndexers_) {
232 const auto & rowManagers = globalIndexer_->getFieldDOFManagers();
233 colGlobalIndexers_.assign(rowManagers.begin(),rowManagers.end());
234 }
235
236 std::string scatterName = p.get<std::string>("Scatter Name");
238 Teuchos::rcp(new PHX::Tag<ScalarT>(scatterName,Teuchos::rcp(new PHX::MDALayout<Dummy>(0))));
239
240 // get names to be evaluated
241 const std::vector<std::string>& names =
242 *(p.get< Teuchos::RCP< std::vector<std::string> > >("Dependent Names"));
243
244 // grab map from evaluated names to field names
245 fieldMap_ = p.get< Teuchos::RCP< std::map<std::string,std::string> > >("Dependent Map");
246
247 Teuchos::RCP<PHX::DataLayout> dl =
248 p.get< Teuchos::RCP<const panzer::PureBasis> >("Basis")->functional;
249
250 // build the vector of fields that this is dependent on
251 scatterFields_.resize(names.size());
252 for (std::size_t eq = 0; eq < names.size(); ++eq) {
253 scatterFields_[eq] = PHX::MDField<const ScalarT,Cell,NODE>(names[eq],dl);
254
255 // tell the field manager that we depend on this field
256 this->addDependentField(scatterFields_[eq]);
257 }
258
259 // this is what this evaluator provides
260 this->addEvaluatedField(*scatterHolder_);
261
262 if (p.isType<std::string>("Global Data Key"))
263 globalDataKey_ = p.get<std::string>("Global Data Key");
264
265 this->setName(scatterName+" Scatter Residual (Jacobian)");
266}
267
268// **********************************************************************
269template <typename TRAITS,typename LO,typename GO,typename NodeT>
271postRegistrationSetup(typename TRAITS::SetupData d,
273{
274 const Workset & workset_0 = (*d.worksets_)[0];
275 const std::string blockId = this->wda(workset_0).block_id;
276
277 fieldIds_.resize(scatterFields_.size());
278 fieldOffsets_.resize(scatterFields_.size());
280 for (std::size_t fd=0; fd < scatterFields_.size(); ++fd) {
281 const std::string fieldName = fieldMap_->find(scatterFields_[fd].fieldTag().name())->second;
282 const int globalFieldNum = globalIndexer_->getFieldNum(fieldName); // Field number in the aggregate BlockDOFManager
283 productVectorBlockIndex_[fd] = globalIndexer_->getFieldBlock(globalFieldNum);
284 const auto& fieldGlobalIndexer = globalIndexer_->getFieldDOFManagers()[productVectorBlockIndex_[fd]];
285 fieldIds_[fd] = fieldGlobalIndexer->getFieldNum(fieldName); // Field number in the sub-global-indexer
286
287 const std::vector<int>& offsets = globalIndexer_->getGIDFieldOffsets(blockId,globalFieldNum);
288 fieldOffsets_[fd] = PHX::View<int*>("ScatterResidual_BlockedTpetra(Jacobian):fieldOffsets",offsets.size());
289 auto hostOffsets = Kokkos::create_mirror_view(fieldOffsets_[fd]);
290 for (std::size_t i=0; i < offsets.size(); ++i)
291 hostOffsets(i) = offsets[i];
292 Kokkos::deep_copy(fieldOffsets_[fd], hostOffsets);
293 }
294
295 // This is sized differently than the Residual implementation since
296 // we need the LIDs for all sub-blocks, not just the single
297 // sub-block for the field residual scatter.
298 int elementBlockGIDCount = 0;
299 for (const auto& blockDOFMgr : globalIndexer_->getFieldDOFManagers())
300 elementBlockGIDCount += blockDOFMgr->getElementBlockGIDCount(blockId);
301
302 worksetLIDs_ = Kokkos::View<LO**, Kokkos::LayoutRight, PHX::Device>(
303 "ScatterResidual_BlockedTpetra(Jacobian):worksetLIDs_",
304 scatterFields_[0].extent(0), elementBlockGIDCount );
305
306 // Compute the block offsets
307 const auto& blockGlobalIndexers = globalIndexer_->getFieldDOFManagers();
308 const int numBlocks = static_cast<int>(globalIndexer_->getFieldDOFManagers().size());
309 blockOffsets_ = PHX::View<LO*>("ScatterResidual_BlockedTpetra(Jacobian):blockOffsets_",
310 numBlocks+1); // Number of fields, plus a sentinel
311 const auto hostBlockOffsets = Kokkos::create_mirror_view(blockOffsets_);
312 for (int blk=0;blk<numBlocks;blk++) {
313 int blockOffset = globalIndexer_->getBlockGIDOffset(blockId,blk);
314 hostBlockOffsets(blk) = blockOffset;
315 }
316 hostBlockOffsets(numBlocks) = hostBlockOffsets(numBlocks-1) + blockGlobalIndexers[blockGlobalIndexers.size()-1]->getElementBlockGIDCount(blockId);
317 Kokkos::deep_copy(blockOffsets_,hostBlockOffsets);
318
319 // The columns carry the derivative components, so when they are indexed
320 // separately from the rows they need their own offsets and LIDs. When they
321 // are not, both alias the row-side objects and everything below is unchanged.
322 auto hostColBlockOffsets = hostBlockOffsets;
323 if (!hasColIndexers_) {
326 }
327 else {
328 const int numColBlocks = static_cast<int>(colGlobalIndexers_.size());
329 colBlockOffsets_ = PHX::View<LO*>("ScatterResidual_BlockedTpetra(Jacobian):colBlockOffsets_",
330 numColBlocks+1);
331 auto hostColOffsets = Kokkos::create_mirror_view(colBlockOffsets_);
332 hostColOffsets(0) = 0;
333 for (int blk=0;blk<numColBlocks;blk++)
334 hostColOffsets(blk+1) = hostColOffsets(blk) + colGlobalIndexers_[blk]->getElementBlockGIDCount(blockId);
335 Kokkos::deep_copy(colBlockOffsets_,hostColOffsets);
336
337 colWorksetLIDs_ = Kokkos::View<LO**, Kokkos::LayoutRight, PHX::Device>(
338 "ScatterResidual_BlockedTpetra(Jacobian):colWorksetLIDs_",
339 scatterFields_[0].extent(0), hostColOffsets(numColBlocks) );
340 hostColBlockOffsets = hostColOffsets;
341 }
342
343 // The offsets never change after this point, so copy them to the host once
344 // here rather than rebuilding a mirror on every evaluateFields() call.
345 blockOffsets_h_ = Kokkos::create_mirror_view(blockOffsets_);
346 Kokkos::deep_copy(blockOffsets_h_, blockOffsets_);
347 colBlockOffsets_h_ = Kokkos::create_mirror_view(colBlockOffsets_);
348 Kokkos::deep_copy(colBlockOffsets_h_, colBlockOffsets_);
349
350 // Size the scatter scratch once. The contents are rebuilt per call, but the
351 // extents only depend on the block structure.
352 {
353 // Size with the same expression evaluateFields() indexes with.
354 const int numRowBlocks = globalIndexer_->getNumFieldBlocks();
355 const int numColBlocks = static_cast<int>(colGlobalIndexers_.size());
356 hostJacTpetraBlocks_ = typename PHX::View<LocalMatrixType**>::host_mirror_type(
357 "ScatterResidual_BlockedTpetra(Jacobian):hostJacTpetraBlocks", numRowBlocks, numColBlocks);
358 jacTpetraBlocks_ = PHX::View<LocalMatrixType**>(
359 "ScatterResidual_BlockedTpetra(Jacobian):jacTpetraBlocks", numRowBlocks, numColBlocks);
360 blockExistsInJac_ = PHX::View<int**>("ScatterResidual_BlockedTpetra(Jacobian):blockExistsInJac", numRowBlocks, numColBlocks);
361 hostBlockExistsInJac_ = Kokkos::create_mirror_view(blockExistsInJac_);
362 }
363
364 // Make sure the that derivative dimension in the evaluate call is large
365 // enough to hold all derivatives for each sub block load
366 int max_blockDerivativeSize = 0;
367 for (int blk=0;blk<static_cast<int>(colGlobalIndexers_.size());blk++) {
368 const int blockDerivativeSize = hostColBlockOffsets(blk+1) - hostColBlockOffsets(blk);
369 if ( blockDerivativeSize > max_blockDerivativeSize )
370 max_blockDerivativeSize = blockDerivativeSize;
371 }
372 workset_vals_ = Kokkos::View<typename Sacado::ScalarType<ScalarT>::type**, Kokkos::LayoutRight, PHX::Device>(
373 "ScatterResidual_BlockedTpetra(Jacobian):workset_vals_",
374 scatterFields_[0].extent(0), max_blockDerivativeSize );
375}
376
377// **********************************************************************
378template <typename TRAITS,typename LO,typename GO,typename NodeT>
380preEvaluate(typename TRAITS::PreEvalData d)
381{
382 using Teuchos::RCP;
383 using Teuchos::rcp_dynamic_cast;
384
385 // extract linear object container
386 blockedContainer_ = rcp_dynamic_cast<const ContainerType>(d.gedc->getDataObject(globalDataKey_));
387
388 if(blockedContainer_==Teuchos::null) {
389 RCP<const LOCPair_GlobalEvaluationData> gdata = rcp_dynamic_cast<const LOCPair_GlobalEvaluationData>(d.gedc->getDataObject(globalDataKey_),true);
390 blockedContainer_ = rcp_dynamic_cast<const ContainerType>(gdata->getGhostedLOC());
391 }
392}
393
394// **********************************************************************
395template <typename TRAITS,typename LO,typename GO,typename NodeT>
397evaluateFields(typename TRAITS::EvalData workset)
398{
399 using Teuchos::RCP;
400 using Teuchos::rcp_dynamic_cast;
401 using Thyra::VectorBase;
404
405 const auto& localCellIds = this->wda(workset).cell_local_ids_k;
406
407 const int numFieldBlocks = globalIndexer_->getNumFieldBlocks();
408 const int numColFieldBlocks = static_cast<int>(colGlobalIndexers_.size());
409 const RCP<const ContainerType> blockedContainer = blockedContainer_;
410 const RCP<ProductVectorBase<double>> thyraBlockResidual = rcp_dynamic_cast<ProductVectorBase<double> >(blockedContainer_->get_f());
411 const bool haveResidual = Teuchos::nonnull(thyraBlockResidual);
412 const RCP<BlockedLinearOpBase<double>> Jac = rcp_dynamic_cast<BlockedLinearOpBase<double> >(blockedContainer_->get_A(),true);
413
414 // Get the local data for the sub-block crs matrices. First allocate
415 // on host and then deep_copy to device. The sub-blocks are
416 // unmanaged since they are allocated and ref counted separately on
417 // host.
418 // Scratch sized in postRegistrationSetup(); only the contents are rebuilt here.
419 TEUCHOS_ASSERT(static_cast<int>(jacTpetraBlocks_.extent(0))==numFieldBlocks &&
420 static_cast<int>(jacTpetraBlocks_.extent(1))==numColFieldBlocks);
421 auto& hostJacTpetraBlocks = hostJacTpetraBlocks_;
422 auto& blockExistsInJac = blockExistsInJac_;
423 auto& hostBlockExistsInJac = hostBlockExistsInJac_;
424
425 for (int row=0; row < numFieldBlocks; ++row) {
426 for (int col=0; col < numColFieldBlocks; ++col) {
427 const auto thyraTpetraOperator = rcp_dynamic_cast<Thyra::TpetraLinearOp<double,LO,GO,NodeT>>(Jac->getNonconstBlock(row,col),false);
428 if (nonnull(thyraTpetraOperator)) {
429
430 // HACK to enforce views in the CrsGraph to be
431 // Unmanaged. Passing in the MemoryTrait<Unmanaged> doesn't
432 // work as the CrsGraph in the CrsMatrix ignores the
433 // MemoryTrait. Need to use the runtime constructor by passing
434 // in points to ensure Unmanaged. See:
435 // https://github.com/kokkos/kokkos/issues/1581
436
437 // These two lines are the original code we can revert to when #1581 is fixed.
438 // const auto crsMatrix = rcp_dynamic_cast<Tpetra::CrsMatrix<double,LO,GO,NodeT>>(thyraTpetraOperator->getTpetraOperator(),true);
439 // new (&hostJacTpetraBlocks(row,col)) KokkosSparse::CrsMatrix<double,LO,PHX::Device,Kokkos::MemoryTraits<Kokkos::Unmanaged>> (crsMatrix->getLocalMatrix());
440
441 // Instead do this
442 {
443 // Grab the local managed matrix and graph
444 const auto tpetraCrsMatrix = rcp_dynamic_cast<Tpetra::CrsMatrix<double,LO,GO,NodeT>>(thyraTpetraOperator->getTpetraOperator(),true);
445 const auto managedMatrix = tpetraCrsMatrix->getLocalMatrixDevice();
446 const auto managedGraph = managedMatrix.graph;
447
448 // Create runtime unmanaged versions
449 using StaticCrsGraphType = typename LocalMatrixType::StaticCrsGraphType;
450 StaticCrsGraphType unmanagedGraph;
451 unmanagedGraph.entries = typename StaticCrsGraphType::entries_type(managedGraph.entries.data(),managedGraph.entries.extent(0));
452 unmanagedGraph.row_map = typename StaticCrsGraphType::row_map_type(managedGraph.row_map.data(),managedGraph.row_map.extent(0));
453 unmanagedGraph.row_block_offsets = typename StaticCrsGraphType::row_block_type(managedGraph.row_block_offsets.data(),managedGraph.row_block_offsets.extent(0));
454
455 typename LocalMatrixType::values_type unmanagedValues(managedMatrix.values.data(),managedMatrix.values.extent(0));
456 LocalMatrixType unmanagedMatrix(managedMatrix.values.label(), managedMatrix.numCols(), unmanagedValues, unmanagedGraph);
457 new (&hostJacTpetraBlocks(row,col)) LocalMatrixType(unmanagedMatrix);
458 }
459
460 hostBlockExistsInJac(row,col) = 1;
461 }
462 else {
463 hostBlockExistsInJac(row,col) = 0;
464 }
465 }
466 }
467 auto& jacTpetraBlocks = jacTpetraBlocks_;
468 Kokkos::deep_copy(jacTpetraBlocks,hostJacTpetraBlocks);
469 Kokkos::deep_copy(blockExistsInJac,hostBlockExistsInJac);
470
471 // worksetLIDs is larger for Jacobian than Residual fill. Need the
472 // entire set of field offsets for derivative indexing no matter
473 // which block row you are scattering. The residual only needs the
474 // lids for the sub-block that it is scattering to. The subviews
475 // below are to offset the LID blocks correctly.
476 const auto& globalIndexers = globalIndexer_->getFieldDOFManagers();
477 const auto& blockOffsets_h = blockOffsets_h_;
478 for (size_t block=0; block < globalIndexers.size(); ++block) {
479 const auto subviewOfBlockLIDs = Kokkos::subview(worksetLIDs_,Kokkos::ALL(), std::make_pair(blockOffsets_h(block),blockOffsets_h(block+1)));
480 globalIndexers[block]->getElementLIDs(localCellIds,subviewOfBlockLIDs);
481 }
482
483 const auto& colBlockOffsets_h = colBlockOffsets_h_;
484 if (hasColIndexers_) {
485 for (size_t block=0; block < colGlobalIndexers_.size(); ++block) {
486 const auto subviewOfBlockLIDs = Kokkos::subview(colWorksetLIDs_,Kokkos::ALL(), std::make_pair(colBlockOffsets_h(block),colBlockOffsets_h(block+1)));
487 colGlobalIndexers_[block]->getElementLIDs(localCellIds,subviewOfBlockLIDs);
488 }
489 }
490
491 // Loop over scattered fields
492 for (std::size_t fieldIndex = 0; fieldIndex < scatterFields_.size(); fieldIndex++) {
493
494 const int blockRowIndex = productVectorBlockIndex_[fieldIndex];
495 typename Tpetra::Vector<double,LO,GO,panzer::TpetraNodeType>::dual_view_type::t_dev kokkosResidual;
496 if (haveResidual) {
497 auto& tpetraResidual = *((rcp_dynamic_cast<Thyra::TpetraVector<RealType,LO,GO,NodeT>>(thyraBlockResidual->getNonconstVectorBlock(blockRowIndex),true))->getTpetraVector());
498 kokkosResidual = tpetraResidual.getLocalViewDevice(Tpetra::Access::ReadWrite);
499 }
500
501 // Class data fields for lambda capture
502 const PHX::View<const int*> fieldOffsets = fieldOffsets_[fieldIndex];
503 const PHX::View<const ScalarT**> fieldValues = scatterFields_[fieldIndex].get_static_view();
504 const PHX::View<const LO*> colBlockOffsets = colBlockOffsets_;
505
506 const Kokkos::View<const LO**, Kokkos::LayoutRight, PHX::Device> worksetLIDs = worksetLIDs_;
507 const Kokkos::View<const LO**, Kokkos::LayoutRight, PHX::Device> colWorksetLIDs = colWorksetLIDs_;
508 Kokkos::View<typename Sacado::ScalarType<ScalarT>::type**, Kokkos::LayoutRight, PHX::Device> workset_vals = workset_vals_;
509
510 Kokkos::parallel_for(Kokkos::RangePolicy<PHX::ExecutionSpace>(0,workset.num_cells), KOKKOS_LAMBDA (const int& cell) {
511 for(int basis=0; basis < static_cast<int>(fieldOffsets.size()); ++basis) {
512 typedef PHX::MDField<const ScalarT,Cell,NODE> FieldType;
513 typename FieldType::array_type::reference_type tmpFieldVal = fieldValues(cell,basis);
514 const int rowLID = worksetLIDs(cell,fieldOffsets(basis));
515
516 if (haveResidual)
517 Kokkos::atomic_add(&kokkosResidual(rowLID,0), tmpFieldVal.val());
518
519 for (int blockColIndex=0; blockColIndex < numColFieldBlocks; ++blockColIndex) {
520 if (blockExistsInJac(blockRowIndex,blockColIndex)) {
521 const int start = colBlockOffsets(blockColIndex);
522 const int stop = colBlockOffsets(blockColIndex+1);
523 const int sensSize = stop-start;
524
525 for (int i=0; i < sensSize; ++i)
526 workset_vals(cell,i) = tmpFieldVal.fastAccessDx(start+i);
527
528 jacTpetraBlocks(blockRowIndex,blockColIndex).sumIntoValues(rowLID, &colWorksetLIDs(cell,start), sensSize, &workset_vals(cell,0), true,true);
529 }
530 }
531 }
532 });
533
534 }
535
536 // Placement delete on view of matrices
537 for (int row=0; row < numFieldBlocks; ++row) {
538 for (int col=0; col < numColFieldBlocks; ++col) {
539 if (hostBlockExistsInJac(row,col)) {
540 hostJacTpetraBlocks(row,col).~CrsMatrix();
541 }
542 }
543 }
544
545}
546
547// **********************************************************************
548// Specialization: Tangent
549// **********************************************************************
550
551template <typename TRAITS,typename LO,typename GO,typename NodeT>
553ScatterResidual_BlockedTpetra(const Teuchos::RCP<const BlockedDOFManager> & indexer,
554 const Teuchos::ParameterList& p)
555 : globalIndexer_(indexer)
556 , globalDataKey_("Residual Scatter Container")
557{
558 std::string scatterName = p.get<std::string>("Scatter Name");
560 Teuchos::rcp(new PHX::Tag<ScalarT>(scatterName,Teuchos::rcp(new PHX::MDALayout<Dummy>(0))));
561
562 // get names to be evaluated
563 const std::vector<std::string>& names =
564 *(p.get< Teuchos::RCP< std::vector<std::string> > >("Dependent Names"));
565
566 // grab map from evaluated names to field names
567 fieldMap_ = p.get< Teuchos::RCP< std::map<std::string,std::string> > >("Dependent Map");
568
569 Teuchos::RCP<PHX::DataLayout> dl =
570 p.get< Teuchos::RCP<const panzer::PureBasis> >("Basis")->functional;
571
572 // build the vector of fields that this is dependent on
573 scatterFields_.resize(names.size());
574 for (std::size_t eq = 0; eq < names.size(); ++eq) {
575 scatterFields_[eq] = PHX::MDField<const ScalarT,Cell,NODE>(names[eq],dl);
576
577 // tell the field manager that we depend on this field
578 this->addDependentField(scatterFields_[eq]);
579 }
580
581 // this is what this evaluator provides
582 this->addEvaluatedField(*scatterHolder_);
583
584 if (p.isType<std::string>("Global Data Key"))
585 globalDataKey_ = p.get<std::string>("Global Data Key");
586
587 this->setName(scatterName+" Scatter Residual (Tangent)");
588}
589
590// **********************************************************************
591template <typename TRAITS,typename LO,typename GO,typename NodeT>
593postRegistrationSetup(typename TRAITS::SetupData d,
595{
596 const Workset & workset_0 = (*d.worksets_)[0];
597 const std::string blockId = this->wda(workset_0).block_id;
598
599 fieldIds_.resize(scatterFields_.size());
600 fieldOffsets_.resize(scatterFields_.size());
603 int maxElementBlockGIDCount = -1;
604 for(std::size_t fd=0; fd < scatterFields_.size(); ++fd) {
605 const std::string fieldName = fieldMap_->find(scatterFields_[fd].fieldTag().name())->second;
606 const int globalFieldNum = globalIndexer_->getFieldNum(fieldName); // Field number in the aggregate BlockDOFManager
607 productVectorBlockIndex_[fd] = globalIndexer_->getFieldBlock(globalFieldNum);
608 fieldGlobalIndexers_[fd] = globalIndexer_->getFieldDOFManagers()[productVectorBlockIndex_[fd]];
609 fieldIds_[fd] = fieldGlobalIndexers_[fd]->getFieldNum(fieldName); // Field number in the sub-global-indexer
610
611 const std::vector<int>& offsets = fieldGlobalIndexers_[fd]->getGIDFieldOffsets(blockId,fieldIds_[fd]);
612 fieldOffsets_[fd] = PHX::View<int*>("ScatterResidual_BlockedTpetra(Tangent):fieldOffsets",offsets.size());
613 auto hostOffsets = Kokkos::create_mirror_view(fieldOffsets_[fd]);
614 for (std::size_t i=0; i < offsets.size(); ++i)
615 hostOffsets(i) = offsets[i];
616 Kokkos::deep_copy(fieldOffsets_[fd], hostOffsets);
617
618 maxElementBlockGIDCount = std::max(fieldGlobalIndexers_[fd]->getElementBlockGIDCount(blockId),maxElementBlockGIDCount);
619 }
620
621 // We will use one workset lid view for all fields, but has to be
622 // sized big enough to hold the largest elementBlockGIDCount in the
623 // ProductVector.
624 worksetLIDs_ = PHX::View<LO**>("ScatterResidual_BlockedTpetra(Tangent):worksetLIDs",
625 scatterFields_[0].extent(0),
626 maxElementBlockGIDCount);
627}
628
629// **********************************************************************
630template <typename TRAITS,typename LO,typename GO,typename NodeT>
632preEvaluate(typename TRAITS::PreEvalData d)
633{
634 using Teuchos::RCP;
635 using Teuchos::rcp_dynamic_cast;
637
638 // this is the list of parameters and their names that this scatter has to account for
639 std::vector<std::string> activeParameters =
640 rcp_dynamic_cast<ParameterList_GlobalEvaluationData>(d.gedc->getDataObject("PARAMETER_NAMES"))->getActiveParameters();
641
642 const int numBlocks = static_cast<int>(globalIndexer_->getFieldDOFManagers().size());
643
644 // Only the outer view is allocated here. The device views of the df/dp
645 // sub-blocks are acquired and released in evaluateFields().
646 dfdpFieldsVoV_.initialize("ScatterResidual_Tpetra<Tangent>::dfdpFieldsVoV_",activeParameters.size(),numBlocks);
647
648 dfdpVectors_.resize(activeParameters.size());
649 for(std::size_t i=0;i<activeParameters.size();i++) {
650 RCP<ContainerType> paramBlockedContainer = rcp_dynamic_cast<ContainerType>(d.gedc->getDataObject(activeParameters[i]),true);
651 RCP<ProductVectorBase<double>> productVector =
652 rcp_dynamic_cast<ProductVectorBase<double>>(paramBlockedContainer->get_f(),true);
653 dfdpVectors_[i].resize(numBlocks);
654 for(int j=0;j<numBlocks;j++)
655 dfdpVectors_[i][j] = rcp_dynamic_cast<Thyra::TpetraVector<RealType,LO,GO,NodeT>>(productVector->getNonconstVectorBlock(j),true)->getTpetraVector();
656 }
657
658 // extract linear object container
659 blockedContainer_ = rcp_dynamic_cast<const ContainerType>(d.gedc->getDataObject(globalDataKey_));
660
661 if(blockedContainer_==Teuchos::null) {
662 RCP<const LOCPair_GlobalEvaluationData> gdata = rcp_dynamic_cast<const LOCPair_GlobalEvaluationData>(d.gedc->getDataObject(globalDataKey_),true);
663 blockedContainer_ = rcp_dynamic_cast<const ContainerType>(gdata->getGhostedLOC());
664 }
665}
666
667// **********************************************************************
668template <typename TRAITS,typename LO,typename GO,typename NodeT>
670evaluateFields(typename TRAITS::EvalData workset)
671{
672 using Teuchos::RCP;
673 using Teuchos::rcp_dynamic_cast;
674 using Thyra::VectorBase;
676
677 const auto& localCellIds = this->wda(workset).cell_local_ids_k;
678 const RCP<ProductVectorBase<double>> thyraBlockResidual = rcp_dynamic_cast<ProductVectorBase<double> >(blockedContainer_->get_f(),true);
679
680 // Acquire the df/dp device views for the duration of this method only. See
681 // the release loop at the end of this method.
682 for(std::size_t i=0;i<dfdpVectors_.size();i++)
683 for(std::size_t j=0;j<dfdpVectors_[i].size();j++)
684 dfdpFieldsVoV_.addView(dfdpVectors_[i][j]->getLocalViewDevice(Tpetra::Access::ReadWrite),i,j);
685 dfdpFieldsVoV_.syncHostToDevice();
686
687 // Loop over scattered fields
688 int currentWorksetLIDSubBlock = -1;
689 for (std::size_t fieldIndex = 0; fieldIndex < scatterFields_.size(); fieldIndex++) {
690 // workset LIDs only change for different sub blocks
691 if (productVectorBlockIndex_[fieldIndex] != currentWorksetLIDSubBlock) {
692 fieldGlobalIndexers_[fieldIndex]->getElementLIDs(localCellIds,worksetLIDs_);
693 currentWorksetLIDSubBlock = productVectorBlockIndex_[fieldIndex];
694 }
695
696 auto& tpetraResidual = *((rcp_dynamic_cast<Thyra::TpetraVector<RealType,LO,GO,NodeT>>(thyraBlockResidual->getNonconstVectorBlock(productVectorBlockIndex_[fieldIndex]),true))->getTpetraVector());
697 const auto& kokkosResidual = tpetraResidual.getLocalViewDevice(Tpetra::Access::ReadWrite);
698
699 // Class data fields for lambda capture
700 const auto& fieldOffsets = fieldOffsets_[fieldIndex];
701 const auto& worksetLIDs = worksetLIDs_;
702 const auto& fieldValues = scatterFields_[fieldIndex].get_static_view();
703 const auto& tangentFieldsDevice = dfdpFieldsVoV_.getViewDevice();
704 const auto& kokkosTangents = Kokkos::subview(tangentFieldsDevice,Kokkos::ALL(),productVectorBlockIndex_[fieldIndex]);
705 const std::size_t num_params = Sacado::dimension_scalar(fieldValues)-1;
706
707 Kokkos::parallel_for(Kokkos::RangePolicy<PHX::ExecutionSpace>(0,workset.num_cells), KOKKOS_LAMBDA (const int& cell) {
708 for(int basis=0; basis < static_cast<int>(fieldOffsets.size()); ++basis) {
709 const int lid = worksetLIDs(cell,fieldOffsets(basis));
710 Kokkos::atomic_add(&kokkosResidual(lid,0), fieldValues(cell,basis).val());
711 for(std::size_t i_param=0; i_param<num_params; i_param++)
712 Kokkos::atomic_add(&kokkosTangents(i_param)(lid,0), fieldValues(cell,basis).fastAccessDx(i_param));
713 }
714 });
715 }
716
717 // Release the df/dp device views. Holding a device view past the return of
718 // this method makes any subsequent host access to the same vector throw, e.g.
719 // AssemblyEngine::evaluateDirichletBCs() -> adjustForDirichletConditions().
720 for(std::size_t i=0;i<dfdpVectors_.size();i++)
721 for(std::size_t j=0;j<dfdpVectors_[i].size();j++)
722 dfdpFieldsVoV_.addView(Kokkos::View<RealType**,Kokkos::LayoutLeft,PHX::Device>(),i,j);
723}
724
725// **********************************************************************
726
727#endif
Kokkos::View< typename Sacado::ScalarType< ScalarT >::type **, Kokkos::LayoutRight, PHX::Device > workset_vals_
Scratch space for local values.
std::vector< PHX::View< int * > > fieldOffsets_
Offset into the cell lids for each field. Size of number of fields to scatter.
KokkosSparse::CrsMatrix< double, LO, PHX::Device, Kokkos::MemoryTraits< Kokkos::Unmanaged >, size_t > LocalMatrixType
Sub-block matrix type handed to the scatter kernel.
PHX::View< LO * >::host_mirror_type blockOffsets_h_
Host copies of the offsets, filled once since the offsets never change.
Teuchos::RCP< const BlockedTpetraLinearObjContainer< RealType, LO, GO, NodeT > > blockedContainer_
PHX::View< LO * > blockOffsets_
The offset values of the blocked DOFs per element. Size of number of blocks in the product vector + 1...
Kokkos::View< LO **, Kokkos::LayoutRight, PHX::Device > colWorksetLIDs_
Column LIDs and derivative offsets. Aliases of the row members when square.
Kokkos::View< LO **, Kokkos::LayoutRight, PHX::Device > worksetLIDs_
Local indices for unknowns.
Teuchos::RCP< const BlockedDOFManager > globalIndexer_
Maps the local (field,element,basis) triplet to a global ID for scattering.
std::vector< PHX::View< int * > > fieldOffsets_
Offset into the cell lids for each field.
std::vector< int > fieldIds_
Field IDs in the local product vector block (not global field id).
Teuchos::RCP< PHX::FieldTag > scatterHolder_
Dummy evalauted field so that the evaluator will have something to do.
std::vector< Teuchos::RCP< const panzer::GlobalIndexer > > fieldGlobalIndexers_
Vector of global indexers, one for each scattered field respectively.
std::vector< PHX::MDField< const ScalarT, Cell, NODE > > scatterFields_
Fields that need to be scattered will be put in this vector.
Teuchos::RCP< const BlockedTpetraLinearObjContainer< RealType, LO, GO, NodeT > > blockedContainer_
Teuchos::RCP< const BlockedDOFManager > globalIndexer_
Maps the local (field,element,basis) triplet to a global ID for scattering.
std::vector< PHX::View< int * > > fieldOffsets_
Offset into the cell lids for each field.
PHX::ViewOfViews< 2, Kokkos::View< RealType **, Kokkos::LayoutLeft, PHX::Device > > dfdpFieldsVoV_
Storage for the tangent data.
Teuchos::RCP< PHX::FieldTag > scatterHolder_
Dummy evalauted field so that the evaluator will have something to do.
std::vector< int > fieldIds_
Field IDs in the local product vector block (not global field id).
std::vector< Teuchos::RCP< const panzer::GlobalIndexer > > fieldGlobalIndexers_
Vector of global indexers, one for each scattered field respectively.
std::vector< PHX::MDField< const ScalarT, Cell, NODE > > scatterFields_
Fields that need to be scattered will be put in this vector.
Teuchos::RCP< const BlockedTpetraLinearObjContainer< RealType, LO, GO, NodeT > > blockedContainer_
Pushes residual values into the residual vector for a Newton-based solve.
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.