/
GetMetadata.hpp
238 lines (208 loc) · 8.67 KB
/
GetMetadata.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/* Copyright 2024 Julian Lenz
*
* This file is part of PIConGPU.
*
* PIConGPU is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PIConGPU is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PIConGPU.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <pmacc/math/Vector.hpp>
#include <pmacc/meta/ForEach.hpp>
#include <pmacc/meta/conversion/MakeSeq.hpp>
#include <boost/mp11/bind.hpp>
#include <numeric>
#include <type_traits>
#include <nlohmann/json.hpp>
namespace picongpu
{
namespace traits
{
template<typename, typename = void>
inline constexpr bool providesMetadata = false;
template<typename T>
inline constexpr bool providesMetadata<T, std::void_t<decltype(std::declval<T>().metadata())>> = true;
template<typename, typename = void>
inline constexpr bool providesMetadataAtCT = false;
template<typename T>
inline constexpr bool providesMetadataAtCT<T, std::void_t<decltype(T::metadata())>> = true;
template<typename, typename = void>
inline constexpr bool providesMetadataAtRT = false;
template<typename T>
inline constexpr bool
providesMetadataAtRT<T, std::enable_if_t<providesMetadata<T> && !providesMetadataAtCT<T>>> = true;
namespace detail
{
/**
* An empty return type to distinguish the fallback implementation of GetMetadata
*
* It has a template argument, so that the static_assert in to_json can print the type of TObject.
*/
template<typename TObject>
struct ReturnTypeFromDefault
{
};
/**
* A template that's always false but technically depends on its template parameter, so that its
* instantiated only in the second stage (and as such does not trigger a static_assert if that function is
* not used).
*/
template<typename>
inline constexpr bool False = false;
/**
* Always-(CT-)failing conversion to json, so we get a nice error message at CT.
*/
template<typename T>
void to_json(nlohmann::json&, ReturnTypeFromDefault<T> const&)
{
static_assert(
False<T>,
"You're missing metadata for a type supposed to provide some. There are three alternatives for "
"you: Specialise GetMetadata<YourType>, add a .metadata() method to your type, or use "
"AllowMissingMetadata<YourType> during the registration. For more infos, see "
"docs/source/usage/metadata.rst.");
}
} // namespace detail
/**
* Main customisation point for the content of the metadata reported
*
* As a user, provide a template specialisation for the type in question in order to customise the information
* reported by it. The defaults reach to the (static -- for CT) member function `metadata` to provide such
* information. If none is provided by the class, this attempt fails at compiletime.
*
* @tparam TObject type of the object we want to provide information about
* @tparam SFINAE parameter used to provide defaults for RT and CT, DO NOT TOUCH
*/
template<typename TObject, typename = void>
struct GetMetadata
{
detail::ReturnTypeFromDefault<TObject> description() const
{
return {};
}
};
// doc-include-start: GetMetdata trait
template<typename TObject>
struct GetMetadata<TObject, std::enable_if_t<providesMetadataAtRT<TObject>>>
{
// Holds a constant reference to the RT instance it's supposed to report about.
// Omit this for the CT specialisation!
TObject const& obj;
nlohmann::json description() const
{
return obj.metadata();
}
};
template<typename TObject>
struct GetMetadata<TObject, std::enable_if_t<providesMetadataAtCT<TObject>>>
{
// CT version has no members. Apart from that, the interface is identical to the RT version.
nlohmann::json description() const
{
return TObject::metadata();
}
};
// doc-include-end: GetMetdata trait
/**
* Policy to wrap another type to allow missing metadata for it.
*
* It doesn't do much by itself. Functionality is provided by the corresponding
* GetMetadata<AllowMissingMetadata<...>> specialisation.
*
* @tparam TObject type to apply this policy to
*/
// doc-include-start: AllowMissingMetadata
template<typename TObject>
struct AllowMissingMetadata
{
// it's probably a nice touch to provide this, so people don't need a lot of
// template metaprogramming to get `TObject`
using type = TObject;
};
template<typename TObject>
struct GetMetadata<AllowMissingMetadata<TObject>> : GetMetadata<TObject>
{
nlohmann::json description() const
{
return handle(GetMetadata<TObject>::description());
}
static nlohmann::json handle(nlohmann::json const& result)
{
// okay, we've found metadata, so we return it
return result;
}
static nlohmann::json handle(detail::ReturnTypeFromDefault<TObject> const& result)
{
// also okay, we couldn't find metadata, so we'll return an empty object
return nlohmann::json::object();
}
};
// doc-include-end: AllowMissingMetadata
/**
* Policy to provide context for incident fields
*
* Incident fields are lacking the knowledge of their own context (in particular from which direction they are
* incident), so we don't let them report immediately but instead use this policy to add the necessary context.
*
* @tparam Profiles a pmacc::MakeSeq_t list of profiles (typically this will be
* `picongpu::fields::incidentField::EnabledProfiles`).
*/
template<typename BoundaryName, typename Profiles>
struct IncidentFieldPolicy
{
};
namespace detail
{
/**
* Gather the metadata from a list of profiles into one annotated json object.
*
* @tparam T_Pack Any class template (typically pmacc::MakeSeq_t but could be anything, e.g., std::tuple),
* not used directly, we're only interested in its template parameters
* @tparam Profiles The list of CT profiles we're actually interested in and want to report about.
*/
template<template<typename...> typename T_Pack, typename... Profiles>
nlohmann::json gatherMetadata(T_Pack<Profiles...>)
{
std::vector<nlohmann::json> collection;
(collection.push_back(GetMetadata<AllowMissingMetadata<Profiles>>{}.description()), ...);
return collection;
}
} // namespace detail
template<typename BoundaryName, typename Profiles>
struct GetMetadata<IncidentFieldPolicy<BoundaryName, Profiles>>
{
nlohmann::json description() const
{
auto result = nlohmann::json::object();
result["incidentField"][BoundaryName::str()] = detail::gatherMetadata(pmacc::MakeSeq_t<Profiles>{});
return result;
}
};
} // namespace traits
} // namespace picongpu
namespace pmacc::math
{
/**
* Provide conversion of pmacc::math::Vector to json.
*/
template<typename T_Type, uint32_t T_dim, typename T_Navigator, typename T_Storage>
void to_json(nlohmann::json& j, Vector<T_Type, T_dim, T_Navigator, T_Storage> const& vec)
{
std::vector<T_Type> stdvec{};
for(size_t i = 0; i < T_dim; ++i)
{
stdvec.push_back(vec[i]);
}
j = stdvec;
}
} // namespace pmacc::math