ESPResSo
Extensible Simulation Package for Research on Soft Matter Systems
Loading...
Searching...
No Matches
BoundaryHandling.hpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2021-2023 The ESPResSo project
3 *
4 * This file is part of ESPResSo.
5 *
6 * ESPResSo is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ESPResSo is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
23
25
26#include <blockforest/StructuredBlockForest.h>
27#include <domain_decomposition/BlockDataID.h>
28#include <domain_decomposition/IBlock.h>
29#include <field/FlagField.h>
30#include <field/FlagUID.h>
31
32#include <utils/Vector.hpp>
33
34#if defined(__CUDACC__)
35#include <thrust/device_vector.h>
36#endif
37
38#include <cassert>
39#include <functional>
40#include <memory>
41#include <tuple>
42#include <unordered_map>
43#include <utility>
44#include <vector>
45
46namespace walberla {
47
48/**
49 * @brief Boundary class optimized for sparse data.
50 *
51 * Instead of storing the boundary data on a vector field,
52 * store individual vectors in a map.
53 * The global cell is used as key.
54 *
55 * Requires a custom communicator:
56 * @ref walberla::field::communication::BoundaryPackInfo.
57 */
58template <typename FloatType, typename ValueType, typename BoundaryClass>
60private:
61 /** Flag for domain cells, i.e. all cells. */
62 FlagUID const Domain_flag{"domain"};
63 /** Flag for boundary cells. */
64 FlagUID const Boundary_flag{"boundary"};
65
66 /** Container for the map between cells and values. */
67 class DynamicValueCallback {
68 public:
69 DynamicValueCallback() {
70 m_value_boundary =
71 std::make_shared<typename decltype(m_value_boundary)::element_type>();
72 }
73
74 [[nodiscard]] ValueType operator()(
75 Cell const &local,
76 std::shared_ptr<blockforest::StructuredBlockForest> const &blocks,
77 IBlock &block) const {
78 Cell global;
79 blocks->transformBlockLocalToGlobalCell(global, block, local);
80 return get_value(global);
81 }
82
83 void set_node_boundary_value(Cell const &global, ValueType const &val) {
84 (*m_value_boundary)[global] = val;
85 }
86
87 void unset_node_boundary_value(Cell const &global) {
88 assert(m_value_boundary->contains(global));
89 m_value_boundary->erase(global);
90 }
91
92 [[nodiscard]] auto &get_node_boundary_value(Cell const &global) const {
93 return get_value(global);
94 }
95
96 bool node_is_boundary(Cell const &global) const {
97 return m_value_boundary->contains(global);
98 }
99
100#if defined(__CUDACC__)
101 /**
102 * @brief Build a flattened version of the unordered map container.
103 * The coordinate list (COO) format is used, similar to how sparse
104 * matrices are compressed, although here zero is a valid value.
105 * Indices are relative to the origin of the local halo.
106 */
107 void rebuild_flat_map_device(CellInterval const &local_domain) {
108 std::vector<int> indices;
109 std::vector<FloatType> values;
110 auto const &local_origin = local_domain.min();
111 for (auto const &[cell, value] : *m_value_boundary) {
112 if (local_domain.contains(cell)) {
113 for (auto i : {0, 1, 2}) {
114 indices.emplace_back(cell[i] - local_origin[i]);
115 }
116 if constexpr (std::is_arithmetic_v<ValueType>) {
117 values.emplace_back(static_cast<FloatType>(value));
118 } else {
119 for (auto i : {0, 1, 2}) {
120 values.emplace_back(static_cast<FloatType>(value[i]));
121 }
122 }
123 }
124 }
125 m_flat_indices = decltype(m_flat_indices)(indices.begin(), indices.end());
126 m_flat_values = decltype(m_flat_values)(values.begin(), values.end());
127 }
128
129 auto get_flattened_map_device() const {
130 return std::make_pair(&m_flat_indices, &m_flat_values);
131 }
132#endif
133
134 private:
135#if defined(__CUDACC__)
136 thrust::device_vector<int> m_flat_indices;
137 thrust::device_vector<FloatType> m_flat_values;
138#endif
139 std::shared_ptr<std::unordered_map<Cell, ValueType>> m_value_boundary;
140 static constexpr ValueType default_value{};
141
142 [[nodiscard]] auto const &get_value(Cell const &cell) const {
143 if (m_value_boundary->contains(cell)) {
144 return m_value_boundary->at(cell);
145 }
146 return default_value;
147 }
148 };
149
150 [[nodiscard]] inline auto get_flag_field_and_flag(IBlock *block) const {
151 auto const flag_field =
152 block->template uncheckedFastGetData<FlagField>(m_flag_field_id);
153 auto const boundary_flag = flag_field->getFlag(Boundary_flag);
154 return std::make_tuple(flag_field, boundary_flag);
155 }
156
157public:
158 using value_type = ValueType;
159 using FlagField = field::FlagField<uint8_t>;
160
161 BoundaryHandling(std::shared_ptr<StructuredBlockForest> blocks,
162 BlockDataID value_field_id, BlockDataID flag_field_id,
163 CellInterval const &local_domain)
164 : m_blocks(std::move(blocks)), m_flag_field_id(flag_field_id),
165 m_callback(DynamicValueCallback()), m_local_domain(local_domain),
166 m_pending_changes(false) {
167 // reinitialize the flag field
168 for (auto &block : *m_blocks) {
169 flag_reset_kernel(block.template getData<FlagField>(m_flag_field_id));
170 }
171 // instantiate the boundary sweep
172 std::function callback = m_callback;
173 m_boundary =
174 std::make_shared<BoundaryClass>(m_blocks, value_field_id, callback);
175 }
176
177 void operator()(IBlock *block) { (*m_boundary)(block); }
178
179 [[nodiscard]] bool
180 node_is_boundary(signed_integral_vector auto const &node) const {
181 return m_callback.node_is_boundary(to_cell(node));
182 }
183
184 [[nodiscard]] auto &
186 return m_callback.get_node_boundary_value(to_cell(node));
187 }
188
190 ValueType const &v, BlockAndCell const &bc) {
191 auto [flag_field, boundary_flag] = get_flag_field_and_flag(bc.block);
192 m_callback.set_node_boundary_value(to_cell(node), v);
193 flag_field->addFlag(bc.cell, boundary_flag);
194 m_pending_changes = true;
195 }
196
197 void unpack_node(signed_integral_vector auto const &node,
198 ValueType const &v) {
199 m_callback.set_node_boundary_value(to_cell(node), v);
200 }
201
203 BlockAndCell const &bc) {
204 auto [flag_field, boundary_flag] = get_flag_field_and_flag(bc.block);
205 m_callback.unset_node_boundary_value(to_cell(node));
206 flag_field->removeFlag(bc.cell, boundary_flag);
207 m_pending_changes = true;
208 }
209
210 /** Assign boundary conditions to boundary cells. */
212 if (m_pending_changes) {
213 m_boundary->template fillFromFlagField<FlagField>(
214 m_blocks, m_flag_field_id, Boundary_flag, Domain_flag);
215#if defined(__CUDACC__)
216 m_callback.rebuild_flat_map_device(m_local_domain);
217#endif
218 m_pending_changes = false;
219 }
220 }
221
222 std::tuple<int, int, int> block_dims(IBlock const &block) const {
223 auto const field = block.template getData<FlagField>(m_flag_field_id);
224 return {static_cast<int>(field->xSize()), static_cast<int>(field->ySize()),
225 static_cast<int>(field->zSize())};
226 }
227
228#if defined(__CUDACC__)
229 auto get_flattened_map_device() const {
230 return m_callback.get_flattened_map_device();
231 }
232#endif
233
234 Vector3<double> get_total_force(IBlock *block) const {
235 return m_boundary->getForce(block);
236 }
237
238 auto const &get_force_vector(IBlock *block) const {
239 using ForceVector = BoundaryClass::ForceVector;
240 auto const force_vector_id = m_boundary->getForceVectorID();
241 auto *forceVector = block->getData<ForceVector>(force_vector_id);
242 forceVector->syncCPU();
243 return m_boundary->getForceVector(block);
244 }
245 auto const &get_index_vector(IBlock const *block) const {
246 return m_boundary->getIndexVector(block);
247 }
248
249private:
250 std::shared_ptr<StructuredBlockForest> m_blocks;
251 BlockDataID m_flag_field_id;
252 DynamicValueCallback m_callback;
253 std::shared_ptr<BoundaryClass> m_boundary;
254 CellInterval m_local_domain;
255 bool m_pending_changes;
256
257 /** Register flags and reset all cells. */
258 void flag_reset_kernel(FlagField *flag_field) {
259 // register flags
260 if (!flag_field->flagExists(Domain_flag))
261 flag_field->registerFlag(Domain_flag);
262 if (!flag_field->flagExists(Boundary_flag))
263 flag_field->registerFlag(Boundary_flag);
264 // mark all cells as domain cells and fluid cells
265 auto domain_flag = flag_field->getFlag(Domain_flag);
266 auto boundary_flag = flag_field->getFlag(Boundary_flag);
267 for (auto it = flag_field->begin(); it != flag_field->end(); ++it) {
268 flag_field->addFlag(it.x(), it.y(), it.z(), domain_flag);
269 flag_field->removeFlag(it.x(), it.y(), it.z(), boundary_flag);
270 }
271 }
272};
273
274} // namespace walberla
Vector implementation and trait types for boost qvm interoperability.
Definition Cell.hpp:96
Boundary class optimized for sparse data.
std::tuple< int, int, int > block_dims(IBlock const &block) const
auto & get_node_value_at_boundary(signed_integral_vector auto const &node) const
Vector3< double > get_total_force(IBlock *block) const
auto const & get_force_vector(IBlock *block) const
field::FlagField< uint8_t > FlagField
void remove_node_from_boundary(signed_integral_vector auto const &node, BlockAndCell const &bc)
void boundary_update()
Assign boundary conditions to boundary cells.
void unpack_node(signed_integral_vector auto const &node, ValueType const &v)
auto const & get_index_vector(IBlock const *block) const
BoundaryHandling(std::shared_ptr< StructuredBlockForest > blocks, BlockDataID value_field_id, BlockDataID flag_field_id, CellInterval const &local_domain)
bool node_is_boundary(signed_integral_vector auto const &node) const
void set_node_value_at_boundary(signed_integral_vector auto const &node, ValueType const &v, BlockAndCell const &bc)
static double * block(double *p, std::size_t index, std::size_t size)
Definition elc.cpp:176
STL namespace.
\file PackInfoPdfDoublePrecision.cpp \author pystencils
Cell to_cell(signed_integral_vector auto const &xyz)