Viskores  1.0
AdvectAlgorithm.h
Go to the documentation of this file.
1 //============================================================================
2 // The contents of this file are covered by the Viskores license. See
3 // LICENSE.txt for details.
4 //
5 // By contributing to this file, all contributors agree to the Developer
6 // Certificate of Origin Version 1.1 (DCO 1.1) as stated in DCO.txt.
7 //============================================================================
8 
9 //============================================================================
10 // Copyright (c) Kitware, Inc.
11 // All rights reserved.
12 // See LICENSE.txt for details.
13 //
14 // This software is distributed WITHOUT ANY WARRANTY; without even
15 // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16 // PURPOSE. See the above copyright notice for more information.
17 //============================================================================
18 
19 #ifndef viskores_filter_flow_internal_AdvectAlgorithm_h
20 #define viskores_filter_flow_internal_AdvectAlgorithm_h
21 
22 
26 #ifdef VISKORES_ENABLE_MPI
29 #include <viskores/thirdparty/diy/diy.h>
30 #include <viskores/thirdparty/diy/mpi-cast.h>
31 #endif
32 
33 namespace viskores
34 {
35 namespace filter
36 {
37 namespace flow
38 {
39 namespace internal
40 {
41 
42 template <typename DSIType>
43 class AdvectAlgorithm
44 {
45 public:
46  using ParticleType = typename DSIType::PType;
47 
48  AdvectAlgorithm(const viskores::filter::flow::internal::BoundsMap& bm,
49  std::vector<DSIType>& blocks)
50  : Blocks(blocks)
51  , BoundsMap(bm)
52 #ifdef VISKORES_ENABLE_MPI
53  , Exchanger(this->Comm)
54  , Terminator(this->Comm)
55 #endif
56  , NumRanks(this->Comm.size())
57  , Rank(this->Comm.rank())
58  {
59  }
60 
61  void Execute(const viskores::cont::ArrayHandle<ParticleType>& seeds,
62  viskores::FloatDefault stepSize)
63  {
64  this->SetStepSize(stepSize);
65  this->SetSeeds(seeds);
66 
67  this->Go();
68  }
69 
70  viskores::cont::PartitionedDataSet GetOutput() const
71  {
73  for (const auto& b : this->Blocks)
74  {
76  if (b.GetOutput(ds))
77  output.AppendPartition(ds);
78  }
79  return output;
80  }
81 
82  void SetStepSize(viskores::FloatDefault stepSize) { this->StepSize = stepSize; }
83 
84  void SetSeeds(const viskores::cont::ArrayHandle<ParticleType>& seeds)
85  {
86  this->ClearParticles();
87 
88  viskores::Id n = seeds.GetNumberOfValues();
89  auto portal = seeds.ReadPortal();
90 
91  std::vector<std::vector<viskores::Id>> blockIDs;
92  std::vector<ParticleType> particles;
93  for (viskores::Id i = 0; i < n; i++)
94  {
95  const ParticleType p = portal.Get(i);
96  std::vector<viskores::Id> ids = this->BoundsMap.FindBlocks(p.GetPosition());
97 
98  //Note: For duplicate blocks, this will give the seeds to the rank that are first in the list.
99  if (!ids.empty())
100  {
101  auto ranks = this->BoundsMap.FindRank(ids[0]);
102  if (!ranks.empty() && this->Rank == ranks[0])
103  {
104  particles.emplace_back(p);
105  blockIDs.emplace_back(ids);
106  }
107  }
108  }
109  this->SetSeedArray(particles, blockIDs);
110  }
111 
112  virtual bool HaveWork()
113  {
114  const bool haveParticles = !this->Active.empty() || !this->Inactive.empty();
115 #ifndef VISKORES_ENABLE_MPI
116  return haveParticles;
117 #else
118  return haveParticles || this->Exchanger.HaveWork();
119 #endif
120  }
121 
122  virtual bool GetDone()
123  {
124 #ifndef VISKORES_ENABLE_MPI
125  return !this->HaveWork();
126 #else
127  return this->Terminator.Done();
128 #endif
129  }
130 
131  //Advect all the particles.
132  virtual void Go()
133  {
134  while (!this->GetDone())
135  {
136  std::vector<ParticleType> v;
137  viskores::Id blockId = -1;
138 
139  if (this->GetActiveParticles(v, blockId))
140  {
141  //make this a pointer to avoid the copy?
142  auto& block = this->GetDataSet(blockId);
143  DSIHelperInfo<ParticleType> bb(v, this->BoundsMap, this->ParticleBlockIDsMap);
144  block.Advect(bb, this->StepSize);
145  this->UpdateResult(bb);
146  }
147 
148  this->ExchangeParticles();
149  }
150  }
151 
152  virtual void ClearParticles()
153  {
154  this->Active.clear();
155  this->Inactive.clear();
156  this->ParticleBlockIDsMap.clear();
157  }
158 
159  DataSetIntegrator<DSIType, ParticleType>& GetDataSet(viskores::Id id)
160  {
161  for (auto& it : this->Blocks)
162  if (it.GetID() == id)
163  return it;
164 
165  throw viskores::cont::ErrorFilterExecution("Bad block");
166  }
167 
168  virtual void SetSeedArray(const std::vector<ParticleType>& particles,
169  const std::vector<std::vector<viskores::Id>>& blockIds)
170  {
171  VISKORES_ASSERT(particles.size() == blockIds.size());
172 
173  auto pit = particles.begin();
174  auto bit = blockIds.begin();
175  while (pit != particles.end() && bit != blockIds.end())
176  {
177  viskores::Id blockId0 = (*bit)[0];
178  this->ParticleBlockIDsMap[pit->GetID()] = *bit;
179  if (this->Active.find(blockId0) == this->Active.end())
180  this->Active[blockId0] = { *pit };
181  else
182  this->Active[blockId0].emplace_back(*pit);
183  pit++;
184  bit++;
185  }
186  }
187 
188  virtual bool GetActiveParticles(std::vector<ParticleType>& particles, viskores::Id& blockId)
189  {
190  particles.clear();
191  blockId = -1;
192  if (this->Active.empty())
193  return false;
194 
195  //If only one, return it.
196  if (this->Active.size() == 1)
197  {
198  blockId = this->Active.begin()->first;
199  particles = std::move(this->Active.begin()->second);
200  this->Active.clear();
201  }
202  else
203  {
204  //Find the blockId with the most particles.
205  std::size_t maxNum = 0;
206  auto maxIt = this->Active.end();
207  for (auto it = this->Active.begin(); it != this->Active.end(); it++)
208  {
209  auto sz = it->second.size();
210  if (sz > maxNum)
211  {
212  maxNum = sz;
213  maxIt = it;
214  }
215  }
216 
217  if (maxNum == 0)
218  {
219  this->Active.clear();
220  return false;
221  }
222 
223  blockId = maxIt->first;
224  particles = std::move(maxIt->second);
225  this->Active.erase(maxIt);
226  }
227 
228  return !particles.empty();
229  }
230 
231  void ExchangeParticles()
232  {
233 #ifndef VISKORES_ENABLE_MPI
234  this->SerialExchange();
235 #else
236  // MPI with only 1 rank.
237  if (this->NumRanks == 1)
238  this->SerialExchange();
239  else
240  {
241  std::vector<ParticleType> outgoing;
242  std::vector<viskores::Id> outgoingRanks;
243 
244  this->GetOutgoingParticles(outgoing, outgoingRanks);
245 
246  std::vector<ParticleType> incoming;
247  std::unordered_map<viskores::Id, std::vector<viskores::Id>> incomingBlockIDs;
248 
249  this->Exchanger.Exchange(
250  outgoing, outgoingRanks, this->ParticleBlockIDsMap, incoming, incomingBlockIDs);
251 
252  //Cleanup what was sent.
253  for (const auto& p : outgoing)
254  this->ParticleBlockIDsMap.erase(p.GetID());
255 
256  this->UpdateActive(incoming, incomingBlockIDs);
257  }
258 
259  this->Terminator.Control(this->HaveWork());
260 #endif
261  }
262 
263  void SerialExchange()
264  {
265  for (const auto& p : this->Inactive)
266  {
267  const auto& bid = this->ParticleBlockIDsMap[p.GetID()];
268  VISKORES_ASSERT(!bid.empty());
269  this->Active[bid[0]].emplace_back(std::move(p));
270  }
271  this->Inactive.clear();
272  }
273 
274 #ifdef VISKORES_ENABLE_MPI
275  void GetOutgoingParticles(std::vector<ParticleType>& outgoing,
276  std::vector<viskores::Id>& outgoingRanks)
277  {
278  outgoing.clear();
279  outgoingRanks.clear();
280 
281  outgoing.reserve(this->Inactive.size());
282  outgoingRanks.reserve(this->Inactive.size());
283 
284  std::vector<ParticleType> particlesStaying;
285  std::unordered_map<viskores::Id, std::vector<viskores::Id>> particlesStayingBlockIDs;
286  //Send out Everything.
287  for (const auto& p : this->Inactive)
288  {
289  const auto& bid = this->ParticleBlockIDsMap[p.GetID()];
290  VISKORES_ASSERT(!bid.empty());
291 
292  auto ranks = this->BoundsMap.FindRank(bid[0]);
293  VISKORES_ASSERT(!ranks.empty());
294 
295  //Only 1 rank has the block.
296  if (ranks.size() == 1)
297  {
298  if (ranks[0] == this->Rank)
299  {
300  particlesStaying.emplace_back(p);
301  particlesStayingBlockIDs[p.GetID()] = this->ParticleBlockIDsMap[p.GetID()];
302  }
303  else
304  {
305  outgoing.emplace_back(p);
306  outgoingRanks.emplace_back(ranks[0]);
307  }
308  }
309  else
310  {
311  //Multiple ranks have the block, decide where it should go...
312 
313  //Random selection:
314  viskores::Id outRank = std::rand() % ranks.size();
315  if (outRank == this->Rank)
316  {
317  particlesStayingBlockIDs[p.GetID()] = this->ParticleBlockIDsMap[p.GetID()];
318  particlesStaying.emplace_back(p);
319  }
320  else
321  {
322  outgoing.emplace_back(p);
323  outgoingRanks.emplace_back(outRank);
324  }
325  }
326  }
327 
328  this->Inactive.clear();
329  VISKORES_ASSERT(outgoing.size() == outgoingRanks.size());
330 
331  VISKORES_ASSERT(particlesStaying.size() == particlesStayingBlockIDs.size());
332  if (!particlesStaying.empty())
333  this->UpdateActive(particlesStaying, particlesStayingBlockIDs);
334  }
335 #endif
336 
337  virtual void UpdateActive(
338  const std::vector<ParticleType>& particles,
339  const std::unordered_map<viskores::Id, std::vector<viskores::Id>>& idsMap)
340  {
341  VISKORES_ASSERT(particles.size() == idsMap.size());
342 
343  if (!particles.empty())
344  {
345  for (auto pit = particles.begin(); pit != particles.end(); pit++)
346  {
347  viskores::Id particleID = pit->GetID();
348  const auto& it = idsMap.find(particleID);
349  VISKORES_ASSERT(it != idsMap.end() && !it->second.empty());
350  viskores::Id blockId = it->second[0];
351  this->Active[blockId].emplace_back(*pit);
352  }
353 
354  for (const auto& it : idsMap)
355  this->ParticleBlockIDsMap[it.first] = it.second;
356  }
357  }
358 
359  virtual void UpdateInactive(
360  const std::vector<ParticleType>& particles,
361  const std::unordered_map<viskores::Id, std::vector<viskores::Id>>& idsMap)
362  {
363  VISKORES_ASSERT(particles.size() == idsMap.size());
364 
365  this->Inactive.insert(this->Inactive.end(), particles.begin(), particles.end());
366  for (const auto& it : idsMap)
367  this->ParticleBlockIDsMap[it.first] = it.second;
368  }
369 
370  viskores::Id UpdateResult(const DSIHelperInfo<ParticleType>& stuff)
371  {
372  this->UpdateActive(stuff.InBounds.Particles, stuff.InBounds.BlockIDs);
373  this->UpdateInactive(stuff.OutOfBounds.Particles, stuff.OutOfBounds.BlockIDs);
374 
375  viskores::Id numTerm = static_cast<viskores::Id>(stuff.TermID.size());
376  //Update terminated particles.
377  if (numTerm > 0)
378  {
379  for (const auto& id : stuff.TermID)
380  this->ParticleBlockIDsMap.erase(id);
381  }
382 
383  return numTerm;
384  }
385 
386  //Member data
387  // {blockId, std::vector of particles}
388  std::unordered_map<viskores::Id, std::vector<ParticleType>> Active;
389  std::vector<DSIType> Blocks;
390  viskores::filter::flow::internal::BoundsMap BoundsMap;
391  viskoresdiy::mpi::communicator Comm = viskores::cont::EnvironmentTracker::GetCommunicator();
392 #ifdef VISKORES_ENABLE_MPI
393  ParticleExchanger<ParticleType> Exchanger;
394  AdvectAlgorithmTerminator Terminator;
395 #endif
396  std::vector<ParticleType> Inactive;
397  viskores::Id MaxNumberOfSteps = 0;
398  viskores::Id NumRanks;
399  //{particleId : {block IDs}}
400  std::unordered_map<viskores::Id, std::vector<viskores::Id>> ParticleBlockIDsMap;
401  viskores::Id Rank;
402  viskores::FloatDefault StepSize;
403 };
404 
405 }
406 }
407 }
408 } //viskores::filter::flow::internal
409 
410 #endif //viskores_filter_flow_internal_AdvectAlgorithm_h
viskores::cont::ArrayHandle::ReadPortal
ReadPortalType ReadPortal() const
Get an array portal that can be used in the control environment.
Definition: ArrayHandle.h:447
BoundsMap.h
viskores::cont::DataSet
Contains and manages the geometric data structures that Viskores operates on.
Definition: DataSet.h:66
viskores::cont::ArrayHandle
Manages an array-worth of data.
Definition: ArrayHandle.h:313
DataSetIntegrator.h
viskores::cont::ErrorFilterExecution
This class is primarily intended to filters to throw in the control environment to indicate an execut...
Definition: ErrorFilterExecution.h:35
AdvectAlgorithmTerminator.h
viskores::Id
viskores::Int64 Id
Base type to use to index arrays.
Definition: Types.h:235
viskores
Groups connected points that have the same field value.
Definition: Atomic.h:27
viskores::cont::EnvironmentTracker::GetCommunicator
static const viskoresdiy::mpi::communicator & GetCommunicator()
viskores::cont::PartitionedDataSet::AppendPartition
void AppendPartition(const viskores::cont::DataSet &ds)
Add DataSet ds to the end of the list of partitions.
viskores::cont::ArrayHandle::GetNumberOfValues
viskores::Id GetNumberOfValues() const
Returns the number of entries in the array.
Definition: ArrayHandle.h:482
VISKORES_ASSERT
#define VISKORES_ASSERT(condition)
Definition: Assert.h:51
viskores::cont::PartitionedDataSet
Comprises a set of viskores::cont::DataSet objects.
Definition: PartitionedDataSet.h:34
PartitionedDataSet.h
viskores::FloatDefault
viskores::Float32 FloatDefault
The floating point type to use when no other precision is specified.
Definition: Types.h:244
ParticleExchanger.h