1 #pragma once 2 3 /// \file Quantity.h 4 /// \brief Holder of quantity values and their temporal derivatives 5 /// \author Pavel Sevecek (sevecek at sirrah.troja.mff.cuni.cz) 6 /// \date 2016-2021 7 8 #include "objects/containers/Array.h" 9 #include "objects/wrappers/Flags.h" 10 #include "objects/wrappers/Variant.h" 11 #include "quantities/QuantityHelpers.h" 12 13 NAMESPACE_SPH_BEGIN 14 15 enum class OrderEnum { 16 ZERO, ///< Quantity without derivatives, or "zero order" of quantity 17 FIRST, ///< Quantity with 1st derivative 18 SECOND, ///< Quantity with 1st and 2nd derivative 19 }; 20 21 22 /// Types of iteration over storage 23 enum class VisitorEnum { 24 /// Iterates only over const quantities or quantities with no derivatives. Passes the values as argument 25 /// of functor. 26 /// \note To iterate over all quantities and pass their values into the functor, use ALL_VALUES 27 ZERO_ORDER = 1 << 0, 28 29 /// Iterates only over first-order quantities. Passes values and derivatives as arguments of functor. 30 FIRST_ORDER = 1 << 1, 31 32 /// Iterates only over second-order quantities. Passes values, 1st derivatives and 2nd derivatives as 33 /// arguments of functor. 34 SECOND_ORDER = 1 << 2, 35 36 /// Iterates over all stored arrays of all quantities. Executes functor for each value array and each 37 /// derivative array. 38 ALL_BUFFERS = 1 << 3, 39 40 /// Iterates over all quantities, but executes the functor for values only (derivatives are not passed for 41 /// higher-order quantities). 42 ALL_VALUES = 1 << 4, 43 44 /// Iterate over quantity values for 1st order quantities and over values and 1st derivatives of 2nd order 45 /// quantities. Zero order quantities are skipped. 46 STATE_VALUES = 1 << 5, 47 48 /// Iterates over all 1st order and 2nd order quantities, passes their 1st and 2nd derivatives as 49 /// parameters, respectively. 50 HIGHEST_DERIVATIVES = 1 << 6, 51 }; 52 53 54 namespace Detail { 55 /// Abstract holder of all data associated with a quantity. Provides interface to extract information 56 /// about the quantity. Must be static/dynamic_casted to one of derived types to extract information 57 /// abount stored types or number of stored arrays. 58 template <typename TValue> 59 class Holder { 60 private: 61 Array<TValue> v; /// stored values 62 Array<TValue> dv_dt; /// 1st derivative 63 Array<TValue> d2v_dt2; /// 2nd derivative 64 65 OrderEnum order; 66 Holder(const OrderEnum order)67 Holder(const OrderEnum order) 68 : order(order) {} 69 70 public: Holder(const OrderEnum order,const TValue & defaultValue,const Size size)71 Holder(const OrderEnum order, const TValue& defaultValue, const Size size) 72 : order(order) { 73 v.resize(size); 74 v.fill(defaultValue); 75 initDerivatives(size); 76 } 77 Holder(const OrderEnum order,Array<TValue> && values)78 Holder(const OrderEnum order, Array<TValue>&& values) 79 : v(std::move(values)) 80 , order(order) { 81 initDerivatives(v.size()); 82 } 83 84 /// Returns number of derivatives stored within the quantity getOrderEnum()85 INLINE OrderEnum getOrderEnum() const { 86 return order; 87 } 88 89 /// Return type of quantity values getValueEnum()90 INLINE ValueEnum getValueEnum() const { 91 return GetValueEnum<TValue>::type; 92 } 93 94 /// Returns size of the stored arrays (=number of particles). size()95 INLINE Size size() const { 96 // the quantity can be incomplete (can hold only derivatives), just return the maximum size 97 return max(v.size(), dv_dt.size(), d2v_dt2.size()); 98 } 99 getValue()100 INLINE Array<TValue>& getValue() { 101 return v; 102 } 103 getValue()104 INLINE const Array<TValue>& getValue() const { 105 return v; 106 } 107 getDt()108 INLINE Array<TValue>& getDt() { 109 SPH_ASSERT(order == OrderEnum::FIRST || order == OrderEnum::SECOND); 110 return dv_dt; 111 } 112 getDt()113 INLINE const Array<TValue>& getDt() const { 114 SPH_ASSERT(order == OrderEnum::FIRST || order == OrderEnum::SECOND); 115 return dv_dt; 116 } 117 getD2t()118 INLINE Array<TValue>& getD2t() { 119 SPH_ASSERT(order == OrderEnum::SECOND); 120 return d2v_dt2; 121 } 122 getD2t()123 INLINE const Array<TValue>& getD2t() const { 124 SPH_ASSERT(order == OrderEnum::SECOND); 125 return d2v_dt2; 126 } 127 getAll()128 INLINE StaticArray<Array<TValue>&, 3> getAll() { 129 switch (order) { 130 case OrderEnum::ZERO: 131 return { v }; 132 case OrderEnum::FIRST: 133 return { v, dv_dt }; 134 case OrderEnum::SECOND: 135 return { v, dv_dt, d2v_dt2 }; 136 default: 137 NOT_IMPLEMENTED; 138 } 139 } 140 getAll()141 INLINE StaticArray<const Array<TValue>&, 3> getAll() const { 142 switch (order) { 143 case OrderEnum::ZERO: 144 return { v }; 145 case OrderEnum::FIRST: 146 return { v, dv_dt }; 147 case OrderEnum::SECOND: 148 return { v, dv_dt, d2v_dt2 }; 149 default: 150 NOT_IMPLEMENTED; 151 } 152 } 153 154 Holder clone(const Flags<VisitorEnum> flags) const; 155 156 157 Holder createZeros(const Size particleCnt) const; 158 159 void swap(Holder& other, Flags<VisitorEnum> flags); 160 161 void setOrder(const OrderEnum newOrder); 162 163 private: 164 void initDerivatives(const Size size); 165 166 template <typename TFunctor> 167 void visitMutable(Holder& other, const Flags<VisitorEnum> flags, TFunctor&& functor); 168 169 template <typename TFunctor> 170 void visitConst(Holder& other, const Flags<VisitorEnum> flags, TFunctor&& functor) const; 171 }; 172 } // namespace Detail 173 174 /// \brief Generic container for storing scalar, vector or tensor quantity and its derivatives. 175 /// 176 /// Contains current values of the quantity and all derivatives. Any quantity can have first and second 177 /// derivatives stored together with quantity values. There is currently no limitation of quatity types and 178 /// their order, i.e. it is possible to have index quantities with derivatives. 179 /// 180 /// As the quantity can have data of different types, there is no direct way to access the arrays stored 181 /// within (like operator [] for \ref Array class, for example). To access the stored values, use on of the 182 /// following: 183 /// 1. Templated member function getValue, getDt, getD2t 184 /// These functions return the reference to stored arrays, provided the template type matches the type of 185 /// the stored quantity. This is checked by assert. Type of the quantity can be checked by \ref 186 /// getValueEnum 187 /// 2. Function getAll; returns all arrays (values and derivatives) stored in the holder if the template type 188 /// matches the holder type. The value type is checked by assert. 189 /// 3. If the quantity is stored in \ref Storage object (which is expected, there is no reason to keep 190 /// Quantity outside of \ref Storage), it is possible to enumerate quantity values using \ref iterate 191 /// function (and related function - iteratePair, iterateWithPositions, ...). Utilizing generic lambdas, we 192 /// can access the values of buffers in a generic way, provided the called operators and functions are 193 /// defiend for all types. 194 /// 195 /// Quantity cannot be easily resized, in order to enforce validity of parent \ref Storage; the number of 196 /// quantity values should be the same for all quantities and equal to the number of particles in the storage. 197 /// To add or remove particles, use \ref Storage::resize function, rather than manually resizing all 198 /// quantities. Even though this is possible to do (using mentioned \ref iterate function), it is not 199 /// recommended, as \ref Storage keeps the number of particles as a state and it would invalidate the Storage. 200 class Quantity : public Noncopyable { 201 private: 202 template <typename... TArgs> 203 using HolderVariant = Variant<NothingType, Detail::Holder<TArgs>...>; 204 205 // Types must be in same order as in ValueEnum! 206 using Holder = HolderVariant<Float, Vector, Tensor, SymmetricTensor, TracelessTensor, Size>; 207 Holder data; 208 Quantity(Holder && holder)209 Quantity(Holder&& holder) 210 : data(std::move(holder)) {} 211 212 public: 213 /// \brief Constructs an empty quantity. 214 /// 215 /// Calling any member functions will cause assert, quantity can be then created using move constructor. 216 /// The default constructor allows using Quantity in STL containers. 217 Quantity() = default; 218 219 /// \brief Creates a quantity given number of particles and default value of the quantity. 220 /// 221 /// All values are set to the default value. If the type is 1st-order or 2nd-order, derivatives arrays 222 /// resized to the same size as the array of values and set to zero. 223 /// \param defaultValue Value assigned to all particles. 224 /// \param size Size of the array, equal to the number of particles. 225 template <typename TValue> Quantity(const OrderEnum order,const TValue & defaultValue,const Size size)226 Quantity(const OrderEnum order, const TValue& defaultValue, const Size size) 227 : data(Detail::Holder<TValue>(order, defaultValue, size)) {} 228 229 /// \brief Creates a quantity from an array of values. 230 /// 231 /// All derivatives are set to zero. 232 template <typename TValue> Quantity(const OrderEnum order,Array<TValue> && values)233 Quantity(const OrderEnum order, Array<TValue>&& values) 234 : data(Detail::Holder<TValue>(order, std::move(values))) {} 235 236 /// \brief Returns the order of the quantity. 237 /// 238 /// Zero order quantities contain only quantity values, first-order quantities contain values and first 239 /// derivatives, and so on. The order is used by timestepping algorithm to advance the quantity values in 240 /// time. getOrderEnum()241 OrderEnum getOrderEnum() const { 242 return forValue(data, [](auto& holder) INL { return holder.getOrderEnum(); }); 243 } 244 245 /// \brief Returns the value order of the quantity. getValueEnum()246 ValueEnum getValueEnum() const { 247 SPH_ASSERT(data.getTypeIdx() != 0); 248 return ValueEnum(data.getTypeIdx() - 1); 249 } 250 251 /// \brief Clones all buffers contained by the quantity, or optionally only selected ones. clone(const Flags<VisitorEnum> flags)252 Quantity clone(const Flags<VisitorEnum> flags) const { 253 return forValue(data, [flags](auto& holder) -> Holder { return holder.clone(flags); }); 254 } 255 256 /// \brief Creates a new quantity with the same type and order as this one. 257 /// 258 /// It creates specified number of particles and initializes them to zeros. createZeros(const Size particleCnt)259 Quantity createZeros(const Size particleCnt) const { 260 return forValue(data, [particleCnt](auto& holder) -> Holder { // 261 return holder.createZeros(particleCnt); 262 }); 263 } 264 265 /// \brief Swap quantity (or selected part of it) with other quantity. 266 /// 267 /// Swapping only part of quantity (only derivatives for example) can be useful for some timestepping 268 /// algorithms, such as predictor-corrector. swap(Quantity & other,const Flags<VisitorEnum> flags)269 void swap(Quantity& other, const Flags<VisitorEnum> flags) { 270 SPH_ASSERT(this->getValueEnum() == other.getValueEnum()); 271 forValue(data, [flags, &other](auto& holder) { 272 using Type = std::decay_t<decltype(holder)>; 273 return holder.swap(other.data.get<Type>(), flags); 274 }); 275 } 276 277 /// \brief Returns the size of the quantity (number of particles) size()278 INLINE Size size() const { 279 return forValue(data, [](auto& holder) INL { return holder.size(); }); 280 } 281 282 /// \brief Returns a reference to array of quantity values. 283 /// 284 /// The type of the quantity must match the provided type, checked by assert. To check whether the type of 285 /// the quantity match, use getValueEnum(). 286 template <typename TValue> getValue()287 INLINE Array<TValue>& getValue() { 288 return get<TValue>().getValue(); 289 } 290 291 /// \brief Returns a reference to array of quantity values, const version. 292 template <typename TValue> getValue()293 INLINE const Array<TValue>& getValue() const { 294 return get<TValue>().getValue(); 295 } 296 setOrder(const OrderEnum order)297 void setOrder(const OrderEnum order) { 298 return forValue(data, [order](auto& holder) INL { return holder.setOrder(order); }); 299 } 300 301 /// \brief Returns a reference to array of first derivatives of quantity. 302 /// 303 /// The type of the quantity must match the provided type and the quantity must be (at least) 1st order, 304 /// checked by assert. To check whether the type and order match, use getValueEnum() and getOrderEnum(), 305 /// respectively. 306 template <typename TValue> getDt()307 INLINE Array<TValue>& getDt() { 308 return get<TValue>().getDt(); 309 } 310 311 /// Returns a reference to array of first derivatives of quantity, const version. 312 template <typename TValue> getDt()313 INLINE const Array<TValue>& getDt() const { 314 return get<TValue>().getDt(); 315 } 316 317 /// \brief Returns a reference to array of second derivatives of quantity. 318 /// 319 /// The type of the quantity must match the provided type and the quantity must be 2st order, checked by 320 /// assert. To check whether the type and order match, use getValueEnum() and getOrderEnum(), 321 /// respectively. 322 template <typename TValue> getD2t()323 INLINE Array<TValue>& getD2t() { 324 return get<TValue>().getD2t(); 325 } 326 327 /// Returns a reference to array of second derivatives of quantity, const version. 328 template <typename TValue> getD2t()329 INLINE const Array<TValue>& getD2t() const { 330 return get<TValue>().getD2t(); 331 } 332 333 /// \brief Returns all buffers of given type stored in this quantity. 334 /// 335 /// If the quantity is of different type, an empty array is returned. Buffers in array are ordered such 336 /// that quantity values is the first element (zero index), first derivative is the second element etc. 337 template <typename TValue> getAll()338 StaticArray<Array<TValue>&, 3> getAll() { 339 return get<TValue>().getAll(); 340 } 341 342 template <typename TValue> getAll()343 StaticArray<const Array<TValue>&, 3> getAll() const { 344 return get<TValue>().getAll(); 345 } 346 347 /// Iterates through the quantity values using given integer sequence and clamps the visited values to 348 /// given range. 349 /// \todo clamp derivatives as well 350 template <typename TIndexSequence> clamp(const TIndexSequence & sequence,const Interval range)351 void clamp(const TIndexSequence& sequence, const Interval range) { 352 forValue(data, [&sequence, range](auto& v) { 353 auto& values = v.getValue(); 354 for (Size idx : sequence) { 355 values[idx] = Sph::clamp(values[idx], range); 356 } 357 }); 358 } 359 360 private: 361 template <typename TValue> get()362 Detail::Holder<TValue>& get() { 363 return data.get<Detail::Holder<TValue>>(); 364 } 365 366 template <typename TValue> get()367 const Detail::Holder<TValue>& get() const { 368 return data.get<Detail::Holder<TValue>>(); 369 } 370 }; 371 372 NAMESPACE_SPH_END 373