VTK-m  2.0
AdvectAlgorithm.h
Go to the documentation of this file.
1 //============================================================================
2 // Copyright (c) Kitware, Inc.
3 // All rights reserved.
4 // See LICENSE.txt for details.
5 //
6 // This software is distributed WITHOUT ANY WARRANTY; without even
7 // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8 // PURPOSE. See the above copyright notice for more information.
9 //============================================================================
10 
11 #ifndef vtk_m_filter_flow_internal_AdvectAlgorithm_h
12 #define vtk_m_filter_flow_internal_AdvectAlgorithm_h
13 
18 
19 namespace vtkm
20 {
21 namespace filter
22 {
23 namespace flow
24 {
25 namespace internal
26 {
27 
28 template <typename DSIType, template <typename> class ResultType, typename ParticleType>
29 class AdvectAlgorithm
30 {
31 public:
32  AdvectAlgorithm(const vtkm::filter::flow::internal::BoundsMap& bm, std::vector<DSIType>& blocks)
33  : Blocks(blocks)
34  , BoundsMap(bm)
35  , NumRanks(this->Comm.size())
36  , Rank(this->Comm.rank())
37  {
38  }
39 
40  void Execute(vtkm::Id numSteps,
41  vtkm::FloatDefault stepSize,
43  {
44  this->SetNumberOfSteps(numSteps);
45  this->SetStepSize(stepSize);
46  this->SetSeeds(seeds);
47  this->Go();
48  }
49 
50  vtkm::cont::PartitionedDataSet GetOutput() const
51  {
53 
54  for (const auto& b : this->Blocks)
55  {
57  if (b.template GetOutput<ParticleType>(ds))
58  output.AppendPartition(ds);
59  }
60 
61  return output;
62  }
63 
64  void SetStepSize(vtkm::FloatDefault stepSize) { this->StepSize = stepSize; }
65  void SetNumberOfSteps(vtkm::Id numSteps) { this->NumberOfSteps = numSteps; }
66  void SetSeeds(const vtkm::cont::ArrayHandle<ParticleType>& seeds)
67  {
68  this->ClearParticles();
69 
70  vtkm::Id n = seeds.GetNumberOfValues();
71  auto portal = seeds.ReadPortal();
72 
73  std::vector<std::vector<vtkm::Id>> blockIDs;
74  std::vector<ParticleType> particles;
75  for (vtkm::Id i = 0; i < n; i++)
76  {
77  const ParticleType p = portal.Get(i);
78  std::vector<vtkm::Id> ids = this->BoundsMap.FindBlocks(p.GetPosition());
79 
80  if (!ids.empty() && this->BoundsMap.FindRank(ids[0]) == this->Rank)
81  {
82  particles.emplace_back(p);
83  blockIDs.emplace_back(ids);
84  }
85  }
86 
87  this->SetSeedArray(particles, blockIDs);
88  }
89 
90  //Advect all the particles.
91  virtual void Go()
92  {
93  vtkm::filter::flow::internal::ParticleMessenger<ParticleType> messenger(
94  this->Comm, this->BoundsMap, 1, 128);
95 
96  vtkm::Id nLocal = static_cast<vtkm::Id>(this->Active.size() + this->Inactive.size());
97  this->ComputeTotalNumParticles(nLocal);
98 
99  while (this->TotalNumTerminatedParticles < this->TotalNumParticles)
100  {
101  std::vector<ParticleType> v;
102  vtkm::Id numTerm = 0, blockId = -1;
103  if (this->GetActiveParticles(v, blockId))
104  {
105  //make this a pointer to avoid the copy?
106  auto& block = this->GetDataSet(blockId);
107  DSIHelperInfoType bb =
108  DSIHelperInfo<ParticleType>(v, this->BoundsMap, this->ParticleBlockIDsMap);
109  block.Advect(bb, this->StepSize, this->NumberOfSteps);
110  numTerm = this->UpdateResult(bb.Get<DSIHelperInfo<ParticleType>>());
111  }
112 
113  vtkm::Id numTermMessages = 0;
114  this->Communicate(messenger, numTerm, numTermMessages);
115 
116  this->TotalNumTerminatedParticles += (numTerm + numTermMessages);
117  if (this->TotalNumTerminatedParticles > this->TotalNumParticles)
118  throw vtkm::cont::ErrorFilterExecution("Particle count error");
119  }
120  }
121 
122 
123  virtual void ClearParticles()
124  {
125  this->Active.clear();
126  this->Inactive.clear();
127  this->ParticleBlockIDsMap.clear();
128  }
129 
130  void ComputeTotalNumParticles(const vtkm::Id& numLocal)
131  {
132  long long total = static_cast<long long>(numLocal);
133 #ifdef VTKM_ENABLE_MPI
134  MPI_Comm mpiComm = vtkmdiy::mpi::mpi_cast(this->Comm.handle());
135  MPI_Allreduce(MPI_IN_PLACE, &total, 1, MPI_LONG_LONG, MPI_SUM, mpiComm);
136 #endif
137  this->TotalNumParticles = static_cast<vtkm::Id>(total);
138  }
139 
140  DataSetIntegrator<DSIType>& GetDataSet(vtkm::Id id)
141  {
142  for (auto& it : this->Blocks)
143  if (it.GetID() == id)
144  return it;
145 
146  throw vtkm::cont::ErrorFilterExecution("Bad block");
147  }
148 
149  virtual void SetSeedArray(const std::vector<ParticleType>& particles,
150  const std::vector<std::vector<vtkm::Id>>& blockIds)
151  {
152  VTKM_ASSERT(particles.size() == blockIds.size());
153 
154  auto pit = particles.begin();
155  auto bit = blockIds.begin();
156  while (pit != particles.end() && bit != blockIds.end())
157  {
158  this->ParticleBlockIDsMap[pit->GetID()] = *bit;
159  pit++;
160  bit++;
161  }
162 
163  this->Active.insert(this->Active.end(), particles.begin(), particles.end());
164  }
165 
166  virtual bool GetActiveParticles(std::vector<ParticleType>& particles, vtkm::Id& blockId)
167  {
168  particles.clear();
169  blockId = -1;
170  if (this->Active.empty())
171  return false;
172 
173  blockId = this->ParticleBlockIDsMap[this->Active.front().GetID()][0];
174  auto it = this->Active.begin();
175  while (it != this->Active.end())
176  {
177  auto p = *it;
178  if (blockId == this->ParticleBlockIDsMap[p.GetID()][0])
179  {
180  particles.emplace_back(p);
181  it = this->Active.erase(it);
182  }
183  else
184  it++;
185  }
186 
187  return !particles.empty();
188  }
189 
190  virtual void Communicate(vtkm::filter::flow::internal::ParticleMessenger<ParticleType>& messenger,
191  vtkm::Id numLocalTerminations,
192  vtkm::Id& numTermMessages)
193  {
194  std::vector<ParticleType> incoming;
195  std::unordered_map<vtkm::Id, std::vector<vtkm::Id>> incomingIDs;
196  numTermMessages = 0;
197  messenger.Exchange(this->Inactive,
198  this->ParticleBlockIDsMap,
199  numLocalTerminations,
200  incoming,
201  incomingIDs,
202  numTermMessages,
203  this->GetBlockAndWait(numLocalTerminations));
204 
205  this->Inactive.clear();
206  this->UpdateActive(incoming, incomingIDs);
207  }
208 
209  virtual void UpdateActive(const std::vector<ParticleType>& particles,
210  const std::unordered_map<vtkm::Id, std::vector<vtkm::Id>>& idsMap)
211  {
212  this->Update(this->Active, particles, idsMap);
213  }
214 
215  virtual void UpdateInactive(const std::vector<ParticleType>& particles,
216  const std::unordered_map<vtkm::Id, std::vector<vtkm::Id>>& idsMap)
217  {
218  this->Update(this->Inactive, particles, idsMap);
219  }
220 
221  void Update(std::vector<ParticleType>& arr,
222  const std::vector<ParticleType>& particles,
223  const std::unordered_map<vtkm::Id, std::vector<vtkm::Id>>& idsMap)
224  {
225  VTKM_ASSERT(particles.size() == idsMap.size());
226 
227  arr.insert(arr.end(), particles.begin(), particles.end());
228  for (const auto& it : idsMap)
229  this->ParticleBlockIDsMap[it.first] = it.second;
230  }
231 
232  vtkm::Id UpdateResult(const DSIHelperInfo<ParticleType>& stuff)
233  {
234  this->UpdateActive(stuff.A, stuff.IdMapA);
235  this->UpdateInactive(stuff.I, stuff.IdMapI);
236 
237  vtkm::Id numTerm = static_cast<vtkm::Id>(stuff.TermID.size());
238  //Update terminated particles.
239  if (numTerm > 0)
240  {
241  for (const auto& id : stuff.TermID)
242  this->ParticleBlockIDsMap.erase(id);
243  }
244 
245  return numTerm;
246  }
247 
248  virtual bool GetBlockAndWait(const vtkm::Id& numLocalTerm)
249  {
250  //There are only two cases where blocking would deadlock.
251  //1. There are active particles.
252  //2. numLocalTerm + this->TotalNumberOfTerminatedParticles == this->TotalNumberOfParticles
253  //So, if neither are true, we can safely block and wait for communication to come in.
254 
255  if (this->Active.empty() && this->Inactive.empty() &&
256  (numLocalTerm + this->TotalNumTerminatedParticles < this->TotalNumParticles))
257  {
258  return true;
259  }
260 
261  return false;
262  }
263 
264  //Member data
265  std::vector<ParticleType> Active;
266  std::vector<DSIType> Blocks;
267  vtkm::filter::flow::internal::BoundsMap BoundsMap;
268  vtkmdiy::mpi::communicator Comm = vtkm::cont::EnvironmentTracker::GetCommunicator();
269  std::vector<ParticleType> Inactive;
270  vtkm::Id NumberOfSteps;
271  vtkm::Id NumRanks;
272  std::unordered_map<vtkm::Id, std::vector<vtkm::Id>> ParticleBlockIDsMap;
273  vtkm::Id Rank;
274  vtkm::FloatDefault StepSize;
275  vtkm::Id TotalNumParticles = 0;
276  vtkm::Id TotalNumTerminatedParticles = 0;
277 };
278 
279 }
280 }
281 }
282 } //vtkm::filter::flow::internal
283 
284 #endif //vtk_m_filter_flow_internal_AdvectAlgorithm_h
vtkm::cont::ArrayHandle::GetNumberOfValues
VTKM_CONT vtkm::Id GetNumberOfValues() const
Returns the number of entries in the array.
Definition: ArrayHandle.h:448
vtkm::cont::ArrayHandle< ParticleType >
vtkm::cont::EnvironmentTracker::GetCommunicator
static const VTKM_CONT vtkmdiy::mpi::communicator & GetCommunicator()
BoundsMap.h
vtkm
Groups connected points that have the same field value.
Definition: Atomic.h:19
VTKM_ASSERT
#define VTKM_ASSERT(condition)
Definition: Assert.h:43
vtkm::cont::ErrorFilterExecution
This class is primarily intended to filters to throw in the control environment to indicate an execut...
Definition: ErrorFilterExecution.h:27
vtkm::cont::DataSet
Definition: DataSet.h:34
DataSetIntegrator.h
ParticleMessenger.h
vtkm::Id
vtkm::Int32 Id
Represents an ID (index into arrays).
Definition: Types.h:191
vtkm::cont::PartitionedDataSet::AppendPartition
VTKM_CONT void AppendPartition(const vtkm::cont::DataSet &ds)
Add DataSet ds to the end of the contained DataSet vector.
vtkm::FloatDefault
vtkm::Float32 FloatDefault
The floating point type to use when no other precision is specified.
Definition: Types.h:198
PartitionedDataSet.h
vtkm::cont::ArrayHandle::ReadPortal
VTKM_CONT ReadPortalType ReadPortal() const
Get an array portal that can be used in the control environment.
Definition: ArrayHandle.h:414
vtkm::cont::PartitionedDataSet
Definition: PartitionedDataSet.h:25