45class PDataArrayHelper {
47 PDataArrayHelper(
const Encoder& e,
55 void add(
const std::string& name,
const Field& field) {
58 static constexpr std::size_t vtk_space_dim = 3;
59 const auto layout = field.
layout();
60 std::size_t ncomps = std::pow(vtk_space_dim, field.
layout().dimension() - 1);
63 if (layout.dimension() == 2 && layout.extent(1) > vtk_space_dim)
64 ncomps = layout.extent(1);
66 XMLElement& arr = _element.add_child(
"PDataArray");
67 arr.set_attribute(
"Name", name);
68 arr.set_attribute(
"type", VTK::attribute_name(field.
precision()));
69 arr.set_attribute(
"format", VTK::data_format_name(_encoder, _data_format));
70 arr.set_attribute(
"NumberOfComponents", ncomps);
74 const Encoder& _encoder;
75 const DataFormat& _data_format;
80class StructuredGridMapper {
82 using Location = std::array<std::size_t, dim>;
84 explicit StructuredGridMapper(std::vector<Location>&& map)
85 : _map{std::move(map)}
88 const auto& location(std::integral
auto rank)
const {
92 std::ranges::view
auto ranks_below(
const Location& loc,
unsigned direction)
const {
93 return std::views::filter(
94 std::views::iota(std::size_t{0}, _map.size()),
95 [&, loc=loc, direction=direction] (
const std::size_t rank) {
96 const auto& rank_loc = _map[rank];
97 for (
unsigned dir = 0; dir < dim; ++dir)
98 if (dir != direction && rank_loc[dir] != loc[dir]) {
101 return rank_loc[direction] < loc[direction];
107 std::vector<Location> _map;
113class StructuredGridMapperHelper {
115 using Origin = std::array<T, dim>;
116 using Basis = std::array<Origin, dim>;
118 explicit StructuredGridMapperHelper(std::integral
auto ranks,
120 T default_epsilon = 1e-6)
124 , _default_epsilon{default_epsilon} {
125 std::ranges::fill(_reverse,
false);
128 void reverse(
int direction) {
129 _reverse[direction] = !_reverse[direction];
132 void set_origin_for(
int rank, Origin origin) {
134 throw ValueError(
"Origin for given rank already set");
135 _origins[rank] = std::move(origin);
139 auto make_mapper()
const {
140 if (_origins.empty())
141 throw InvalidState(
"No origins have been set");
143 std::vector<std::array<std::size_t, dim>> map(_origins.size());
144 for (
unsigned dir = 0; dir < dim; ++dir) {
145 auto ordinates = _get_ordinates(dir);
146 const auto eps = _epsilon(ordinates);
147 _sort_ordinates(ordinates, eps, _reverse[dir]);
149 if (ordinates.size() > 1) {
150 for (
unsigned rank = 0; rank < _origins.size(); ++rank) {
151 const auto rank_origin_ordinate = _get_ordinate(_origins[rank], dir);
152 auto it = std::ranges::find_if(ordinates, [&] (
const T o) {
154 return abs(o - rank_origin_ordinate) < eps;
156 if (it == ordinates.end())
157 throw InvalidState(
"Could not determine rank ordinate");
158 map[rank][dir] = std::distance(ordinates.begin(), it);
161 for (
unsigned rank = 0; rank < _origins.size(); ++rank)
168 Origin compute_origin()
const {
169 if (_origins.empty())
170 throw InvalidState(
"No origins have been set");
173 for (
unsigned dir = 0; dir < dim; ++dir) {
174 auto ordinates = _get_ordinates(dir);
175 _sort_ordinates(ordinates, _reverse[dir]);
176 result[dir] = ordinates[0];
182 auto _get_ordinates(
unsigned int axis)
const {
183 std::vector<T> result;
184 result.reserve(_origins.size());
186 std::views::transform(_origins, [&] (
const Origin& o) {
187 return _get_ordinate(o, axis);
189 std::back_inserter(result)
194 auto _get_ordinate(
const Origin& o,
unsigned int axis)
const {
195 return dot_product(o, _basis[axis]);
198 void _sort_ordinates(std::vector<T>& ordinates,
bool reverse)
const {
199 if (ordinates.size() > 1)
200 _sort_ordinates(ordinates, _epsilon(ordinates), reverse);
203 void _sort_ordinates(std::vector<T>& ordinates,
const T& eps,
bool reverse)
const {
204 if (ordinates.size() > 1) {
205 auto [it, _] = Ranges::sort_and_unique(ordinates, {}, [&] (T a, T b) {
207 return abs(a - b) < eps;
209 ordinates.erase(it, ordinates.end());
211 std::ranges::reverse(ordinates);
215 T _epsilon(
const std::vector<T>& ordinates)
const {
216 const auto max = *std::ranges::max_element(ordinates);
217 const auto min = *std::ranges::min_element(ordinates);
218 const auto size = max - min;
220 if (size > _default_epsilon) {
221 std::vector<T> dx; dx.reserve(ordinates.size());
222 std::adjacent_difference(ordinates.begin(), ordinates.end(), std::back_inserter(dx));
223 std::ranges::for_each(dx, [] (
auto& v) { v = std::abs(v); });
224 std::ranges::sort(dx);
230 return _default_epsilon;
234 std::vector<Origin> _origins;
235 std::vector<bool> _set;
236 std::array<bool, dim> _reverse;
242class StructuredParallelGridHelper {
244 StructuredParallelGridHelper(
const Communicator& comm,
int root_rank = 0)
246 , _root_rank{root_rank}
249 template<Concepts::Scalar CT,
251 Concepts::StaticallySizedMDRange<2> B = std::array<std::array<CT, dim>, dim>>
252 auto compute_extents_and_origin(
const std::vector<CT>& all_origins,
253 const std::vector<std::size_t>& all_extents,
254 const std::array<bool, dim>& is_negative_axis,
255 const B& basis = GridDetail::standard_basis<CT, dim>())
const {
256 const auto num_ranks = Parallel::size(_comm);
257 std::vector<std::array<std::size_t, dim>> pieces_begin(num_ranks);
258 std::vector<std::array<std::size_t, dim>> pieces_end(num_ranks);
259 std::array<std::size_t, dim> whole_extent;
260 std::array<CT, dim> origin;
263 Parallel::invoke_collectively(_comm, [&] () {
264 if (Parallel::rank(_comm) == _root_rank) {
265 const auto default_epsilon = 1e-6*std::ranges::max(all_origins | std::views::transform([] (CT v) {
269 const auto mapper_helper = _make_mapper_helper(basis, all_origins, is_negative_axis, default_epsilon);
270 const auto rank_mapper = mapper_helper.make_mapper();
271 origin = mapper_helper.compute_origin();
273 for (
unsigned dir = 0; dir < dim; ++dir) {
274 for (
int rank = 0; rank < num_ranks; ++rank) {
275 auto ranks_below = rank_mapper.ranks_below(rank_mapper.location(rank), dir);
276 const std::size_t offset = std::accumulate(
277 std::ranges::begin(ranks_below),
278 std::ranges::end(ranks_below),
280 [&] (
const std::size_t current,
int r) {
281 return current + Parallel::access_gathered<dim>(all_extents, _comm, {dir, r});
284 pieces_begin[rank][dir] = offset;
285 pieces_end[rank][dir] = offset + Parallel::access_gathered<dim>(all_extents, _comm, {dir, rank});
288 whole_extent[dir] = (*std::max_element(
289 pieces_end.begin(), pieces_end.end(),
290 [&] (
const auto& a1,
const auto& a2) { return a1[dir] < a2[dir]; }
294 },
"Computing the extents of the parallel grid");
296 return std::make_tuple(
297 std::move(pieces_begin),
298 std::move(pieces_end),
299 std::move(whole_extent),
305 template<Concepts::StaticallySizedMDRange<2> Basis, Concepts::Scalar CT, std::
size_t dim>
306 auto _make_mapper_helper(
const Basis& basis,
307 const std::vector<CT>& all_origins,
308 const std::array<bool, dim>& is_negative_axis,
309 CT default_eps)
const {
311 std::ranges::for_each(Parallel::ranks(_comm), [&] (
int rank) {
312 helper.set_origin_for(rank, Parallel::access_gathered<dim>(all_origins, _comm, rank));
315 for (
unsigned dir = 0; dir < dim; ++dir)
316 if (is_negative_axis[dir])
322 const Communicator& _comm;