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