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