1 #pragma once
2 
3 /// \file Storage.h
4 /// \brief Container for storing particle quantities and materials.
5 /// \author Pavel Sevecek (sevecek at sirrah.troja.mff.cuni.cz)
6 /// \date 2016-2021
7 
8 #include "common/ForwardDecl.h"
9 #include "objects/Exceptions.h"
10 #include "objects/containers/Array.h"
11 #include "objects/containers/FlatMap.h"
12 #include "objects/wrappers/Flags.h"
13 #include "objects/wrappers/Function.h"
14 #include "objects/wrappers/Outcome.h"
15 #include "objects/wrappers/SharedPtr.h"
16 #include "quantities/QuantityIds.h"
17 
18 NAMESPACE_SPH_BEGIN
19 
20 class IMaterial;
21 class Quantity;
22 class Box;
23 class StorageSequence;
24 class ConstStorageSequence;
25 struct Attractor;
26 enum class OrderEnum;
27 enum class VisitorEnum;
28 
29 struct StorageElement {
30     QuantityId id;
31     Quantity& quantity;
32 };
33 
34 struct ConstStorageElement {
35     QuantityId id;
36     const Quantity& quantity;
37 };
38 
39 /// \brief Helper class for iterating over quantities stored in \ref Storage.
40 class StorageIterator {
41 private:
42     using ActIterator = Iterator<FlatMap<QuantityId, Quantity>::Element>;
43 
44     ActIterator iter;
45 
46 public:
47     StorageIterator(const ActIterator iterator, Badge<StorageSequence>);
48 
49     StorageIterator& operator++();
50 
51     StorageElement operator*();
52 
53     bool operator==(const StorageIterator& other) const;
54 
55     bool operator!=(const StorageIterator& other) const;
56 };
57 
58 /// \brief Helper class for iterating over quantities stored in \ref Storage, const version.
59 class ConstStorageIterator {
60 private:
61     using ActIterator = Iterator<const FlatMap<QuantityId, Quantity>::Element>;
62 
63     ActIterator iter;
64 
65 public:
66     ConstStorageIterator(const ActIterator iterator, Badge<ConstStorageSequence>);
67 
68     ConstStorageIterator& operator++();
69 
70     ConstStorageElement operator*();
71 
72     bool operator==(const ConstStorageIterator& other) const;
73 
74     bool operator!=(const ConstStorageIterator& other) const;
75 };
76 
77 /// \brief Helper class, provides functions \ref begin and \ref end, returning iterators to the first and last
78 /// quantity in \ref Storage, respectively.
79 class StorageSequence {
80 private:
81     FlatMap<QuantityId, Quantity>& quantities;
82 
83 public:
84     StorageSequence(FlatMap<QuantityId, Quantity>& quantities, Badge<Storage>);
85 
86     /// \brief Returns iterator pointing to the beginning of the quantity storage.
87     ///
88     /// Dereferencing the iterator yields \ref StorageElement, holding the \ref QuantityId and the reference
89     /// to the \ref Quantity.
90     StorageIterator begin();
91 
92     /// \brief Returns iterator pointing to the one-past-the-end element of the quantity storage.
93     StorageIterator end();
94 
95     /// Provides random access into the sequence.
96     StorageElement operator[](const Size i) const;
97 
98     /// \brief Returns the number of quantities.
99     Size size() const;
100 };
101 
102 /// \brief Helper class, provides functions \ref begin and \ref end, returning const iterators to the first
103 /// and last quantity in \ref Storage, respectively.
104 class ConstStorageSequence {
105 private:
106     const FlatMap<QuantityId, Quantity>& quantities;
107 
108 public:
109     ConstStorageSequence(const FlatMap<QuantityId, Quantity>& quantities, Badge<Storage>);
110 
111     /// \brief Returns iterator pointing to the beginning of the quantity storage.
112     ///
113     /// Dereferencing the iterator yields \ref ConstStorageElement, holding the \ref QuantityId and the
114     /// reference to the \ref Quantity.
115     ConstStorageIterator begin();
116 
117     /// Returns iterator pointing to the one-past-the-end element of the quantity storage.
118     ConstStorageIterator end();
119 
120     /// Provides random access into the sequence.
121     ConstStorageElement operator[](const Size i) const;
122 
123     /// Returns the number of quantities.
124     Size size() const;
125 };
126 
127 /// \brief Base class for arbitrary data stored in the storage alongside particles
128 class IStorageUserData : public Polymorphic {};
129 
130 /// \brief Exception thrown when accessing missing quantities, casting to different types, etc.
131 class InvalidStorageAccess : public Exception {
132 public:
133     explicit InvalidStorageAccess(const QuantityId id);
134 
135     explicit InvalidStorageAccess(const String& message);
136 };
137 
138 /// \brief Container storing all quantities used within the simulations.
139 ///
140 /// Storage provides a convenient way to store quantities, iterate over specified subset of quantities, modify
141 /// quantities etc. Every quantity is a \ref Quantity object and is identified by \ref QuantityId key. The
142 /// quantities are stored as key-value pairs; for every \ref QuantityId there can be at most one \ref Quantity
143 /// stored.
144 ///
145 /// Storage can contain scalar, vector, tensor and integer quantities. Every quantity can also have associated
146 /// one or two derivatives. There is no constraint on quantity order or type for given \ref QuantityId, i.e.
147 /// as far as \ref Storage object is concerned, one can create a QuantityId::ENERGY tensor quantity with
148 /// second derivatives or integer quantity QuantityId::SOUND_SPEED. Different parts of the code require
149 /// certain types for some quantities, though. Particle positions, QuantityId::POSITION, are mostly assumed
150 /// to be vector quantities of second order. Inconsistency of types will cause an assert when encountered.
151 ///
152 /// Storage can hold arbitrary number of materials, objects derived from \ref IMaterial. In theory,
153 /// every particle can have a different material (different equation of state, different rheology, ...).
154 /// The storage can also exist with no material; this is a valid state, useful for situations where no
155 /// material is necessary. A storage with material can be created using constructor
156 /// Storage(AutoPtr<IMaterial>&& material). All particles subsequently added into the storage
157 /// will have the material passed in the parameter of the constructor. Storage with multiple materials can
158 /// then be created by merging the storage with another object, using function \ref merge.
159 ///
160 /// Storage is not thread-safe. If used in multithreaded context, any calls of member functions must be
161 /// synchonized by the caller.
162 ///
163 /// The following demostrates how to access the particle data:
164 /// \code
165 /// // Get particle masses from the storage
166 /// ArrayView<Float> m = storage.getValue<Float>(QuantityId::MASS);
167 /// std::cout << "Mass of 5th particle = " << m[5] << std::endl;
168 ///
169 /// // Get particle velocities (=derivative of positions) and accelerations (=second derivative of positions)
170 /// ArrayView<Vector> v = storage.getDt<Vector>(QuantityId::POSITION);
171 /// ArrayView<Vector> dv = storage.getD2t<Vector>(QuantityId::POSITION);
172 ///
173 /// // To get values and all derivatives at once, we can use the getAll function. The function returns an
174 /// // array containing all buffers, which can be "decomposed" into individual variables using tie function
175 /// // (similarly to std::tie).
176 /// ArrayView<Vector> r;
177 /// tie(r, v, dv) = storage.getAll<Vector>(QuantityId::POSITION); // return value is "decomposed"
178 ///
179 /// // Lastly, if we want to get multiple values (not derivatives) of the same type, we can use getValues. We
180 /// // can also utilize the function tie; make sure to list the variables in the same order as the IDs.
181 /// ArrayView<Float> rho, u;
182 /// tie(rho, u, m) = storage.getValues<Float>(QuantityId::DENSITY, QuantityId::ENERGY, QuantityId::MASS);
183 /// \endcode
184 ///
185 /// When adding a new quantity into the storage, it is necessary to specify the type of the quantity and the
186 /// number of derivatives using \ref OrderEnum. Quantity can be either initialized by providing a single
187 /// default value (used for all particles), or an array of values; see functions \ref insert. To add arbitrary
188 /// quantity, use:
189 /// \code
190 /// // Fill the array of angular frequencies
191 /// Array<Vector> omega(storage.getParticleCnt());
192 /// omega.fill(Vector(0, 0, 5)); // initialize it to 5 rad/s parallel to z-axis.
193 /// // Add angular frequencies to the storage, evolved as first-order quantity
194 /// storage.insert<Vector>(QuantityId::ANGULAR_FREQUENCY, OrderEnum::FIRST, std::move(omega));
195 ///
196 /// // Add moment of inertia (with no derivatives)
197 /// const SymmetricTensor I = Rigid::sphereInertia(3, 2); // moment of a sphere with mass 3kg and radius 2m
198 /// storage.insert<SymmetricTensor>(QuantityId::MOMENT_OF_INERTIA, OrderEnum::ZERO, I);
199 /// \endcode
200 ///
201 /// In some cases, it is useful to read or modify all quantities in the storage, without the need to fetch
202 /// them manually using \ref getValue and related function. There are two different ways to iterate over
203 /// quantities stored in storage. You can use the function \ref getQuantities, which returns a range (pair of
204 /// iterators) and thus allows to visit quantities in a for-loop:
205 /// \code
206 /// for (StorageElement element : storage.getQuantities()) {
207 ///     std::cout << "Quantity " << element.id << std::endl
208 ///     std::cout << " - order " << int(element.quantity.getOrderEnum()) << std::endl;
209 ///     std::cout << " - type  " << int(element.quantity.getValueEnum()) << std::endl;
210 /// }
211 /// \endcode
212 /// This approach can be utilized to access properties of the quantities (as in the example above), clone
213 /// quantities, etc. The downside is that we still need to know the value type to actually access the quantity
214 /// values. To overcome this problem and access the quantity values in generic (type-agnostic) way, consider
215 /// using the function \ref iterate:
216 /// \code
217 /// // Iterate over all first order quantities in the storage
218 /// iterate<VisitorEnum::FIRST_ORDER>(storage, [](QuantityId id, auto& values, auto& derivatives) {
219 ///     // Values and derivatives are arrays with quantity values. The type of the values can be Float,
220 ///     // Vector, SymmetricTensor, etc., depending on the actual type of the stored quantity. Therefore, we
221 ///     // can only use generic code here (functions overload for all quantity types).
222 ///     std::cout << "Quantity " << id << std::endl;
223 ///     std::cout << "Particle 0: " << values[0] << ", derivative ", derivatives[0] << std::endl;
224 /// });
225 ///
226 /// // Iterates over all arrays in the storage, meaning all quantity values and derivatives.
227 /// iterate<VisitorEnum::ALL_BUFFERS>(storage, [](auto& array) {
228 ///     // Use decltype to determine the type of the array
229 ///     using Type = std::decay_t<decltype(array)>::Type;
230 ///
231 ///     // Set all values and all derivatives to zero (zero vector, zero tensor)
232 ///     array.fill(Type(0._f));
233 /// });
234 /// \endcode
235 /// Note that arguments of the provided functor differ for each \ref VisitorEnum.
236 class Storage : public Noncopyable {
237 private:
238     /// \brief Stored quantities (array of arrays). All arrays must be the same size at all times.
239     FlatMap<QuantityId, Quantity> quantities;
240 
241     /// \brief Holds information about a material and particles with this material.
242     struct MatRange {
243         /// Actual implementation of the material
244         SharedPtr<IMaterial> material;
245 
246         /// First index of particle with this material
247         Size from = 0;
248 
249         /// One-past-last index of particle with this material
250         Size to = 0;
251 
252         MatRange() = default;
253 
254         MatRange(const SharedPtr<IMaterial>& material, const Size from, const Size to);
255     };
256 
257     /// \brief Materials of particles in the storage.
258     ///
259     /// Particles of the same material are stored consecutively; first material always starts with index 0 and
260     /// last material ends with index equal to the number of particles.
261     Array<MatRange> mats;
262 
263     /// \brief Cached view of material IDs of particles.
264     ///
265     /// Used for fast access of material properties.
266     ArrayView<Size> matIds;
267 
268     /// \brief Additional point masses that only interact with other particles gravitationally.
269     Array<Attractor> attractors;
270 
271     /// \brief Dependent storages, modified when the number of particles of this storage is changed.
272     ///
273     /// Needed in order to keep the number of particles in dependent storages the same.
274     Array<WeakPtr<Storage>> dependent;
275 
276     /// \brief Arbitrary data set by \ref setUserData.
277     ///
278     /// May be nullptr.
279     SharedPtr<IStorageUserData> userData;
280 
281 public:
282     /// \brief Creates a storage with no material.
283     ///
284     /// Any call of \ref getMaterial function will result in an assert.
285     Storage();
286 
287     /// \brief Initialize a storage with a material.
288     ///
289     /// All particles of the storage will have the same material. To create a heterogeneous storage, it is
290     /// necessary to merge another storage object into this one, using \ref merge function.
291     explicit Storage(const SharedPtr<IMaterial>& material);
292 
293     ~Storage();
294 
295     Storage(Storage&& other);
296 
297     Storage& operator=(Storage&& other);
298 
299     /// \brief Checks if the storage contains quantity with given key.
300     ///
301     /// Type or order of unit is not specified, any quantity with this key will match.
302     bool has(const QuantityId key) const;
303 
304     /// \brief Checks if the storage contains quantity with given key, value type and order.
305     template <typename TValue>
306     bool has(const QuantityId key, const OrderEnum order) const;
307 
308     /// \brief Retrieves quantity with given key from the storage.
309     ///
310     /// Quantity must be already stored, function throws an exception otherwise.
311     Quantity& getQuantity(const QuantityId key);
312 
313     /// \brief Retrieves quantity with given key from the storage, const version.
314     const Quantity& getQuantity(const QuantityId key) const;
315 
316     /// \brief Retrieves quantity buffers from the storage, given its key and value type.
317     ///
318     /// The stored quantity must be of type TValue, checked by assert. Quantity must already exist in the
319     /// storage, checked by assert. To check whether the quantity is stored, use has() method.
320     /// \return Array of references to Arrays, containing quantity values and all derivatives.
321     template <typename TValue>
322     StaticArray<Array<TValue>&, 3> getAll(const QuantityId key);
323 
324     /// \brief Retrieves quantity buffers from the storage, given its key and value type, const version.
325     template <typename TValue>
326     StaticArray<const Array<TValue>&, 3> getAll(const QuantityId key) const;
327 
328     /// \brief Retrieves a quantity values from the storage, given its key and value type.
329     ///
330     /// The stored quantity must be of type TValue, checked by assert. Quantity must already exist in the
331     /// storage, checked by assert.
332     /// \return Array reference containing stored quantity values.
333     template <typename TValue>
334     Array<TValue>& getValue(const QuantityId key);
335 
336     /// \copydoc getValue
337     template <typename TValue>
338     const Array<TValue>& getValue(const QuantityId key) const;
339 
340     /// \brief Retrieves a quantity derivative from the storage, given its key and value type.
341     ///
342     /// The stored quantity must be of type TValue, checked by assert. Quantity must already exist in the
343     /// storage and must be first or second order, checked by assert.
344     /// \return Array reference containing quantity derivatives.
345     template <typename TValue>
346     Array<TValue>& getDt(const QuantityId key);
347 
348     /// \brief Retrieves a quantity derivative from the storage, given its key and value type, const version.
349     template <typename TValue>
350     const Array<TValue>& getDt(const QuantityId key) const;
351 
352     /// \brief Retrieves a quantity second derivative from the storage, given its key and value type.
353     ///
354     /// The stored quantity must be of type TValue, checked by assert. Quantity must already exist in the
355     /// storage and must be second order, checked by assert.
356     /// \return Array reference containing quantity second derivatives.
357     template <typename TValue>
358     Array<TValue>& getD2t(const QuantityId key);
359 
360     /// \copydoc getD2t
361     template <typename TValue>
362     const Array<TValue>& getD2t(const QuantityId key) const;
363 
364     /// \brief Retrieves an array of quantities from the key.
365     ///
366     /// The type of all quantities must be the same and equal to TValue, checked by assert.
367     template <typename TValue, typename... TArgs>
getValues(const QuantityId first,const QuantityId second,const TArgs...others)368     auto getValues(const QuantityId first, const QuantityId second, const TArgs... others) {
369         return tie(getValue<TValue>(first), getValue<TValue>(second), getValue<TValue>(others)...);
370     }
371 
372     /// \brief Retrieves an array of quantities from the key, const version.
373     template <typename TValue, typename... TArgs>
getValues(const QuantityId first,const QuantityId second,const TArgs...others)374     auto getValues(const QuantityId first, const QuantityId second, const TArgs... others) const {
375         // better not const_cast here as we are deducing return type
376         return tie(getValue<TValue>(first), getValue<TValue>(second), getValue<TValue>(others)...);
377     }
378 
379     /// \brief Creates a quantity in the storage, given its key, value type and order.
380     ///
381     /// Quantity is resized and filled with default value. This cannot be used to set number of particles, the
382     /// size of the quantity is set to match current particle number. If the quantity is already stored in the
383     /// storage, function only checks that the type of the quantity matches, but otherwise keeps the
384     /// previously stored values. If the required order of quantity is larger than the one currently stored,
385     /// additional derivatives are created with no assert nor exception, otherwise the order is unchanged.
386     /// \tparam TValue Type of the quantity. Can be scalar, vector, tensor or traceless tensor.
387     /// \param key Unique key of the quantity.
388     /// \param TOrder Order (number of derivatives) associated with the quantity.
389     /// \param defaultValue Value to which quantity is initialized. If the quantity already exists in the
390     ///                     storage, the value is unused.
391     /// \returns Reference to the inserted quantity.
392     template <typename TValue>
393     Quantity& insert(const QuantityId key, const OrderEnum order, const TValue& defaultValue);
394 
395     /// \brief Creates a quantity in the storage, given array of values.
396     ///
397     /// The size of the array must match the number of particles. Derivatives of the quantity are set to zero.
398     /// If this is the first quantity inserted into the storage, it sets the number of particles; all
399     /// quantities added after that must have the same size. If a quantity with the same key already exists in
400     /// the storage, its values are overriden. Derivatives are not changed. In this case, the function checks
401     /// that the quantity type is the same; if it isn't, InvalidSetup exception is thrown.
402     /// \returns Reference to the inserted quantity.
403     template <typename TValue>
404     Quantity& insert(const QuantityId key, const OrderEnum order, Array<TValue>&& values);
405 
406     /// \brief Adds a point-mass attractor to the storage.
407     void addAttractor(const Attractor& a);
408 
409     /// \brief Registers a dependent storage.
410     ///
411     /// A dependent storage mirrors changes of particle counts. Every time new particles are added into the
412     /// storage or when some particles are removed, the same action is performed on all (existing) dependent
413     /// storages. However, no other action is handled this way, namely new quantities have to be added
414     /// manually to all storages. Same goes for clearing the derivatives, changing materials, etc.
415     ///
416     /// Note that the storage holds weak references, the dependent storages must be held in SharedPtr
417     /// somewhere to keep the link.
418     void addDependent(const WeakPtr<Storage>& other);
419 
420     /// \brief Returns an object containing a reference to given material.
421     ///
422     /// The object can also be used to iterate over indices of particles belonging to given material.
423     /// \param matIdx Index of given material in storage. Materials are stored in unspecified order; to get
424     ///               material of given particle, use \ref getMaterialOfParticle.
425     MaterialView getMaterial(const Size matIdx) const;
426 
427     /// \brief Returns material view for material of given particle.
428     MaterialView getMaterialOfParticle(const Size particleIdx) const;
429 
430     /// \brief Modifies material with given index.
431     ///
432     /// The new material cannot be nullptr.
433     void setMaterial(const Size matIdx, const SharedPtr<IMaterial>& material);
434 
435     /// \brief Returns the bounding range of given quantity.
436     ///
437     /// Provides an easy access to the material range without construcing intermediate object of \ref
438     /// MaterialView, otherwise this function is equivalent to: \code getMaterial(matIdx)->range(id) \endcode
439     Interval getRange(const QuantityId id, const Size matIdx) const;
440 
441     /// \brief Returns the given material parameter for all materials in the storage.
442     ///
443     /// To get the material parameter for given particle, use the index given by material ID.
444     template <typename TValue>
445     Array<TValue> getMaterialParams(const BodySettingsId param) const;
446 
447     /// \brief Checks if the particles in the storage are homogeneous with respect to given parameter.
448     ///
449     /// It is assumed that the parameter is scalar.
450     bool isHomogeneous(const BodySettingsId param) const;
451 
452     /// \brief Returns the sequence of quantities.
453     StorageSequence getQuantities();
454 
455     /// \brief Returns the sequence of quantities, const version.
456     ConstStorageSequence getQuantities() const;
457 
458     /// \brief Returns the sequence of stored point-mass attractors.
459     ArrayView<Attractor> getAttractors();
460 
461     /// \brief Returns the sequence of stored point-mass attractors, const version.
462     ArrayView<const Attractor> getAttractors() const;
463 
464     /// \brief Executes a given functor recursively for all dependent storages.
465     ///
466     /// This storage is not visited, the functor is executed with child storages, grandchild storages, etc. If
467     /// one of dependent storages expired (no shared pointer is currently holding it), it is removed from the
468     /// list.
469     void propagate(const Function<void(Storage& storage)>& functor);
470 
471     /// \brief Return the number of materials in the storage.
472     ///
473     /// Material indices from 0 to (getMaterialCnt() - 1) are valid input for \ref getMaterialView function.
474     Size getMaterialCnt() const;
475 
476     /// \brief Returns the number of stored quantities.
477     Size getQuantityCnt() const;
478 
479     /// \brief Returns the number of particles.
480     ///
481     /// The number of particle is always the same for all quantities.
482     /// \warning This count does not include the number of attractors.
483     Size getParticleCnt() const;
484 
485     /// \brief Returns the number of attractors.
486     Size getAttractorCnt() const;
487 
488     /// \brief Checks if the storage is empty, i.e. without particles.
489     bool empty() const;
490 
491     /// \brief Merges another storage into this object.
492     ///
493     /// The passed storage is moved in the process. All materials in the merged storage are conserved;
494     /// particles will keep the materials they had before the merge. The merge is only allowed for storages
495     /// that both have materials or neither have one. Merging a storage without a material into a storage with
496     /// at least one material will result in assert. Similarly for merging a storage with materials into a
497     /// storage without materials.
498     ///
499     /// The function invalidates any reference or \ref ArrayView to quantity values or derivatives. For this
500     /// reason, storages can only be merged when setting up initial conditions or inbetween timesteps, never
501     /// while evaluating solver!
502     void merge(Storage&& other);
503 
504     /// \brief Sets all highest-level derivatives of quantities to zero.
505     ///
506     /// Other values are unchanged.
507     void zeroHighestDerivatives(IScheduler& scheduler);
508 
509     enum class IndicesFlag {
510         /// Use if the given array is already sorted (optimization)
511         INDICES_SORTED = 1 << 0,
512     };
513 
514     /// \brief Duplicates some particles in the storage.
515     ///
516     /// New particles are added to an unspecified positions in the storage, copying all the quantities and
517     /// materials from the source particles. Note that this is not intended to be used without further
518     /// modifications of the newly created particles as we never want to create particle pairs; make sure to
519     /// move the created particles to required positions and modify their quantities as needed. The function
520     /// can be used to add new particles with materials already existing in the storage.
521     /// \param idxs Indices of the particles to duplicate.
522     /// \return Indices of the newly created particles (in the modified storage). Note that the original
523     ///         indices passed into the storage are no longer valid after the function is called.
524     Array<Size> duplicate(ArrayView<const Size> idxs, const Flags<IndicesFlag> flags = EMPTY_FLAGS);
525 
526     /// \brief Removes specified particles from the storage.
527     ///
528     /// If all particles of some material are removed by this, the material is also removed from the storage.
529     /// Same particles are also removed from all dependent storages.
530     /// \param idxs Indices of particles to remove. No need to sort the indices.
531     void remove(ArrayView<const Size> idxs, const Flags<IndicesFlag> flags = EMPTY_FLAGS);
532 
533     /// \brief Removes all particles with all quantities (including materials) from the storage.
534     ///
535     /// The storage is left is a state as if it was default-constructed. Dependent storages are also cleared.
536     void removeAll();
537 
538     /// \brief Clones specified buffers of the storage.
539     ///
540     /// Cloned (sub)set of buffers is given by flags. Cloned storage will have the same number of quantities
541     /// and the order and types of quantities will match; if some buffer is excluded from cloning, it is
542     /// simply left empty.
543     /// Note that materials are NOT cloned, but rather shared with the parent storage. Modifying material
544     /// parameters in cloned storage will also modify the parameters in the parent storage.
545     Storage clone(const Flags<VisitorEnum> buffers) const;
546 
547     /// Options for the storage resize
548     enum class ResizeFlag {
549         /// Empty buffers will not be resized to new values.
550         KEEP_EMPTY_UNCHANGED = 1 << 0,
551     };
552 
553     /// \brief Changes number of particles for all quantities stored in the storage.
554     ///
555     /// If the new number of particles is larger than the current one, the quantities of the newly created
556     /// particles are set to zero, regardless of the actual initial value. This is true for all quantity
557     /// values and derivatives. Storage must already contain at least one quantity, checked by assert.
558     /// All dependent storages are resized as well. Can be only used on storages with no material or storages
559     /// with only a single material, checked by assert.
560     /// \param newParticleCnt New number of particles.
561     /// \param flags Options of the resizing, see ResizeFlag enum. By default, all quantities are resized.
562     void resize(const Size newParticleCnt, const Flags<ResizeFlag> flags = EMPTY_FLAGS);
563 
564     /// \brief Swap quantities or given subset of quantities between two storages.
565     ///
566     /// Note that materials of the storages are NOT changed.
567     void swap(Storage& other, const Flags<VisitorEnum> flags);
568 
569     enum class ValidFlag {
570         /// Checks that the storage is complete, i.e. there are no empty buffers.
571         COMPLETE = 1 << 0,
572     };
573 
574     /// \brief Checks whether the storage is in valid state.
575     ///
576     /// The valid state means that all quantities have the same number of particles and materials are stored
577     /// consecutively in the storage. This should be handled automatically, the function is mainly for
578     /// debugging purposes.
579     Outcome isValid(const Flags<ValidFlag> flags = ValidFlag::COMPLETE) const;
580 
581     /// \brief Stores new user data into the storage.
582     ///
583     /// Previous user data are overriden.
584     void setUserData(SharedPtr<IStorageUserData> newData);
585 
586     /// \brief Returns the stored user data.
587     ///
588     /// If no data are stored, the function returns nullptr.
589     SharedPtr<IStorageUserData> getUserData() const;
590 
591 private:
592     /// \brief Inserts all quantities contained in source storage that are not present in this storage.
593     ///
594     /// All added quantities are initialized to zero.
595     void addMissingBuffers(const Storage& source);
596 
597     /// \brief Updates the cached matIds view.
598     void update();
599 };
600 
601 /// \brief Adds or updates a quantity holding particle indices to the storage.
602 ///
603 /// The indices are accessible through quantity QuantityId::PERSISTENT_INDEX. Initially, particle are numbered
604 /// from 0 to #particleCnt - 1, but the indices are persistent, meaning they remain unchanged when removing
605 /// particles from the storage. When a particle is removed from the storage, its index can be then re-used by
606 /// another particle added into the storage.
607 ///
608 /// When merging two storages, indices remain unchanged in the storage where the particles are into, but they
609 /// are reset in the storage being merged, as they could be collisions of indices. Same goes to particles
610 /// added via \ref duplicate function.
611 void setPersistentIndices(Storage& storage);
612 
613 NAMESPACE_SPH_END
614