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
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
39template <typename EvalT,typename TRAITS,typename LO,typename GO,typename NodeT>
41ScatterResidual_BlockedTpetra(const Teuchos::RCP<const BlockedDOFManager> & /* indexer */,
42 const Teuchos::ParameterList& p)
43{
44 std::string scatterName = p.get<std::string>("Scatter Name");
45 Teuchos::RCP<PHX::FieldTag> scatterHolder =
46 Teuchos::rcp(new PHX::Tag<ScalarT>(scatterName,Teuchos::rcp(new PHX::MDALayout<Dummy>(0))));
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
52 Teuchos::RCP<PHX::DataLayout> dl =
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) {
57 PHX::MDField<const ScalarT,Cell,NODE> field = PHX::MDField<const ScalarT,Cell,NODE>(names[eq],dl);
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
73template <typename TRAITS,typename LO,typename GO,typename NodeT>
75ScatterResidual_BlockedTpetra(const Teuchos::RCP<const BlockedDOFManager> & indexer,
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_ =
82 Teuchos::rcp(new PHX::Tag<ScalarT>(scatterName,Teuchos::rcp(new PHX::MDALayout<Dummy>(0))));
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
91 Teuchos::RCP<PHX::DataLayout> dl =
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// **********************************************************************
113template <typename TRAITS,typename LO,typename GO,typename NodeT>
115postRegistrationSetup(typename TRAITS::SetupData d,
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// **********************************************************************
152template <typename TRAITS,typename LO,typename GO,typename NodeT>
154preEvaluate(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// **********************************************************************
169template <typename TRAITS,typename LO,typename GO,typename NodeT>
171evaluateFields(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
211template <typename TRAITS,typename LO,typename GO,typename NodeT>
213ScatterResidual_BlockedTpetra(const Teuchos::RCP<const BlockedDOFManager> & indexer,
214 const Teuchos::ParameterList& p)
215 : ScatterResidual_BlockedTpetra(indexer,std::vector<Teuchos::RCP<const panzer::GlobalIndexer> >(),p)
216{ }
217
218template <typename TRAITS,typename LO,typename GO,typename NodeT>
220ScatterResidual_BlockedTpetra(const Teuchos::RCP<const BlockedDOFManager> & indexer,
221 const std::vector<Teuchos::RCP<const panzer::GlobalIndexer> > & colIndexers,
222 const Teuchos::ParameterList& p)
223 : globalIndexer_(indexer)
224 , globalDataKey_("Residual Scatter Container")
225 , colGlobalIndexers_(colIndexers)
226 , hasColIndexers_(!colIndexers.empty())
227{
228 // When the columns are not indexed separately they are the rows, so resolve
229 // that here and let everything below read colGlobalIndexers_ unconditionally.
230 if(!hasColIndexers_) {
231 const auto & rowManagers = globalIndexer_->getFieldDOFManagers();
232 colGlobalIndexers_.assign(rowManagers.begin(),rowManagers.end());
233 }
234
235 std::string scatterName = p.get<std::string>("Scatter Name");
236 scatterHolder_ =
237 Teuchos::rcp(new PHX::Tag<ScalarT>(scatterName,Teuchos::rcp(new PHX::MDALayout<Dummy>(0))));
238
239 // get names to be evaluated
240 const std::vector<std::string>& names =
241 *(p.get< Teuchos::RCP< std::vector<std::string> > >("Dependent Names"));
242
243 // grab map from evaluated names to field names
244 fieldMap_ = p.get< Teuchos::RCP< std::map<std::string,std::string> > >("Dependent Map");
245
246 Teuchos::RCP<PHX::DataLayout> dl =
247 p.get< Teuchos::RCP<const panzer::PureBasis> >("Basis")->functional;
248
249 // build the vector of fields that this is dependent on
250 scatterFields_.resize(names.size());
251 for (std::size_t eq = 0; eq < names.size(); ++eq) {
252 scatterFields_[eq] = PHX::MDField<const ScalarT,Cell,NODE>(names[eq],dl);
253
254 // tell the field manager that we depend on this field
255 this->addDependentField(scatterFields_[eq]);
256 }
257
258 // this is what this evaluator provides
259 this->addEvaluatedField(*scatterHolder_);
260
261 if (p.isType<std::string>("Global Data Key"))
262 globalDataKey_ = p.get<std::string>("Global Data Key");
263
264 this->setName(scatterName+" Scatter Residual (Jacobian)");
265}
266
267// **********************************************************************
268template <typename TRAITS,typename LO,typename GO,typename NodeT>
270postRegistrationSetup(typename TRAITS::SetupData d,
272{
273 const Workset & workset_0 = (*d.worksets_)[0];
274 const std::string blockId = this->wda(workset_0).block_id;
275
276 fieldIds_.resize(scatterFields_.size());
277 fieldOffsets_.resize(scatterFields_.size());
278 productVectorBlockIndex_.resize(scatterFields_.size());
279 for (std::size_t fd=0; fd < scatterFields_.size(); ++fd) {
280 const std::string fieldName = fieldMap_->find(scatterFields_[fd].fieldTag().name())->second;
281 const int globalFieldNum = globalIndexer_->getFieldNum(fieldName); // Field number in the aggregate BlockDOFManager
282 productVectorBlockIndex_[fd] = globalIndexer_->getFieldBlock(globalFieldNum);
283 const auto& fieldGlobalIndexer = globalIndexer_->getFieldDOFManagers()[productVectorBlockIndex_[fd]];
284 fieldIds_[fd] = fieldGlobalIndexer->getFieldNum(fieldName); // Field number in the sub-global-indexer
285
286 const std::vector<int>& offsets = globalIndexer_->getGIDFieldOffsets(blockId,globalFieldNum);
287 fieldOffsets_[fd] = PHX::View<int*>("ScatterResidual_BlockedTpetra(Jacobian):fieldOffsets",offsets.size());
288 auto hostOffsets = Kokkos::create_mirror_view(fieldOffsets_[fd]);
289 for (std::size_t i=0; i < offsets.size(); ++i)
290 hostOffsets(i) = offsets[i];
291 Kokkos::deep_copy(fieldOffsets_[fd], hostOffsets);
292 }
293
294 // This is sized differently than the Residual implementation since
295 // we need the LIDs for all sub-blocks, not just the single
296 // sub-block for the field residual scatter.
297 int elementBlockGIDCount = 0;
298 for (const auto& blockDOFMgr : globalIndexer_->getFieldDOFManagers())
299 elementBlockGIDCount += blockDOFMgr->getElementBlockGIDCount(blockId);
300
301 worksetLIDs_ = Kokkos::View<LO**, Kokkos::LayoutRight, PHX::Device>(
302 "ScatterResidual_BlockedTpetra(Jacobian):worksetLIDs_",
303 scatterFields_[0].extent(0), elementBlockGIDCount );
304
305 // Compute the block offsets
306 const auto& blockGlobalIndexers = globalIndexer_->getFieldDOFManagers();
307 const int numBlocks = static_cast<int>(globalIndexer_->getFieldDOFManagers().size());
308 blockOffsets_ = PHX::View<LO*>("ScatterResidual_BlockedTpetra(Jacobian):blockOffsets_",
309 numBlocks+1); // Number of fields, plus a sentinel
310 const auto hostBlockOffsets = Kokkos::create_mirror_view(blockOffsets_);
311 for (int blk=0;blk<numBlocks;blk++) {
312 int blockOffset = globalIndexer_->getBlockGIDOffset(blockId,blk);
313 hostBlockOffsets(blk) = blockOffset;
314 }
315 hostBlockOffsets(numBlocks) = hostBlockOffsets(numBlocks-1) + blockGlobalIndexers[blockGlobalIndexers.size()-1]->getElementBlockGIDCount(blockId);
316 Kokkos::deep_copy(blockOffsets_,hostBlockOffsets);
317
318 // The columns carry the derivative components, so when they are indexed
319 // separately from the rows they need their own offsets and LIDs. When they
320 // are not, both alias the row-side objects and everything below is unchanged.
321 auto hostColBlockOffsets = hostBlockOffsets;
322 if (!hasColIndexers_) {
323 colBlockOffsets_ = blockOffsets_;
324 colWorksetLIDs_ = worksetLIDs_;
325 }
326 else {
327 const int numColBlocks = static_cast<int>(colGlobalIndexers_.size());
328 colBlockOffsets_ = PHX::View<LO*>("ScatterResidual_BlockedTpetra(Jacobian):colBlockOffsets_",
329 numColBlocks+1);
330 auto hostColOffsets = Kokkos::create_mirror_view(colBlockOffsets_);
331 hostColOffsets(0) = 0;
332 for (int blk=0;blk<numColBlocks;blk++)
333 hostColOffsets(blk+1) = hostColOffsets(blk) + colGlobalIndexers_[blk]->getElementBlockGIDCount(blockId);
334 Kokkos::deep_copy(colBlockOffsets_,hostColOffsets);
335
336 colWorksetLIDs_ = Kokkos::View<LO**, Kokkos::LayoutRight, PHX::Device>(
337 "ScatterResidual_BlockedTpetra(Jacobian):colWorksetLIDs_",
338 scatterFields_[0].extent(0), hostColOffsets(numColBlocks) );
339 hostColBlockOffsets = hostColOffsets;
340 }
341
342 // The offsets never change after this point, so copy them to the host once
343 // here rather than rebuilding a mirror on every evaluateFields() call.
344 blockOffsets_h_ = Kokkos::create_mirror_view(blockOffsets_);
345 Kokkos::deep_copy(blockOffsets_h_, blockOffsets_);
346 colBlockOffsets_h_ = Kokkos::create_mirror_view(colBlockOffsets_);
347 Kokkos::deep_copy(colBlockOffsets_h_, colBlockOffsets_);
348
349 // Size the scatter scratch once. The contents are rebuilt per call, but the
350 // extents only depend on the block structure.
351 {
352 // Size with the same expression evaluateFields() indexes with.
353 const int numRowBlocks = globalIndexer_->getNumFieldBlocks();
354 const int numColBlocks = static_cast<int>(colGlobalIndexers_.size());
355 hostJacTpetraBlocks_ = typename PHX::View<LocalMatrixType**>::host_mirror_type(
356 "ScatterResidual_BlockedTpetra(Jacobian):hostJacTpetraBlocks", numRowBlocks, numColBlocks);
357 jacTpetraBlocks_ = PHX::View<LocalMatrixType**>(
358 "ScatterResidual_BlockedTpetra(Jacobian):jacTpetraBlocks", numRowBlocks, numColBlocks);
359 blockExistsInJac_ = PHX::View<int**>("ScatterResidual_BlockedTpetra(Jacobian):blockExistsInJac", numRowBlocks, numColBlocks);
360 hostBlockExistsInJac_ = Kokkos::create_mirror_view(blockExistsInJac_);
361 }
362
363 // Make sure the that derivative dimension in the evaluate call is large
364 // enough to hold all derivatives for each sub block load
365 int max_blockDerivativeSize = 0;
366 for (int blk=0;blk<static_cast<int>(colGlobalIndexers_.size());blk++) {
367 const int blockDerivativeSize = hostColBlockOffsets(blk+1) - hostColBlockOffsets(blk);
368 if ( blockDerivativeSize > max_blockDerivativeSize )
369 max_blockDerivativeSize = blockDerivativeSize;
370 }
371 workset_vals_ = Kokkos::View<typename Sacado::ScalarType<ScalarT>::type**, Kokkos::LayoutRight, PHX::Device>(
372 "ScatterResidual_BlockedTpetra(Jacobian):workset_vals_",
373 scatterFields_[0].extent(0), max_blockDerivativeSize );
374}
375
376// **********************************************************************
377template <typename TRAITS,typename LO,typename GO,typename NodeT>
379preEvaluate(typename TRAITS::PreEvalData d)
380{
381 using Teuchos::RCP;
382 using Teuchos::rcp_dynamic_cast;
383
384 // extract linear object container
385 blockedContainer_ = rcp_dynamic_cast<const ContainerType>(d.gedc->getDataObject(globalDataKey_));
386
387 if(blockedContainer_==Teuchos::null) {
388 RCP<const LOCPair_GlobalEvaluationData> gdata = rcp_dynamic_cast<const LOCPair_GlobalEvaluationData>(d.gedc->getDataObject(globalDataKey_),true);
389 blockedContainer_ = rcp_dynamic_cast<const ContainerType>(gdata->getGhostedLOC());
390 }
391}
392
393// **********************************************************************
394template <typename TRAITS,typename LO,typename GO,typename NodeT>
396evaluateFields(typename TRAITS::EvalData workset)
397{
398 using Teuchos::RCP;
399 using Teuchos::rcp_dynamic_cast;
400 using Thyra::VectorBase;
403
404 const auto& localCellIds = this->wda(workset).cell_local_ids_k;
405
406 const int numFieldBlocks = globalIndexer_->getNumFieldBlocks();
407 const int numColFieldBlocks = static_cast<int>(colGlobalIndexers_.size());
408 const RCP<const ContainerType> blockedContainer = blockedContainer_;
409 const RCP<ProductVectorBase<double>> thyraBlockResidual = rcp_dynamic_cast<ProductVectorBase<double> >(blockedContainer_->get_f());
410 const bool haveResidual = Teuchos::nonnull(thyraBlockResidual);
411 const RCP<BlockedLinearOpBase<double>> Jac = rcp_dynamic_cast<BlockedLinearOpBase<double> >(blockedContainer_->get_A(),true);
412
413 // Get the local data for the sub-block crs matrices. First allocate
414 // on host and then deep_copy to device. The sub-blocks are
415 // unmanaged since they are allocated and ref counted separately on
416 // host.
417 // Scratch sized in postRegistrationSetup(); only the contents are rebuilt here.
418 TEUCHOS_ASSERT(static_cast<int>(jacTpetraBlocks_.extent(0))==numFieldBlocks &&
419 static_cast<int>(jacTpetraBlocks_.extent(1))==numColFieldBlocks);
420 auto& hostJacTpetraBlocks = hostJacTpetraBlocks_;
421 auto& blockExistsInJac = blockExistsInJac_;
422 auto& hostBlockExistsInJac = hostBlockExistsInJac_;
423
424 for (int row=0; row < numFieldBlocks; ++row) {
425 for (int col=0; col < numColFieldBlocks; ++col) {
426 const auto thyraTpetraOperator = rcp_dynamic_cast<Thyra::TpetraLinearOp<double,LO,GO,NodeT>>(Jac->getNonconstBlock(row,col),false);
427 if (nonnull(thyraTpetraOperator)) {
428
429 // HACK to enforce views in the CrsGraph to be
430 // Unmanaged. Passing in the MemoryTrait<Unmanaged> doesn't
431 // work as the CrsGraph in the CrsMatrix ignores the
432 // MemoryTrait. Need to use the runtime constructor by passing
433 // in points to ensure Unmanaged. See:
434 // https://github.com/kokkos/kokkos/issues/1581
435
436 // These two lines are the original code we can revert to when #1581 is fixed.
437 // const auto crsMatrix = rcp_dynamic_cast<Tpetra::CrsMatrix<double,LO,GO,NodeT>>(thyraTpetraOperator->getTpetraOperator(),true);
438 // new (&hostJacTpetraBlocks(row,col)) KokkosSparse::CrsMatrix<double,LO,PHX::Device,Kokkos::MemoryTraits<Kokkos::Unmanaged>> (crsMatrix->getLocalMatrix());
439
440 // Instead do this
441 {
442 // Grab the local managed matrix and graph
443 const auto tpetraCrsMatrix = rcp_dynamic_cast<Tpetra::CrsMatrix<double,LO,GO,NodeT>>(thyraTpetraOperator->getTpetraOperator(),true);
444 const auto managedMatrix = tpetraCrsMatrix->getLocalMatrixDevice();
445 const auto managedGraph = managedMatrix.graph;
446
447 // Create runtime unmanaged versions
448 using StaticCrsGraphType = typename LocalMatrixType::StaticCrsGraphType;
449 StaticCrsGraphType unmanagedGraph;
450 unmanagedGraph.entries = typename StaticCrsGraphType::entries_type(managedGraph.entries.data(),managedGraph.entries.extent(0));
451 unmanagedGraph.row_map = typename StaticCrsGraphType::row_map_type(managedGraph.row_map.data(),managedGraph.row_map.extent(0));
452 unmanagedGraph.row_block_offsets = typename StaticCrsGraphType::row_block_type(managedGraph.row_block_offsets.data(),managedGraph.row_block_offsets.extent(0));
453
454 typename LocalMatrixType::values_type unmanagedValues(managedMatrix.values.data(),managedMatrix.values.extent(0));
455 LocalMatrixType unmanagedMatrix(managedMatrix.values.label(), managedMatrix.numCols(), unmanagedValues, unmanagedGraph);
456 new (&hostJacTpetraBlocks(row,col)) LocalMatrixType(unmanagedMatrix);
457 }
458
459 hostBlockExistsInJac(row,col) = 1;
460 }
461 else {
462 hostBlockExistsInJac(row,col) = 0;
463 }
464 }
465 }
466 auto& jacTpetraBlocks = jacTpetraBlocks_;
467 Kokkos::deep_copy(jacTpetraBlocks,hostJacTpetraBlocks);
468 Kokkos::deep_copy(blockExistsInJac,hostBlockExistsInJac);
469
470 // worksetLIDs is larger for Jacobian than Residual fill. Need the
471 // entire set of field offsets for derivative indexing no matter
472 // which block row you are scattering. The residual only needs the
473 // lids for the sub-block that it is scattering to. The subviews
474 // below are to offset the LID blocks correctly.
475 const auto& globalIndexers = globalIndexer_->getFieldDOFManagers();
476 const auto& blockOffsets_h = blockOffsets_h_;
477 for (size_t block=0; block < globalIndexers.size(); ++block) {
478 const auto subviewOfBlockLIDs = Kokkos::subview(worksetLIDs_,Kokkos::ALL(), std::make_pair(blockOffsets_h(block),blockOffsets_h(block+1)));
479 globalIndexers[block]->getElementLIDs(localCellIds,subviewOfBlockLIDs);
480 }
481
482 const auto& colBlockOffsets_h = colBlockOffsets_h_;
483 if (hasColIndexers_) {
484 for (size_t block=0; block < colGlobalIndexers_.size(); ++block) {
485 const auto subviewOfBlockLIDs = Kokkos::subview(colWorksetLIDs_,Kokkos::ALL(), std::make_pair(colBlockOffsets_h(block),colBlockOffsets_h(block+1)));
486 colGlobalIndexers_[block]->getElementLIDs(localCellIds,subviewOfBlockLIDs);
487 }
488 }
489
490 // Loop over scattered fields
491 for (std::size_t fieldIndex = 0; fieldIndex < scatterFields_.size(); fieldIndex++) {
492
493 const int blockRowIndex = productVectorBlockIndex_[fieldIndex];
494 typename Tpetra::Vector<double,LO,GO,PHX::Device>::dual_view_type::t_dev kokkosResidual;
495 if (haveResidual) {
496 auto& tpetraResidual = *((rcp_dynamic_cast<Thyra::TpetraVector<RealType,LO,GO,NodeT>>(thyraBlockResidual->getNonconstVectorBlock(blockRowIndex),true))->getTpetraVector());
497 kokkosResidual = tpetraResidual.getLocalViewDevice(Tpetra::Access::ReadWrite);
498 }
499
500 // Class data fields for lambda capture
501 const PHX::View<const int*> fieldOffsets = fieldOffsets_[fieldIndex];
502 const PHX::View<const ScalarT**> fieldValues = scatterFields_[fieldIndex].get_static_view();
503 const PHX::View<const LO*> colBlockOffsets = colBlockOffsets_;
504
505 const Kokkos::View<const LO**, Kokkos::LayoutRight, PHX::Device> worksetLIDs = worksetLIDs_;
506 const Kokkos::View<const LO**, Kokkos::LayoutRight, PHX::Device> colWorksetLIDs = colWorksetLIDs_;
507 Kokkos::View<typename Sacado::ScalarType<ScalarT>::type**, Kokkos::LayoutRight, PHX::Device> workset_vals = workset_vals_;
508
509 Kokkos::parallel_for(Kokkos::RangePolicy<PHX::Device>(0,workset.num_cells), KOKKOS_LAMBDA (const int& cell) {
510 for(int basis=0; basis < static_cast<int>(fieldOffsets.size()); ++basis) {
511 typedef PHX::MDField<const ScalarT,Cell,NODE> FieldType;
512 typename FieldType::array_type::reference_type tmpFieldVal = fieldValues(cell,basis);
513 const int rowLID = worksetLIDs(cell,fieldOffsets(basis));
514
515 if (haveResidual)
516 Kokkos::atomic_add(&kokkosResidual(rowLID,0), tmpFieldVal.val());
517
518 for (int blockColIndex=0; blockColIndex < numColFieldBlocks; ++blockColIndex) {
519 if (blockExistsInJac(blockRowIndex,blockColIndex)) {
520 const int start = colBlockOffsets(blockColIndex);
521 const int stop = colBlockOffsets(blockColIndex+1);
522 const int sensSize = stop-start;
523
524 for (int i=0; i < sensSize; ++i)
525 workset_vals(cell,i) = tmpFieldVal.fastAccessDx(start+i);
526
527 jacTpetraBlocks(blockRowIndex,blockColIndex).sumIntoValues(rowLID, &colWorksetLIDs(cell,start), sensSize, &workset_vals(cell,0), true,true);
528 }
529 }
530 }
531 });
532
533 }
534
535 // Placement delete on view of matrices
536 for (int row=0; row < numFieldBlocks; ++row) {
537 for (int col=0; col < numColFieldBlocks; ++col) {
538 if (hostBlockExistsInJac(row,col)) {
539 hostJacTpetraBlocks(row,col).~CrsMatrix();
540 }
541 }
542 }
543
544}
545
546// **********************************************************************
547// Specialization: Tangent
548// **********************************************************************
549
550template <typename TRAITS,typename LO,typename GO,typename NodeT>
552ScatterResidual_BlockedTpetra(const Teuchos::RCP<const BlockedDOFManager> & indexer,
553 const Teuchos::ParameterList& p)
554 : globalIndexer_(indexer)
555 , globalDataKey_("Residual Scatter Container")
556{
557 std::string scatterName = p.get<std::string>("Scatter Name");
558 scatterHolder_ =
559 Teuchos::rcp(new PHX::Tag<ScalarT>(scatterName,Teuchos::rcp(new PHX::MDALayout<Dummy>(0))));
560
561 // get names to be evaluated
562 const std::vector<std::string>& names =
563 *(p.get< Teuchos::RCP< std::vector<std::string> > >("Dependent Names"));
564
565 // grab map from evaluated names to field names
566 fieldMap_ = p.get< Teuchos::RCP< std::map<std::string,std::string> > >("Dependent Map");
567
568 Teuchos::RCP<PHX::DataLayout> dl =
569 p.get< Teuchos::RCP<const panzer::PureBasis> >("Basis")->functional;
570
571 // build the vector of fields that this is dependent on
572 scatterFields_.resize(names.size());
573 for (std::size_t eq = 0; eq < names.size(); ++eq) {
574 scatterFields_[eq] = PHX::MDField<const ScalarT,Cell,NODE>(names[eq],dl);
575
576 // tell the field manager that we depend on this field
577 this->addDependentField(scatterFields_[eq]);
578 }
579
580 // this is what this evaluator provides
581 this->addEvaluatedField(*scatterHolder_);
582
583 if (p.isType<std::string>("Global Data Key"))
584 globalDataKey_ = p.get<std::string>("Global Data Key");
585
586 this->setName(scatterName+" Scatter Residual (Tangent)");
587}
588
589// **********************************************************************
590template <typename TRAITS,typename LO,typename GO,typename NodeT>
592postRegistrationSetup(typename TRAITS::SetupData d,
594{
595 const Workset & workset_0 = (*d.worksets_)[0];
596 const std::string blockId = this->wda(workset_0).block_id;
597
598 fieldIds_.resize(scatterFields_.size());
599 fieldOffsets_.resize(scatterFields_.size());
600 fieldGlobalIndexers_.resize(scatterFields_.size());
601 productVectorBlockIndex_.resize(scatterFields_.size());
602 int maxElementBlockGIDCount = -1;
603 for(std::size_t fd=0; fd < scatterFields_.size(); ++fd) {
604 const std::string fieldName = fieldMap_->find(scatterFields_[fd].fieldTag().name())->second;
605 const int globalFieldNum = globalIndexer_->getFieldNum(fieldName); // Field number in the aggregate BlockDOFManager
606 productVectorBlockIndex_[fd] = globalIndexer_->getFieldBlock(globalFieldNum);
607 fieldGlobalIndexers_[fd] = globalIndexer_->getFieldDOFManagers()[productVectorBlockIndex_[fd]];
608 fieldIds_[fd] = fieldGlobalIndexers_[fd]->getFieldNum(fieldName); // Field number in the sub-global-indexer
609
610 const std::vector<int>& offsets = fieldGlobalIndexers_[fd]->getGIDFieldOffsets(blockId,fieldIds_[fd]);
611 fieldOffsets_[fd] = PHX::View<int*>("ScatterResidual_BlockedTpetra(Tangent):fieldOffsets",offsets.size());
612 auto hostOffsets = Kokkos::create_mirror_view(fieldOffsets_[fd]);
613 for (std::size_t i=0; i < offsets.size(); ++i)
614 hostOffsets(i) = offsets[i];
615 Kokkos::deep_copy(fieldOffsets_[fd], hostOffsets);
616
617 maxElementBlockGIDCount = std::max(fieldGlobalIndexers_[fd]->getElementBlockGIDCount(blockId),maxElementBlockGIDCount);
618 }
619
620 // We will use one workset lid view for all fields, but has to be
621 // sized big enough to hold the largest elementBlockGIDCount in the
622 // ProductVector.
623 worksetLIDs_ = PHX::View<LO**>("ScatterResidual_BlockedTpetra(Tangent):worksetLIDs",
624 scatterFields_[0].extent(0),
625 maxElementBlockGIDCount);
626}
627
628// **********************************************************************
629template <typename TRAITS,typename LO,typename GO,typename NodeT>
631preEvaluate(typename TRAITS::PreEvalData d)
632{
633 using Teuchos::RCP;
634 using Teuchos::rcp_dynamic_cast;
636
637 // this is the list of parameters and their names that this scatter has to account for
638 std::vector<std::string> activeParameters =
639 rcp_dynamic_cast<ParameterList_GlobalEvaluationData>(d.gedc->getDataObject("PARAMETER_NAMES"))->getActiveParameters();
640
641 const int numBlocks = static_cast<int>(globalIndexer_->getFieldDOFManagers().size());
642
643 // Only the outer view is allocated here. The device views of the df/dp
644 // sub-blocks are acquired and released in evaluateFields().
645 dfdpFieldsVoV_.initialize("ScatterResidual_Tpetra<Tangent>::dfdpFieldsVoV_",activeParameters.size(),numBlocks);
646
647 dfdpVectors_.resize(activeParameters.size());
648 for(std::size_t i=0;i<activeParameters.size();i++) {
649 RCP<ContainerType> paramBlockedContainer = rcp_dynamic_cast<ContainerType>(d.gedc->getDataObject(activeParameters[i]),true);
650 RCP<ProductVectorBase<double>> productVector =
651 rcp_dynamic_cast<ProductVectorBase<double>>(paramBlockedContainer->get_f(),true);
652 dfdpVectors_[i].resize(numBlocks);
653 for(int j=0;j<numBlocks;j++)
654 dfdpVectors_[i][j] = rcp_dynamic_cast<Thyra::TpetraVector<RealType,LO,GO,NodeT>>(productVector->getNonconstVectorBlock(j),true)->getTpetraVector();
655 }
656
657 // extract linear object container
658 blockedContainer_ = rcp_dynamic_cast<const ContainerType>(d.gedc->getDataObject(globalDataKey_));
659
660 if(blockedContainer_==Teuchos::null) {
661 RCP<const LOCPair_GlobalEvaluationData> gdata = rcp_dynamic_cast<const LOCPair_GlobalEvaluationData>(d.gedc->getDataObject(globalDataKey_),true);
662 blockedContainer_ = rcp_dynamic_cast<const ContainerType>(gdata->getGhostedLOC());
663 }
664}
665
666// **********************************************************************
667template <typename TRAITS,typename LO,typename GO,typename NodeT>
669evaluateFields(typename TRAITS::EvalData workset)
670{
671 using Teuchos::RCP;
672 using Teuchos::rcp_dynamic_cast;
673 using Thyra::VectorBase;
675
676 const auto& localCellIds = this->wda(workset).cell_local_ids_k;
677 const RCP<ProductVectorBase<double>> thyraBlockResidual = rcp_dynamic_cast<ProductVectorBase<double> >(blockedContainer_->get_f(),true);
678
679 // Acquire the df/dp device views for the duration of this method only. See
680 // the release loop at the end of this method.
681 for(std::size_t i=0;i<dfdpVectors_.size();i++)
682 for(std::size_t j=0;j<dfdpVectors_[i].size();j++)
683 dfdpFieldsVoV_.addView(dfdpVectors_[i][j]->getLocalViewDevice(Tpetra::Access::ReadWrite),i,j);
684 dfdpFieldsVoV_.syncHostToDevice();
685
686 // Loop over scattered fields
687 int currentWorksetLIDSubBlock = -1;
688 for (std::size_t fieldIndex = 0; fieldIndex < scatterFields_.size(); fieldIndex++) {
689 // workset LIDs only change for different sub blocks
690 if (productVectorBlockIndex_[fieldIndex] != currentWorksetLIDSubBlock) {
691 fieldGlobalIndexers_[fieldIndex]->getElementLIDs(localCellIds,worksetLIDs_);
692 currentWorksetLIDSubBlock = productVectorBlockIndex_[fieldIndex];
693 }
694
695 auto& tpetraResidual = *((rcp_dynamic_cast<Thyra::TpetraVector<RealType,LO,GO,NodeT>>(thyraBlockResidual->getNonconstVectorBlock(productVectorBlockIndex_[fieldIndex]),true))->getTpetraVector());
696 const auto& kokkosResidual = tpetraResidual.getLocalViewDevice(Tpetra::Access::ReadWrite);
697
698 // Class data fields for lambda capture
699 const auto& fieldOffsets = fieldOffsets_[fieldIndex];
700 const auto& worksetLIDs = worksetLIDs_;
701 const auto& fieldValues = scatterFields_[fieldIndex].get_static_view();
702 const auto& tangentFieldsDevice = dfdpFieldsVoV_.getViewDevice();
703 const auto& kokkosTangents = Kokkos::subview(tangentFieldsDevice,Kokkos::ALL(),productVectorBlockIndex_[fieldIndex]);
704 const std::size_t num_params = Sacado::dimension_scalar(fieldValues)-1;
705
706 Kokkos::parallel_for(Kokkos::RangePolicy<PHX::Device>(0,workset.num_cells), KOKKOS_LAMBDA (const int& cell) {
707 for(int basis=0; basis < static_cast<int>(fieldOffsets.size()); ++basis) {
708 const int lid = worksetLIDs(cell,fieldOffsets(basis));
709 Kokkos::atomic_add(&kokkosResidual(lid,0), fieldValues(cell,basis).val());
710 for(std::size_t i_param=0; i_param<num_params; i_param++)
711 Kokkos::atomic_add(&kokkosTangents(i_param)(lid,0), fieldValues(cell,basis).fastAccessDx(i_param));
712 }
713 });
714 }
715
716 // Release the df/dp device views. Holding a device view past the return of
717 // this method makes any subsequent host access to the same vector throw, e.g.
718 // AssemblyEngine::evaluateDirichletBCs() -> adjustForDirichletConditions().
719 for(std::size_t i=0;i<dfdpVectors_.size();i++)
720 for(std::size_t j=0;j<dfdpVectors_[i].size();j++)
721 dfdpFieldsVoV_.addView(Kokkos::View<RealType**,Kokkos::LayoutLeft,PHX::Device>(),i,j);
722}
723
724// **********************************************************************
725
726#endif
PHX::View< const int * > offsets
PHX::MDField< ScalarT, panzer::Cell, panzer::BASIS > field
A field to which we'll contribute, or in which we'll store, the result of computing this integral.
KokkosSparse::CrsMatrix< double, LO, PHX::Device, Kokkos::MemoryTraits< Kokkos::Unmanaged >, size_t > LocalMatrixType
Sub-block matrix type handed to the scatter kernel.
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 > &)
std::string block_id
DEPRECATED - use: getElementBlock()
FieldType
The type of discretization to use for a field pattern.