OGS
ReflectionIPData.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#pragma once
5
6#include <boost/mp11.hpp>
7
11#include "ReflectionData.h"
12
14{
15namespace detail
16{
24template <typename T>
25struct is_raw_data : std::false_type
26{
27};
28
29template <>
30struct is_raw_data<double> : std::true_type
31{
32};
33
34template <int N>
35struct is_raw_data<Eigen::Matrix<double, N, 1, Eigen::ColMajor, N, 1>>
36 : std::true_type
37{
38};
39
40template <int N, int M>
41struct is_raw_data<Eigen::Matrix<double, N, M, Eigen::RowMajor, N, M>>
42 : std::true_type
43{
44};
45
46template <typename T>
48
49template <typename T>
51
52template <>
53struct NumberOfRows<double> : std::integral_constant<unsigned, 1>
54{
55};
56
57template <int N>
58struct NumberOfRows<Eigen::Matrix<double, N, 1, Eigen::ColMajor, N, 1>>
59 : std::integral_constant<unsigned, N>
60{
61};
62
63template <int N, int M>
64struct NumberOfRows<Eigen::Matrix<double, N, M, Eigen::RowMajor, N, M>>
65 : std::integral_constant<unsigned, N>
66{
67};
68
69template <typename T>
71
72template <>
73struct NumberOfColumns<double> : std::integral_constant<unsigned, 1>
74{
75};
76
77template <int N>
78struct NumberOfColumns<Eigen::Matrix<double, N, 1, Eigen::ColMajor, N, 1>>
79 : std::integral_constant<unsigned, 1>
80{
81};
82
83template <int N, int M>
84struct NumberOfColumns<Eigen::Matrix<double, N, M, Eigen::RowMajor, N, M>>
85 : std::integral_constant<unsigned, M>
86{
87};
88
89template <typename T>
91 : std::integral_constant<unsigned,
92 NumberOfRows<T>::value * NumberOfColumns<T>::value>
93{
94};
95
96template <typename T>
97concept has_reflect = requires
98{
99 T::reflect();
100};
101
102template <typename... Ts>
103auto reflect(std::type_identity<std::tuple<Ts...>>)
104{
105 using namespace boost::mp11;
106
107 // The types Ts... must be unique. Duplicate types are incompatible with the
108 // concept of "reflected" I/O: they would lead to duplicate names for the
109 // I/O data.
110 static_assert(mp_is_set_v<mp_list<Ts...>>);
111
112 return reflectWithoutName<std::tuple<Ts...>>(
113 [](auto& tuple_) -> auto& { return std::get<Ts>(tuple_); }...);
114}
115
116template <has_reflect T>
117auto reflect(std::type_identity<T>)
118{
119 return T::reflect();
120}
121
122template <typename T>
123concept has_ioName = requires(T* t)
124{
125 ioName(t);
126};
127
128template <typename T, typename Tag>
129auto reflect(std::type_identity<BaseLib::StrongType<T, Tag>>)
130{
132
133 auto accessor = [](auto& o) -> auto&
134 {
135 return *o;
136 };
137
138 // Maybe in the future we might want to lift the following two constraints.
139 // But beware: that generalization has to be tested thoroughly such that we
140 // don't accidentally produce I/O data without name and the like.
141 static_assert(
143 /* We use ioName(Tag* tag), because it works with an incomplete type
144 * Tag, as opposed to ioName(Tag tag), i.e. declaring
145 * std::string_view ioName(struct SomeTag*);
146 * is possible, whereas
147 * std::string_view ioName(struct SomeTag);
148 * is not.
149 * This choice makes the code for every ioName() definition rather
150 * compact.
151 */
152 "For I/O of StrongType<T, Tag> you have to define an ioName(Tag* tag) "
153 "function returning the name used for I/O.");
154 static_assert(
156 "I/O of StrongTypes is supported only for StrongTypes wrapping 'raw "
157 "data' such as double values, vectors and matrices.");
158
159 return std::tuple{makeReflectionData<ST>(
160 std::string{ioName(static_cast<Tag*>(nullptr))}, std::move(accessor))};
161}
162
163template <typename T>
164concept is_reflectable = requires
165{
166 ProcessLib::Reflection::detail::reflect(std::type_identity<T>{});
167};
168
224template <int Dim, typename Accessor_IPDataVecInLocAsm,
225 typename Accessor_CurrentLevelFromIPDataVecElement>
227{
228 static_assert(!std::is_reference_v<Accessor_IPDataVecInLocAsm>);
229 static_assert(
230 !std::is_reference_v<Accessor_CurrentLevelFromIPDataVecElement>);
231
232 Accessor_IPDataVecInLocAsm accessor_ip_data_vec_in_loc_asm;
233 Accessor_CurrentLevelFromIPDataVecElement
235
236 template <typename LocAsm>
237 std::vector<double> operator()(LocAsm const& loc_asm) const
238 {
239 using IPDataVector = std::remove_cvref_t<
240 std::invoke_result_t<Accessor_IPDataVecInLocAsm, LocAsm const&>>;
241 using IPDataVectorElement = typename IPDataVector::value_type;
242
243 // the concrete IP data, e.g. double or Eigen::Vector
244 using ConcreteIPData = std::remove_cvref_t<
245 std::invoke_result_t<Accessor_CurrentLevelFromIPDataVecElement,
246 IPDataVectorElement const&>>;
248 "This method only deals with raw data. The given "
249 "ConcreteIPData is not raw data.");
250
251 constexpr unsigned num_rows = NumberOfRows<ConcreteIPData>::value;
252 constexpr unsigned num_cols = NumberOfColumns<ConcreteIPData>::value;
253 constexpr unsigned num_comp = num_rows * num_cols;
254 auto const& ip_data_vector = accessor_ip_data_vec_in_loc_asm(loc_asm);
255 auto const num_ips = ip_data_vector.size();
256
257 std::vector<double> result(num_comp * num_ips);
258
259 for (std::size_t ip = 0; ip < num_ips; ++ip)
260 {
261 auto const& ip_data_vector_element = ip_data_vector[ip];
262 auto const& ip_data =
264 ip_data_vector_element);
265
266 if constexpr (num_comp == 1)
267 {
268 // scalar
269 result[ip] = ip_data;
270 }
271 else if constexpr (num_rows == MathLib::KelvinVector::
273 num_cols == 1)
274 {
275 // Kelvin vector
276 auto const converted =
278 ip_data);
279
280 for (unsigned comp = 0; comp < num_comp; ++comp)
281 {
282 result[ip * num_comp + comp] = converted[comp];
283 }
284 }
285 else if constexpr (num_cols == MathLib::KelvinVector::
287 num_rows == 1)
288 {
289 static_assert(
290 num_rows != 1 /* always false in this branch */,
291 "We support Kelvin column-vectors, but not Kelvin "
292 "row-vectors. The latter are unusual and confusion with "
293 "generic vectors might be possible.");
294 }
295 else if constexpr (num_rows == 1 || num_cols == 1)
296 {
297 // row or column vector
298 for (unsigned comp = 0; comp < num_comp; ++comp)
299 {
300 result[ip * num_comp + comp] = ip_data[comp];
301 }
302 }
303 else
304 {
305 // matrix
306 // row-major traversal
307 for (unsigned row = 0; row < num_rows; ++row)
308 {
309 for (unsigned col = 0; col < num_cols; ++col)
310 {
311 result[ip * num_comp + row * num_cols + col] =
312 ip_data(row, col);
313 }
314 }
315 }
316 }
317 return result;
318 }
319};
320
321// Convenience function for template argument deduction with
322// GetFlattenedIPDataFromLocAsm
323template <int Dim, typename Accessor_IPDataVecInLocAsm,
324 typename Accessor_CurrentLevelFromIPDataVecElement>
325GetFlattenedIPDataFromLocAsm<
326 Dim, std::remove_cvref_t<Accessor_IPDataVecInLocAsm>,
327 std::remove_cvref_t<Accessor_CurrentLevelFromIPDataVecElement>>
329 Accessor_IPDataVecInLocAsm accessor_ip_data_vec_in_loc_asm,
330 Accessor_CurrentLevelFromIPDataVecElement
331 accessor_current_level_from_ip_data_vec_element)
332{
333 return {std::forward<Accessor_IPDataVecInLocAsm>(
334 accessor_ip_data_vec_in_loc_asm),
335 std::forward<Accessor_CurrentLevelFromIPDataVecElement>(
336 accessor_current_level_from_ip_data_vec_element)};
337}
338
339// Convenience function for template argument deduction with
340// GetFlattenedIPDataFromLocAsm. Overload of the function above with less
341// arguments.
342template <int Dim, typename Accessor_IPDataVecInLocAsm>
344 Accessor_IPDataVecInLocAsm&& accessor_ip_data_vec_in_loc_asm)
345{
347 std::forward<Accessor_IPDataVecInLocAsm>(
348 accessor_ip_data_vec_in_loc_asm),
349 std::identity{});
350}
351
362template <int Dim, typename Callback, typename ReflectionDataTuple,
363 typename Accessor_IPDataVecInLocAsm,
364 typename Accessor_CurrentLevelFromIPDataVecElement>
366 Callback const& callback, ReflectionDataTuple const& reflection_data,
367 Accessor_IPDataVecInLocAsm const& accessor_ip_data_vec_in_loc_asm,
368 Accessor_CurrentLevelFromIPDataVecElement const&
369 accessor_current_level_from_ip_data_vec_element)
370{
372 "The passed reflection data is not a std::tuple.");
373 static_assert(
374 std::is_same_v<ReflectionDataTuple,
375 boost::mp11::mp_rename<ReflectionDataTuple, std::tuple>>,
376 "The passed reflection data is not a std::tuple.");
377
378 boost::mp11::tuple_for_each(
379 reflection_data,
380 [&accessor_ip_data_vec_in_loc_asm,
381 &accessor_current_level_from_ip_data_vec_element,
382 &callback]<typename Class, typename Accessor>(
383 ReflectionData<Class, Accessor> const& refl_data)
384 {
385 using MemberRef = std::invoke_result_t<Accessor, Class const&>;
386 using Member = std::remove_cvref_t<MemberRef>;
387
388 auto accessor_member_from_ip_data_vec_element =
389 [accessor_next_level = refl_data.accessor,
390 accessor_current_level_from_ip_data_vec_element](
391 auto const& ip_data_vec_element) -> Member const&
392 {
393 return accessor_next_level(
394 accessor_current_level_from_ip_data_vec_element(
395 ip_data_vec_element));
396 };
397
398 if constexpr (is_reflectable<Member>)
399 {
401 callback, detail::reflect(std::type_identity<Member>{}),
402 accessor_ip_data_vec_in_loc_asm,
403 accessor_member_from_ip_data_vec_element);
404 }
405 else
406 {
407 static_assert(is_raw_data_v<Member>,
408 "The current member is not reflectable, so we "
409 "expect it to be raw data.");
410
411 constexpr unsigned num_comp = NumberOfComponents<Member>::value;
412
413 assert(!refl_data.name.empty());
414 callback(refl_data.name, num_comp,
416 accessor_ip_data_vec_in_loc_asm,
417 accessor_member_from_ip_data_vec_element));
418 }
419 });
420}
421
422// Overload of the function above with less arguments
423template <int Dim, typename Callback, typename ReflectionDataTuple,
424 typename Accessor_IPDataVecInLocAsm>
426 Callback const& callback,
427 ReflectionDataTuple const& reflection_data,
428 Accessor_IPDataVecInLocAsm const& accessor_ip_data_vec_in_loc_asm)
429{
431 callback, reflection_data, accessor_ip_data_vec_in_loc_asm,
432 std::identity{});
433}
434
435} // namespace detail
436
448template <int Dim, typename LocAsmIF, typename Callback, typename ReflData>
449void forEachReflectedFlattenedIPDataAccessor(ReflData const& reflection_data,
450 Callback const& callback)
451{
452 using namespace boost::mp11;
453
454 static_assert(mp_is_list_v<ReflData>,
455 "The passed reflection data is not a std::tuple.");
456 static_assert(std::is_same_v<ReflData, mp_rename<ReflData, std::tuple>>,
457 "The passed reflection data is not a std::tuple.");
458
459 tuple_for_each(
460 reflection_data,
461 [&callback]<typename Class, typename Accessor>(
462 ReflectionData<Class, Accessor> const& refl_data)
463 {
464 static_assert(std::is_same_v<Class, LocAsmIF>,
465 "The currently processed reflection data is not for "
466 "the given LocAsmIF but for a different class.");
467
468 using AccessorResultRef =
469 std::invoke_result_t<Accessor, Class const&>;
470 using AccessorResult = std::remove_cvref_t<AccessorResultRef>;
471
472 // AccessorResult must be a std::vector<SomeType, SomeAllocator>. We
473 // check that, now.
474 static_assert(
475 mp_is_list_v<AccessorResult>); // std::vector<SomeType,
476 // SomeAllocator> is a list in
477 // the Boost MP11 sense
478 static_assert(
479 std::is_same_v<AccessorResult,
480 mp_rename<AccessorResult, std::vector>>,
481 "We expect a std::vector, here.");
482 // Now, we know that AccessorResult is std::vector<Member>. To be
483 // more specific, AccessorResult is a std::vector<IPData> and Member
484 // is IPData.
485 using Member = typename AccessorResult::value_type;
486
487 auto accessor_ip_data_vec_in_loc_asm =
488 [ip_data_vector_accessor =
489 refl_data.accessor](LocAsmIF const& loc_asm) -> auto const&
490 {
491 return ip_data_vector_accessor(loc_asm);
492 };
493
494 if constexpr (detail::is_reflectable<Member>)
495 {
497 callback,
498 detail::reflect(std::type_identity<Member>{}),
499 accessor_ip_data_vec_in_loc_asm);
500 }
501 else
502 {
503 static_assert(detail::is_raw_data_v<Member>,
504 "The current member is not reflectable, so we "
505 "expect it to be raw data.");
506
507 constexpr unsigned num_comp =
509
510 assert(!refl_data.name.empty());
511 callback(refl_data.name, num_comp,
513 accessor_ip_data_vec_in_loc_asm));
514 }
515 });
516}
517} // namespace ProcessLib::Reflection
Eigen::Matrix< double, 4, 1 > kelvinVectorToSymmetricTensor(Eigen::Matrix< double, 4, 1, Eigen::ColMajor, 4, 1 > const &v)
constexpr int kelvin_vector_dimensions(int const displacement_dim)
Kelvin vector dimensions for given displacement dimension.
GetFlattenedIPDataFromLocAsm< Dim, std::remove_cvref_t< Accessor_IPDataVecInLocAsm >, std::remove_cvref_t< Accessor_CurrentLevelFromIPDataVecElement > > getFlattenedIPDataFromLocAsm(Accessor_IPDataVecInLocAsm accessor_ip_data_vec_in_loc_asm, Accessor_CurrentLevelFromIPDataVecElement accessor_current_level_from_ip_data_vec_element)
auto reflect(std::type_identity< std::tuple< Ts... > >)
void forEachReflectedFlattenedIPDataAccessor(Callback const &callback, ReflectionDataTuple const &reflection_data, Accessor_IPDataVecInLocAsm const &accessor_ip_data_vec_in_loc_asm, Accessor_CurrentLevelFromIPDataVecElement const &accessor_current_level_from_ip_data_vec_element)
auto makeReflectionData(Accessor &&accessor)
void forEachReflectedFlattenedIPDataAccessor(ReflData const &reflection_data, Callback const &callback)
auto reflectWithoutName(Accessors &&... accessors)
constexpr bool mp_is_set_v
constexpr bool mp_is_list_v
Accessor_CurrentLevelFromIPDataVecElement accessor_current_level_from_ip_data_vec_element
std::vector< double > operator()(LocAsm const &loc_asm) const