SPH
Storage.cpp
Go to the documentation of this file.
1 #include "quantities/Storage.h"
2 #include "objects/Exceptions.h"
3 #include "physics/Eos.h"
4 #include "quantities/IMaterial.h"
5 #include "quantities/Iterate.h"
6 #include "system/Factory.h"
7 #include "system/Profiler.h"
8 
10 
12  : iter(iterator) {}
13 
15  ++iter;
16  return *this;
17 }
18 
20  return { iter->key, iter->value };
21 }
22 
23 bool StorageIterator::operator==(const StorageIterator& other) const {
24  return iter == other.iter;
25 }
26 
27 bool StorageIterator::operator!=(const StorageIterator& other) const {
28  return iter != other.iter;
29 }
30 
32  : iter(iterator) {}
33 
35  ++iter;
36  return *this;
37 }
38 
40  return { iter->key, iter->value };
41 }
42 
44  return iter == other.iter;
45 }
46 
48  return iter != other.iter;
49 }
50 
52  : quantities(quantities) {}
53 
55  return StorageIterator(quantities.begin(), {});
56 }
57 
59  return StorageIterator(quantities.end(), {});
60 }
61 
63  return quantities.size();
64 }
65 
67  : quantities(quantities) {}
68 
70  return ConstStorageIterator(quantities.begin(), {});
71 }
72 
74  return ConstStorageIterator(quantities.end(), {});
75 }
76 
78  return quantities.size();
79 }
80 
82  : Exception("Invalid storage access to quantity " + getMetadata(id).quantityName) {}
83 
84 InvalidStorageAccess::InvalidStorageAccess(const std::string& message)
85  : Exception("Invalid storage access. " + message) {}
86 
87 static void checkStorageAccess(const bool result, const QuantityId id) {
88  if (!result) {
89  throw InvalidStorageAccess(id);
90  }
91 }
92 
93 static void checkStorageAccess(const bool result, const std::string& message) {
94  if (!result) {
95  throw InvalidStorageAccess(message);
96  }
97 }
98 
99 Storage::MatRange::MatRange(const SharedPtr<IMaterial>& material, const Size from, const Size to)
100  : material(material)
101  , from(from)
102  , to(to) {
103  SPH_ASSERT(from < to || (from == 0 && to == 0));
104 }
105 
106 Storage::Storage() = default;
107 
109  mats.emplaceBack(material, 0, 0);
110 }
111 
112 Storage::~Storage() = default;
113 
115  *this = std::move(other);
116 }
117 
119  quantities = std::move(other.quantities);
120  mats = std::move(other.mats);
121  dependent = std::move(other.dependent);
122  userData = std::move(other.userData);
123 
124  if (this->getParticleCnt() > 0) {
125  this->update();
126  }
127  return *this;
128 }
129 
130 bool Storage::has(const QuantityId key) const {
131  return quantities.contains(key);
132 }
133 
134 template <typename TValue>
135 bool Storage::has(const QuantityId key, const OrderEnum order) const {
136  Optional<const Quantity&> quantity = quantities.tryGet(key);
137  if (!quantity) {
138  return false;
139  }
140  return quantity->getOrderEnum() == order && quantity->getValueEnum() == GetValueEnum<TValue>::type;
141 }
142 
143 template bool Storage::has<Size>(const QuantityId, const OrderEnum) const;
144 template bool Storage::has<Float>(const QuantityId, const OrderEnum) const;
145 template bool Storage::has<Vector>(const QuantityId, const OrderEnum) const;
146 template bool Storage::has<SymmetricTensor>(const QuantityId, const OrderEnum) const;
147 template bool Storage::has<TracelessTensor>(const QuantityId, const OrderEnum) const;
148 template bool Storage::has<Tensor>(const QuantityId, const OrderEnum) const;
149 
151  Optional<Quantity&> quantity = quantities.tryGet(key);
152  checkStorageAccess(bool(quantity), key);
153  return quantity.value();
154 }
155 
156 const Quantity& Storage::getQuantity(const QuantityId key) const {
157  Optional<const Quantity&> quantity = quantities.tryGet(key);
158  checkStorageAccess(bool(quantity), key);
159  return quantity.value();
160 }
161 
162 template <typename TValue>
164  Quantity& q = this->getQuantity(key);
165  checkStorageAccess(q.getValueEnum() == GetValueEnum<TValue>::type, key);
166  return q.getAll<TValue>();
167 }
168 
175 
176 template <typename TValue>
178  const Quantity& q = this->getQuantity(key);
179  checkStorageAccess(q.getValueEnum() == GetValueEnum<TValue>::type, key);
180  return q.getAll<TValue>();
181 }
182 
183 template StaticArray<const Array<Size>&, 3> Storage::getAll(const QuantityId) const;
189 
190 template <typename TValue>
192  Quantity& q = this->getQuantity(key);
193  checkStorageAccess(q.getValueEnum() == GetValueEnum<TValue>::type, key);
194  return q.getValue<TValue>();
195 }
196 
197 template Array<Size>& Storage::getValue(const QuantityId);
198 template Array<Float>& Storage::getValue(const QuantityId);
203 
204 template <typename TValue>
205 const Array<TValue>& Storage::getValue(const QuantityId key) const {
206  return const_cast<Storage*>(this)->getValue<TValue>(key);
207 }
208 
209 template const Array<Size>& Storage::getValue(const QuantityId) const;
210 template const Array<Float>& Storage::getValue(const QuantityId) const;
211 template const Array<Vector>& Storage::getValue(const QuantityId) const;
212 template const Array<SymmetricTensor>& Storage::getValue(const QuantityId) const;
213 template const Array<TracelessTensor>& Storage::getValue(const QuantityId) const;
214 template const Array<Tensor>& Storage::getValue(const QuantityId) const;
215 
216 template <typename TValue>
218  Quantity& q = this->getQuantity(key);
219  checkStorageAccess(q.getValueEnum() == GetValueEnum<TValue>::type, key);
220  return q.getDt<TValue>();
221 }
222 
223 template Array<Size>& Storage::getDt(const QuantityId);
224 template Array<Float>& Storage::getDt(const QuantityId);
225 template Array<Vector>& Storage::getDt(const QuantityId);
228 template Array<Tensor>& Storage::getDt(const QuantityId);
229 
230 template <typename TValue>
231 const Array<TValue>& Storage::getDt(const QuantityId key) const {
232  return const_cast<Storage*>(this)->getDt<TValue>(key);
233 }
234 
235 template const Array<Size>& Storage::getDt(const QuantityId) const;
236 template const Array<Float>& Storage::getDt(const QuantityId) const;
237 template const Array<Vector>& Storage::getDt(const QuantityId) const;
238 template const Array<SymmetricTensor>& Storage::getDt(const QuantityId) const;
239 template const Array<TracelessTensor>& Storage::getDt(const QuantityId) const;
240 template const Array<Tensor>& Storage::getDt(const QuantityId) const;
241 
242 template <typename TValue>
244  Quantity& q = this->getQuantity(key);
245  checkStorageAccess(q.getValueEnum() == GetValueEnum<TValue>::type, key);
246  return q.getD2t<TValue>();
247 }
248 
249 template Array<Size>& Storage::getD2t(const QuantityId);
250 template Array<Float>& Storage::getD2t(const QuantityId);
251 template Array<Vector>& Storage::getD2t(const QuantityId);
254 template Array<Tensor>& Storage::getD2t(const QuantityId);
255 
256 template <typename TValue>
257 const Array<TValue>& Storage::getD2t(const QuantityId key) const {
258  return const_cast<Storage*>(this)->getD2t<TValue>(key);
259 }
260 
261 template const Array<Size>& Storage::getD2t(const QuantityId) const;
262 template const Array<Float>& Storage::getD2t(const QuantityId) const;
263 template const Array<Vector>& Storage::getD2t(const QuantityId) const;
264 template const Array<SymmetricTensor>& Storage::getD2t(const QuantityId) const;
265 template const Array<TracelessTensor>& Storage::getD2t(const QuantityId) const;
266 template const Array<Tensor>& Storage::getD2t(const QuantityId) const;
267 
268 
269 template <typename TValue>
270 Quantity& Storage::insert(const QuantityId key, const OrderEnum order, const TValue& defaultValue) {
271  SPH_ASSERT(isReal(defaultValue));
272  if (this->has(key)) {
273  Quantity& q = this->getQuantity(key);
274  checkStorageAccess(q.getValueEnum() == GetValueEnum<TValue>::type,
275  "Inserting quantity already stored with different type");
276 
277  if (q.getOrderEnum() < order) {
278  q.setOrder(order);
279  }
280  } else {
281  const Size particleCnt = getParticleCnt();
282  checkStorageAccess(particleCnt > 0, "Cannot insert quantity with default value to an empty storage.");
283  quantities.insert(key, Quantity(order, defaultValue, particleCnt));
284  }
285  return quantities[key];
286 }
287 
288 template Quantity& Storage::insert(const QuantityId, const OrderEnum, const Size&);
289 template Quantity& Storage::insert(const QuantityId, const OrderEnum, const Float&);
290 template Quantity& Storage::insert(const QuantityId, const OrderEnum, const Vector&);
291 template Quantity& Storage::insert(const QuantityId, const OrderEnum, const TracelessTensor&);
292 template Quantity& Storage::insert(const QuantityId, const OrderEnum, const SymmetricTensor&);
293 template Quantity& Storage::insert(const QuantityId, const OrderEnum, const Tensor&);
294 
295 template <typename TValue>
296 Quantity& Storage::insert(const QuantityId key, const OrderEnum order, Array<TValue>&& values) {
297  if (this->has(key)) {
298  checkStorageAccess(values.size() == this->getParticleCnt(),
299  "Size of input array must match number of particles in the storage.");
300 
301  Quantity& q = this->getQuantity(key);
302  checkStorageAccess(q.getValueEnum() == GetValueEnum<TValue>::type,
303  "Inserting quantity already stored with different type");
304 
305  if (q.getOrderEnum() < order) {
306  q.setOrder(order);
307  }
308  q.getValue<TValue>() = std::move(values);
309  if (key == QuantityId::MATERIAL_ID) {
310  // matIds view has been invalidated, cache again
311  this->update();
312  }
313  } else {
314  Quantity q(order, std::move(values));
315  const Size size = q.size();
316  quantities.insert(key, std::move(q));
317  checkStorageAccess(quantities.empty() || size == getParticleCnt(),
318  "Size of input array must match number of particles in the storage.");
319 
320  if (this->getQuantityCnt() == 1 && this->getMaterialCnt() > 0) {
321  // this is the first inserted quantity, initialize the 'internal' matId quantity
322  Quantity& quantity = this->insert<Size>(QuantityId::MATERIAL_ID, OrderEnum::ZERO, 0);
323  matIds = quantity.getValue<Size>();
324  SPH_ASSERT(this->getMaterialCnt() == 1);
325  mats[0].from = 0;
326  mats[0].to = this->getParticleCnt();
327  }
328  }
329  return quantities[key];
330 }
331 
332 template Quantity& Storage::insert(const QuantityId, const OrderEnum, Array<Size>&&);
333 template Quantity& Storage::insert(const QuantityId, const OrderEnum, Array<Float>&&);
334 template Quantity& Storage::insert(const QuantityId, const OrderEnum, Array<Vector>&&);
337 template Quantity& Storage::insert(const QuantityId, const OrderEnum, Array<Tensor>&&);
338 
340 #ifdef SPH_DEBUG
341  // check for a cycle - look for itself in a hierarchy
342  Function<bool(const Storage&)> checkDependent = [this, &checkDependent](const Storage& storage) {
343  for (const WeakPtr<Storage>& weakPtr : storage.dependent) {
344  if (SharedPtr<Storage> ptr = weakPtr.lock()) {
345  if (&*ptr == this) {
346  return false;
347  }
348  const bool retval = checkDependent(*ptr);
349  if (!retval) {
350  return false;
351  }
352  }
353  }
354  return true;
355  };
356  SPH_ASSERT(checkDependent(*this));
357  if (SharedPtr<Storage> sharedPtr = other.lock()) {
358  SPH_ASSERT(&*sharedPtr != this);
359  SPH_ASSERT(checkDependent(*sharedPtr));
360  }
361 #endif
362 
363  dependent.push(other);
364 }
365 
367  SPH_ASSERT(!mats.empty());
368  const MatRange& mat = mats[matId];
369  return MaterialView(mat.material.get(), IndexSequence(mat.from, mat.to));
370 }
371 
373  SPH_ASSERT(!mats.empty() && particleIdx < matIds.size());
374  return this->getMaterial(matIds[particleIdx]);
375 }
376 
377 void Storage::setMaterial(const Size matIdx, const SharedPtr<IMaterial>& material) {
378  if (matIdx >= mats.size()) {
379  throw InvalidStorageAccess("No material with index " + std::to_string(matIdx));
380  }
381 
383  mats[matIdx].material = material;
384 }
385 
386 bool Storage::isHomogeneous(const BodySettingsId param) const {
387  if (mats.empty()) {
388  return true;
389  }
390  const MaterialView mat0 = this->getMaterial(0);
391  const Float value0 = mat0->getParam<Float>(param);
392  for (Size matId = 1; matId < this->getMaterialCnt(); ++matId) {
393  const MaterialView mat = this->getMaterial(matId);
394  const Float value = mat->getParam<Float>(param);
395  if (value != value0) {
396  return false;
397  }
398  }
399  return true;
400 }
401 
402 Interval Storage::getRange(const QuantityId id, const Size matIdx) const {
403  SPH_ASSERT(matIdx < mats.size());
404  return mats[matIdx].material->range(id);
405 }
406 
407 template <typename TValue>
409  Array<TValue> values;
410  for (Size matId = 0; matId < this->getMaterialCnt(); ++matId) {
411  values.push(this->getMaterial(matId)->getParam<TValue>(param));
412  }
413  return values;
414 }
415 
417  return StorageSequence(quantities, {});
418 }
419 
421  return ConstStorageSequence(quantities, {});
422 }
423 
424 void Storage::propagate(const Function<void(Storage& storage)>& functor) {
425  for (Size i = 0; i < dependent.size();) {
426  if (SharedPtr<Storage> storagePtr = dependent[i].lock()) {
427  functor(*storagePtr);
428  storagePtr->propagate(functor);
429  ++i;
430  } else {
431  // remove expired storage
432  dependent.remove(i);
433  }
434  }
435 }
436 
438  return mats.size();
439 }
440 
442  return quantities.size();
443 }
444 
446  if (quantities.empty()) {
447  return 0;
448  } else {
449  return quantities.begin()->value.size();
450  }
451 }
452 
453 bool Storage::empty() const {
454  return this->getParticleCnt() == 0;
455 }
456 
457 void Storage::addMissingBuffers(const Storage& source) {
458  const Size cnt = this->getParticleCnt();
459  for (ConstStorageElement element : source.getQuantities()) {
460  // add the quantity if it's missing
461  if (!this->has(element.id)) {
462  quantities.insert(element.id, element.quantity.createZeros(cnt));
463  }
464 
465  // if it has lower order, initialize the other buffers as well
466  if (quantities[element.id].getOrderEnum() < element.quantity.getOrderEnum()) {
467  quantities[element.id].setOrder(element.quantity.getOrderEnum());
468  }
469  }
470 }
471 
472 void Storage::merge(Storage&& other) {
473  SPH_ASSERT(!userData && !other.userData, "Merging storages with user data is currently not supported");
474 
475  // allow merging into empty storage for convenience
476  if (this->getQuantityCnt() == 0) {
477  *this = std::move(other);
478  return;
479  }
480 
481  // must have the same quantities
482  this->addMissingBuffers(other);
483  other.addMissingBuffers(*this);
484 
485  SPH_ASSERT(this->isValid() && other.isValid());
486 
487 
488  // make sure that either both have materials or neither
489  if (bool(this->getMaterialCnt()) != bool(other.getMaterialCnt())) {
490  Storage* withoutMat = bool(this->getMaterialCnt()) ? &other : this;
491  const BodySettings& body = BodySettings::getDefaults();
492  withoutMat->mats.emplaceBack(makeShared<NullMaterial>(body), 0, other.getParticleCnt());
494  }
495 
496  // update material intervals and cached matIds before merge
497  const Size partCnt = this->getParticleCnt();
498  for (MatRange& mat : other.mats) {
499  mat.from += partCnt;
500  mat.to += partCnt;
501  }
502  if (other.has(QuantityId::MATERIAL_ID)) {
503  const Size matCnt = this->getMaterialCnt();
504  for (Size& id : other.getValue<Size>(QuantityId::MATERIAL_ID)) {
505  id += matCnt;
506  }
507  }
508 
509  // merge all quantities
510  iteratePair<VisitorEnum::ALL_BUFFERS>(*this, other, [](auto& ar1, auto& ar2) { //
511  ar1.pushAll(std::move(ar2));
512  });
513 
514  // update persistent indices
515  if (this->has(QuantityId::PERSISTENT_INDEX)) {
516  ArrayView<Size> idxs = this->getValue<Size>(QuantityId::PERSISTENT_INDEX);
517  const Size idx0 = idxs[partCnt - 1] + 1; // next available index
518  for (Size i = partCnt; i < this->getParticleCnt(); ++i) {
519  idxs[i] = idx0 + (i - partCnt);
520  }
521  }
522 
523  // merge materials
524  this->mats.pushAll(std::move(other.mats));
525 
526  // remove duplicate materials (only consecutive, otherwise we would have to reorder particles)
527  for (Size matId = 1; matId < this->getMaterialCnt();) {
528  if (mats[matId].material == mats[matId - 1].material) {
529  // same material, we can merge them
530  mats[matId - 1].to = mats[matId].to;
531  mats.remove(matId);
532 
533  if (this->has(QuantityId::MATERIAL_ID)) {
534  ArrayView<Size> id = this->getValue<Size>(QuantityId::MATERIAL_ID);
535  for (Size i = 0; i < id.size(); ++i) {
536  if (id[i] >= matId) {
537  id[i]--;
538  }
539  }
540  }
541  } else {
542  ++matId;
543  }
544  }
545 
546  // cache the view
547  this->update();
548 
549  // since we moved the buffers away, remove all particles from other to keep it in consistent state
550  other.removeAll();
551 
552  // sanity check
553  SPH_ASSERT(this->isValid());
554 }
555 
557  iterate<VisitorEnum::HIGHEST_DERIVATIVES>(*this, [](const QuantityId, auto& dv) {
558  using TValue = typename std::decay_t<decltype(dv)>::Type;
559  dv.fill(TValue(0._f));
560  });
561 }
562 
564  SPH_ASSERT(!userData, "Cloning storages with user data is currently not supported");
565  Storage cloned;
566  for (const auto& q : quantities) {
567  cloned.quantities.insert(q.key, q.value.clone(buffers));
568  }
569 
570  // clone the materials if we cloned MATERIAL_IDs.
572  cloned.mats = this->mats.clone();
573  }
574 
575  cloned.update();
576  return cloned;
577 }
578 
579 void Storage::resize(const Size newParticleCnt, const Flags<ResizeFlag> flags) {
580  SPH_ASSERT(getQuantityCnt() > 0 && getMaterialCnt() <= 1);
581  SPH_ASSERT(!userData, "Cloning storages with user data is currently not supported");
582 
583  iterate<VisitorEnum::ALL_BUFFERS>(*this, [newParticleCnt, flags](auto& buffer) { //
584  if (!flags.has(ResizeFlag::KEEP_EMPTY_UNCHANGED) || !buffer.empty()) {
585  using Type = typename std::decay_t<decltype(buffer)>::Type;
586  buffer.resizeAndSet(newParticleCnt, Type(0._f));
587  }
588  });
589 
590  if (!mats.empty()) {
591  // can be only used for homogeneous storages
592  mats[0].to = newParticleCnt;
593  }
594 
595  this->propagate([newParticleCnt, flags](Storage& storage) { storage.resize(newParticleCnt, flags); });
596 
597  this->update();
598  SPH_ASSERT(this->isValid(
600 }
601 
602 void Storage::swap(Storage& other, const Flags<VisitorEnum> flags) {
603  SPH_ASSERT(this->getQuantityCnt() == other.getQuantityCnt());
604  for (auto i1 = quantities.begin(), i2 = other.quantities.begin(); i1 != quantities.end(); ++i1, ++i2) {
605  i1->value.swap(i2->value, flags);
606  }
607 }
608 
610  const Size cnt = this->getParticleCnt();
611  Outcome result = SUCCESS;
612  // check that all buffers have the same number of particles
613  iterate<VisitorEnum::ALL_BUFFERS>(*this, [cnt, &result, flags](const auto& buffer) {
614  if (buffer.size() != cnt && (flags.has(ValidFlag::COMPLETE) || !buffer.empty())) {
615  result = makeFailed("One or more buffers have different number of particles:\nExpected: ",
616  cnt,
617  ", actual: ",
618  buffer.size());
619  }
620  });
621  if (!result) {
622  return result;
623  }
624 
625  // check that materials are set up correctly
626  if (this->getMaterialCnt() == 0 || this->getQuantityCnt() == 0) {
627  // no materials are a valid state, all OK
628  return SUCCESS;
629  }
630  if (bool(matIds) != this->has(QuantityId::MATERIAL_ID)) {
631  // checks that either both matIds and quantity MATERIAL_ID exist, or neither of them do;
632  // if we cloned just some buffers, quantity MATERIAL_ID might not be there even though the storage has
633  // materials, so we don't report it as an error.
634  return makeFailed("MaterialID view not present");
635  }
636 
637  if (ArrayView<const Size>(matIds) != this->getValue<Size>(QuantityId::MATERIAL_ID)) {
638  // WTF did I cache?
639  return makeFailed(
640  "Cached view of MaterialID does not reference the stored quantity. Did you forget to call "
641  "update?");
642  }
643 
644  for (Size matId = 0; matId < mats.size(); ++matId) {
645  const MatRange& mat = mats[matId];
646  for (Size i = mat.from; i < mat.to; ++i) {
647  if (matIds[i] != matId) {
648  return makeFailed("MaterialID of particle does not belong to the material range.\nExpected: ",
649  matId,
650  ", actual: ",
651  matIds[i]);
652  }
653  }
654  if ((matId != mats.size() - 1) && (mat.to != mats[matId + 1].from)) {
655  return makeFailed("Material are not stored consecutively.\nLast index: ",
656  mat.to,
657  ", first index: ",
658  mats[matId + 1].from);
659  }
660  }
661  if (mats[0].from != 0 || mats[mats.size() - 1].to != cnt) {
662  return makeFailed("Materials do not cover all particles.\nFirst: ",
663  mats[0].from,
664  ", last: ",
665  mats[mats.size() - 1].to,
666  " (size: ",
667  cnt,
668  ").");
669  }
670 
671  return SUCCESS;
672 }
673 
675  SPH_ASSERT(!userData, "Duplicating particles in storages with user data is currently not supported");
676  MEASURE_SCOPE("Storage::duplicate");
677  Array<Size> sortedHolder;
678  ArrayView<const Size> sorted;
679  if (flags.has(IndicesFlag::INDICES_SORTED)) {
680  SPH_ASSERT(std::is_sorted(idxs.begin(), idxs.end()));
681  sorted = idxs;
682  } else {
683  sortedHolder.reserve(idxs.size());
684  sortedHolder.pushAll(idxs.begin(), idxs.end());
685  std::sort(sortedHolder.begin(), sortedHolder.end());
686  sorted = sortedHolder;
687  }
688 
689  Array<Size> createdIdxs;
690  if (this->has(QuantityId::MATERIAL_ID)) {
691 
692  // split by material
693  Array<Array<Size>> idxsPerMaterial(this->getMaterialCnt());
694  Array<Size>& matIdsRef = this->getValue<Size>(QuantityId::MATERIAL_ID);
695  for (Size i : sorted) {
696  idxsPerMaterial[matIdsRef[i]].push(i);
697  }
698 
699  // add the new values after the last value of each material
700  for (Array<Size>& idxs : reverse(idxsPerMaterial)) {
701  if (idxs.empty()) {
702  // no duplicates from this material
703  continue;
704  }
705  const Size matId = matIdsRef[idxs[0]];
706  iterate<VisitorEnum::ALL_BUFFERS>(*this, [this, &idxs, matId](auto& buffer) {
707  using Type = typename std::decay_t<decltype(buffer)>::Type;
708  Array<Type> duplicates;
709  for (Size i : idxs) {
710  Type value = buffer[i];
711  duplicates.push(value);
712  }
713  buffer.insert(mats[matId].to, duplicates.begin(), duplicates.end());
714  });
715 
716  for (Size& createdIdx : createdIdxs) {
717  createdIdx += idxs.size();
718  }
719  for (Size i = 0; i < idxs.size(); ++i) {
720  createdIdxs.push(mats[matId].to + i);
721  }
722  }
723 
724  // fix material ranges
725  SPH_ASSERT(std::is_sorted(matIdsRef.begin(), matIdsRef.end()));
726  for (Size matId = 0; matId < this->getMaterialCnt(); ++matId) {
727  std::pair<Iterator<Size>, Iterator<Size>> range =
728  std::equal_range(matIdsRef.begin(), matIdsRef.end(), matId);
729  mats[matId].from = std::distance(matIdsRef.begin(), range.first);
730  mats[matId].to = std::distance(matIdsRef.begin(), range.second);
731  }
732  } else {
733  // just duplicate the particles
734  const Size n0 = this->getParticleCnt();
735  for (Size i = 0; i < sorted.size(); ++i) {
736  createdIdxs.push(n0 + i);
737  }
738  iterate<VisitorEnum::ALL_BUFFERS>(*this, [&sorted](auto& buffer) {
739  using Type = typename std::decay_t<decltype(buffer)>::Type;
740  Array<Type> duplicates;
741  for (Size i : sorted) {
742  Type value = buffer[i];
743  duplicates.push(value);
744  }
745  buffer.pushAll(duplicates.cbegin(), duplicates.cend());
746  });
747  }
748 
749  this->update();
750  SPH_ASSERT(this->isValid(), this->isValid().error());
751 
752  std::sort(createdIdxs.begin(), createdIdxs.end());
753  return createdIdxs;
754 }
755 
757  SPH_ASSERT(!userData, "Removing particles from storages with user data is currently not supported");
758  if (idxs.empty()) {
759  // job well done!
760  return;
761  }
762 
763  ArrayView<const Size> sortedIdxs;
764  Array<Size> sortedHolder;
765  if (flags.has(IndicesFlag::INDICES_SORTED)) {
766  SPH_ASSERT(std::is_sorted(idxs.begin(), idxs.end()));
767  sortedIdxs = idxs;
768  } else {
769  sortedHolder.pushAll(idxs.begin(), idxs.end());
770  std::sort(sortedHolder.begin(), sortedHolder.end());
771  sortedIdxs = sortedHolder;
772  }
773 
774  iterate<VisitorEnum::ALL_BUFFERS>(*this, [&sortedIdxs](auto& buffer) { buffer.remove(sortedIdxs); });
775 
776  // update material ids
777  this->update();
778 
779  // regenerate material ranges
780  if (this->has(QuantityId::MATERIAL_ID)) {
781  Array<Size> matsToRemove;
782  for (Size matId = 0; matId < mats.size(); ++matId) {
783  Iterator<Size> from, to;
784  std::tie(from, to) = std::equal_range(matIds.begin(), matIds.end(), matId);
785  if (from != matIds.end() && *from == matId) {
786  // at least one particle from the material remained
787  mats[matId].from = Size(std::distance(matIds.begin(), from));
788  mats[matId].to = Size(std::distance(matIds.begin(), to));
789  } else {
790  // defer the removal to avoid changing indices
791  matsToRemove.push(matId);
792  }
793  }
794  mats.remove(matsToRemove);
795 
796  } else {
797  SPH_ASSERT(mats.empty());
798  }
799 
800  // in case some materials have been removed, we need to re-assign matIds
801  for (Size matId = 0; matId < mats.size(); ++matId) {
802  for (Size i = mats[matId].from; i < mats[matId].to; ++i) {
803  matIds[i] = matId;
804  }
805  }
806 
807  SPH_ASSERT(this->isValid(), this->isValid().error());
808 }
809 
811  SPH_ASSERT(!userData, "Removing particles from storages with user data is currently not supported");
812  this->propagate([](Storage& storage) { storage.removeAll(); });
813  *this = Storage();
814 }
815 
816 void Storage::update() {
817  if (this->has(QuantityId::MATERIAL_ID)) {
818  matIds = this->getValue<Size>(QuantityId::MATERIAL_ID);
819  } else {
820  matIds = nullptr;
821  }
822 }
823 
825  userData = std::move(newData);
826 }
827 
829  return userData;
830 }
831 
832 Box getBoundingBox(const Storage& storage, const Float radius) {
833  Box box;
835  for (Size i = 0; i < r.size(); ++i) {
836  box.extend(r[i] + radius * Vector(r[i][H]));
837  box.extend(r[i] - radius * Vector(r[i][H]));
838  }
839  return box;
840 }
841 
842 Vector getCenterOfMass(const Storage& storage) {
844  if (storage.has(QuantityId::MASS)) {
846 
847  Float m_sum = 0._f;
848  Vector r_com(0._f);
849  for (Size i = 0; i < r.size(); ++i) {
850  m_sum += m[i];
851  r_com += m[i] * r[i];
852  }
853  r_com[H] = 0._f;
854  return r_com / m_sum;
855  } else {
856  Vector r_com(0._f);
857  for (Size i = 0; i < r.size(); ++i) {
858  r_com += r[i];
859  }
860  r_com[H] = 0._f;
861  return r_com / r.size();
862  }
863 }
864 
866  const Size n = storage.getParticleCnt();
867  Array<Size> idxs(n);
868  for (Size i = 0; i < n; ++i) {
869  idxs[i] = i;
870  }
871  storage.insert<Size>(QuantityId::PERSISTENT_INDEX, OrderEnum::ZERO, std::move(idxs));
872 }
873 
874 
INLINE bool isReal(const AntisymmetricTensor &t)
#define SPH_ASSERT(x,...)
Definition: Assert.h:94
NAMESPACE_SPH_BEGIN
Definition: BarnesHut.cpp:13
const float radius
Definition: CurveDialog.cpp:18
Equations of state.
uint32_t Size
Integral type used to index arrays (by default).
Definition: Globals.h:16
double Float
Precision used withing the code. Use Float instead of float or double where precision is important.
Definition: Globals.h:13
Base class for all particle materials.
Functions for iterating over individual quantities in Storage.
ReverseAdapter< TContainer > reverse(TContainer &&container)
#define NAMESPACE_SPH_END
Definition: Object.h:12
const SuccessTag SUCCESS
Global constant for successful outcome.
Definition: Outcome.h:141
INLINE Outcome makeFailed(TArgs &&... args)
Constructs failed object with error message.
Definition: Outcome.h:157
Tool to measure time spent in functions and profile the code.
#define MEASURE_SCOPE(name)
Definition: Profiler.h:70
QuantityMetadata getMetadata(const QuantityId key)
Returns the quantity information using quantity ID.
Definition: QuantityIds.cpp:27
QuantityId
Unique IDs of basic quantities of SPH particles.
Definition: QuantityIds.h:19
@ POSITION
Positions (velocities, accelerations) of particles, always a vector quantity,.
@ MASS
Paricles masses, always a scalar quantity.
@ MATERIAL_ID
Index of material of the particle. Can be generally different than the flag value.
OrderEnum
Definition: Quantity.h:15
@ ZERO
Quantity without derivatives, or "zero order" of quantity.
StaticArray< T0 &, sizeof...(TArgs)+1 > tie(T0 &t0, TArgs &... rest)
Creates a static array from a list of l-value references.
Definition: StaticArray.h:281
Vector getCenterOfMass(const Storage &storage)
Returns the center of mass of all particles.
Definition: Storage.cpp:842
Box getBoundingBox(const Storage &storage, const Float radius)
Convenience function to get the bounding box of all particles.
Definition: Storage.cpp:832
void setPersistentIndices(Storage &storage)
Adds or updates a quantity holding particle indices to the storage.
Definition: Storage.cpp:865
Container for storing particle quantities and materials.
INLINE Float distance(const Vector &r, const Vector &axis)
Returns the distance of vector from given axis. The axis is assumed to be normalized.
Definition: Vector.h:827
BasicVector< Float > Vector
Definition: Vector.h:539
@ H
Definition: Vector.h:25
INLINE bool empty() const
Definition: ArrayView.h:105
INLINE TCounter size() const
Definition: ArrayView.h:101
INLINE Iterator< StorageType > begin()
Definition: ArrayView.h:55
INLINE Iterator< StorageType > end()
Definition: ArrayView.h:63
INLINE Iterator< const StorageType > cbegin() const noexcept
Definition: Array.h:458
void reserve(const TCounter newMaxSize)
Allocates enough memory to store the given number of elements.
Definition: Array.h:279
INLINE Iterator< StorageType > end() noexcept
Definition: Array.h:462
INLINE void push(U &&u)
Adds new element to the end of the array, resizing the array if necessary.
Definition: Array.h:306
StorageType & emplaceBack(TArgs &&... args)
Constructs a new element at the end of the array in place, using the provided arguments.
Definition: Array.h:332
void remove(const TCounter idx)
Removes an element with given index from the array.
Definition: Array.h:383
INLINE TCounter size() const noexcept
Definition: Array.h:193
INLINE bool empty() const noexcept
Definition: Array.h:201
INLINE Iterator< const StorageType > cend() const noexcept
Definition: Array.h:470
INLINE Iterator< StorageType > begin() noexcept
Definition: Array.h:450
void pushAll(const TIter first, const TIter last)
Definition: Array.h:312
Array clone() const
Performs a deep copy of all elements of the array.
Definition: Array.h:147
Helper class used to allow calling a function only from within T.
Definition: Object.h:94
Helper object defining three-dimensional interval (box).
Definition: Box.h:17
INLINE void extend(const Vector &v)
Enlarges the box to contain the vector.
Definition: Box.h:49
Helper class for iterating over quantities stored in Storage, const version.
Definition: Storage.h:59
bool operator!=(const ConstStorageIterator &other) const
Definition: Storage.cpp:47
bool operator==(const ConstStorageIterator &other) const
Definition: Storage.cpp:43
ConstStorageIterator & operator++()
Definition: Storage.cpp:34
ConstStorageElement operator*()
Definition: Storage.cpp:39
ConstStorageIterator(const ActIterator iterator, Badge< ConstStorageSequence >)
Definition: Storage.cpp:31
Helper class, provides functions begin and end, returning const iterators to the first and last quant...
Definition: Storage.h:101
ConstStorageSequence(const FlatMap< QuantityId, Quantity > &quantities, Badge< Storage >)
Definition: Storage.cpp:66
Size size() const
Returns the number of quantities.
Definition: Storage.cpp:77
ConstStorageIterator end()
Returns iterator pointing to the one-past-the-end element of the quantity storage.
Definition: Storage.cpp:73
ConstStorageIterator begin()
Returns iterator pointing to the beginning of the quantity storage.
Definition: Storage.cpp:69
Generic exception.
Definition: Exceptions.h:10
Wrapper of an integral value providing functions for reading and modifying individual bits.
Definition: Flags.h:20
constexpr INLINE bool has(const TEnum flag) const
Checks if the object has a given flag.
Definition: Flags.h:77
INLINE Iterator< Element > end()
Returns the iterator pointing to the one-past-last element.
Definition: FlatMap.h:165
INLINE Size empty() const
Returns true if the map contains no elements, false otherwise.
Definition: FlatMap.h:150
INLINE bool contains(const TKey &key) const
Returns true if the map contains element of given key.
Definition: FlatMap.h:140
INLINE TValue & insert(const TKey &key, const TValue &value)
Adds a new element into the map or sets new value of element with the same key.
Definition: FlatMap.h:65
INLINE Iterator< Element > begin()
Returns the iterator pointing to the first element.
Definition: FlatMap.h:155
INLINE Optional< TValue & > tryGet(const TKey &key)
Returns a reference to the value matching the given key, or NOTHING if no such value exists.
Definition: FlatMap.h:118
INLINE Size size() const
Returns the number of elements in the map.
Definition: FlatMap.h:145
Object representing a 1D interval of real numbers.
Definition: Interval.h:17
Exception thrown when accessing missing quantities, casting to different types, etc.
Definition: Storage.h:125
InvalidStorageAccess(const QuantityId id)
Definition: Storage.cpp:81
Non-owning wrapper of a material and particles with this material.
Definition: IMaterial.h:30
Wrapper of type value of which may or may not be present.
Definition: Optional.h:23
INLINE Type & value()
Returns the reference to the stored value.
Definition: Optional.h:172
Generic container for storing scalar, vector or tensor quantity and its derivatives.
Definition: Quantity.h:200
INLINE Size size() const
Returns the size of the quantity (number of particles)
Definition: Quantity.h:278
void setOrder(const OrderEnum order)
Definition: Quantity.h:297
OrderEnum getOrderEnum() const
Returns the order of the quantity.
Definition: Quantity.h:241
INLINE Array< TValue > & getD2t()
Returns a reference to array of second derivatives of quantity.
Definition: Quantity.h:323
StaticArray< Array< TValue > &, 3 > getAll()
Returns all buffers of given type stored in this quantity.
Definition: Quantity.h:338
INLINE Array< TValue > & getValue()
Returns a reference to array of quantity values.
Definition: Quantity.h:287
INLINE Array< TValue > & getDt()
Returns a reference to array of first derivatives of quantity.
Definition: Quantity.h:307
ValueEnum getValueEnum() const
Returns the value order of the quantity.
Definition: Quantity.h:246
static const Settings & getDefaults()
\brief Returns a reference to object containing default values of all settings.
Array with fixed number of allocated elements.
Definition: StaticArray.h:19
Helper class for iterating over quantities stored in Storage.
Definition: Storage.h:40
StorageIterator & operator++()
Definition: Storage.cpp:14
bool operator!=(const StorageIterator &other) const
Definition: Storage.cpp:27
StorageIterator(const ActIterator iterator, Badge< StorageSequence >)
Definition: Storage.cpp:11
StorageElement operator*()
Definition: Storage.cpp:19
bool operator==(const StorageIterator &other) const
Definition: Storage.cpp:23
Helper class, provides functions begin and end, returning iterators to the first and last quantity in...
Definition: Storage.h:79
Size size() const
Returns the number of quantities.
Definition: Storage.cpp:62
StorageSequence(FlatMap< QuantityId, Quantity > &quantities, Badge< Storage >)
Definition: Storage.cpp:51
StorageIterator end()
Returns iterator pointing to the one-past-the-end element of the quantity storage.
Definition: Storage.cpp:58
StorageIterator begin()
Returns iterator pointing to the beginning of the quantity storage.
Definition: Storage.cpp:54
Container storing all quantities used within the simulations.
Definition: Storage.h:230
Size getMaterialCnt() const
Return the number of materials in the storage.
Definition: Storage.cpp:437
Array< TValue > getMaterialParams(const BodySettingsId param) const
Returns the given material parameter for all materials in the storage.
Definition: Storage.cpp:408
bool isHomogeneous(const BodySettingsId param) const
Checks if the particles in the storage are homogeneous with respect to given parameter.
Definition: Storage.cpp:386
void merge(Storage &&other)
Merges another storage into this object.
Definition: Storage.cpp:472
Quantity & getQuantity(const QuantityId key)
Retrieves quantity with given key from the storage.
Definition: Storage.cpp:150
void resize(const Size newParticleCnt, const Flags< ResizeFlag > flags=EMPTY_FLAGS)
Changes number of particles for all quantities stored in the storage.
Definition: Storage.cpp:579
Outcome isValid(const Flags< ValidFlag > flags=ValidFlag::COMPLETE) const
Checks whether the storage is in valid state.
Definition: Storage.cpp:609
void setMaterial(const Size matIdx, const SharedPtr< IMaterial > &material)
Modifies material with given index.
Definition: Storage.cpp:377
Quantity & insert(const QuantityId key, const OrderEnum order, const TValue &defaultValue)
Creates a quantity in the storage, given its key, value type and order.
Definition: Storage.cpp:270
@ COMPLETE
Checks that the storage is complete, i.e. there are no empty buffers.
MaterialView getMaterialOfParticle(const Size particleIdx) const
Returns material view for material of given particle.
Definition: Storage.cpp:372
bool empty() const
Checks if the storage is empty, i.e. without particles.
Definition: Storage.cpp:453
void addDependent(const WeakPtr< Storage > &other)
Registers a dependent storage.
Definition: Storage.cpp:339
StaticArray< Array< TValue > &, 3 > getAll(const QuantityId key)
Retrieves quantity buffers from the storage, given its key and value type.
Definition: Storage.cpp:163
void swap(Storage &other, const Flags< VisitorEnum > flags)
Swap quantities or given subset of quantities between two storages.
Definition: Storage.cpp:602
Storage()
Creates a storage with no material.
Array< TValue > & getDt(const QuantityId key)
Retrieves a quantity derivative from the storage, given its key and value type.
Definition: Storage.cpp:217
void removeAll()
Removes all particles with all quantities (including materials) from the storage.
Definition: Storage.cpp:810
void setUserData(SharedPtr< IStorageUserData > newData)
Stores new user data into the storage.
Definition: Storage.cpp:824
Size getParticleCnt() const
Returns the number of particles.
Definition: Storage.cpp:445
StorageSequence getQuantities()
Returns the sequence of quantities.
Definition: Storage.cpp:416
Size getQuantityCnt() const
Returns the number of stored quantities.
Definition: Storage.cpp:441
void propagate(const Function< void(Storage &storage)> &functor)
Executes a given functor recursively for all dependent storages.
Definition: Storage.cpp:424
Array< Size > duplicate(ArrayView< const Size > idxs, const Flags< IndicesFlag > flags=EMPTY_FLAGS)
Duplicates some particles in the storage.
Definition: Storage.cpp:674
SharedPtr< IStorageUserData > getUserData() const
Returns the stored user data.
Definition: Storage.cpp:828
Interval getRange(const QuantityId id, const Size matIdx) const
Returns the bounding range of given quantity.
Definition: Storage.cpp:402
@ INDICES_SORTED
Use if the given array is already sorted (optimization)
void remove(ArrayView< const Size > idxs, const Flags< IndicesFlag > flags=EMPTY_FLAGS)
Removes specified particles from the storage.
Definition: Storage.cpp:756
Storage & operator=(Storage &&other)
Definition: Storage.cpp:118
Array< TValue > & getD2t(const QuantityId key)
Retrieves a quantity second derivative from the storage, given its key and value type.
Definition: Storage.cpp:243
MaterialView getMaterial(const Size matIdx) const
Returns an object containing a reference to given material.
Definition: Storage.cpp:366
bool has(const QuantityId key) const
Checks if the storage contains quantity with given key.
Definition: Storage.cpp:130
@ KEEP_EMPTY_UNCHANGED
Empty buffers will not be resized to new values.
void zeroHighestDerivatives()
Sets all highest-level derivatives of quantities to zero.
Definition: Storage.cpp:556
Storage clone(const Flags< VisitorEnum > buffers) const
Clones specified buffers of the storage.
Definition: Storage.cpp:563
Array< TValue > & getValue(const QuantityId key)
Retrieves a quantity values from the storage, given its key and value type.
Definition: Storage.cpp:191
Symmetric tensor of 2nd order.
Generic 2nd-order tensor with 9 independent components.
Definition: Tensor.h:13
Symmetric traceless 2nd order tensor.
SharedPtr< T > lock() const
Definition: SharedPtr.h:373
Creating code components based on values from settings.
BodySettingsId
Settings of a single body / gas phase / ...
Definition: Settings.h:1394
Convert type to ValueType enum.