peclet.voro 1.0.4
Device-native moving-particle Voronoi dynamics
Loading...
Searching...
No Matches
voro_bindings_common.hpp
Go to the documentation of this file.
1
7#ifndef PECLET_VORO_BINDINGS_COMMON_HPP
8#define PECLET_VORO_BINDINGS_COMMON_HPP
9
10#include <nanobind/nanobind.h>
11#include <nanobind/ndarray.h>
12#include <nanobind/stl/array.h>
13#include <nanobind/stl/optional.h>
14#include <nanobind/stl/string.h>
15
16#include <algorithm>
17#include <array>
18#include <cmath>
19#include <cstdio>
20#include <deque>
21#include <Kokkos_Core.hpp>
22#include <optional>
23#include <set>
24#include <stdexcept>
25#include <string>
26#include <type_traits>
27#include <variant>
28#include <vector>
29
30#include "peclet/core/common/view.hpp"
31#include "peclet/core/geom/scene_builder.hpp"
32#include "peclet/core/python/kokkos_teardown.hpp"
33#include "peclet/core/python/ndarray_interop.hpp"
35#include "peclet/voro/sdf.hpp"
36
37namespace nb = nanobind;
38using real_t = double;
39using DView = Kokkos::View<real_t*, peclet::core::MemSpace>;
40
41// A NAMED namespace on purpose: Tess / Flow / Sim have virtual methods (Releasable), and under
42// hipcc/lld a virtual class in an anonymous namespace leaves its vtable unemitted in the host
43// object ("undefined hidden symbol: vtable for (anonymous namespace)::Tess"). Same fix as
44// core/python/amr_bindings.cpp.
46
47// --------------------------------------------------------------------------------------------------
48// The named defaults (QUALITY_PLAN G.7 "TessellationParams"): every literal the bound surface
49// drives the engine with, in one place. `peclet.voro.defaults` exposes them and the docstrings
50// below are generated from them.
51// --------------------------------------------------------------------------------------------------
52namespace defaults {
53// The engine-level defaults live in include/peclet/voro/params.hpp (the template defaults and
54// default arguments of the headers name them); re-exported here, never repeated: the ConvexCell
55// capacities of the resident tessellation and of the pore-space reconstruction, the grid gather
56// window, the repair tolerance + Verlet skin (fractions of the mean spacing cbrt(V/N)), the
57// optimisers' CG cap and the log-barrier decay.
67// SDF geometry: the central-difference step of the SDF gradient; the wall re-gather skin
68// (fraction of the mean spacing) of the 'skin' wall mode.
69constexpr double kSdfGradientStep = 1e-5;
70constexpr double kWallSkin = 0.0;
71// Mesh optimisers (Gauss-Newton on Σ(V/V_ref − 1)², the interface minimiser), bound surface only:
72// the optimiser's own search window, iteration caps, gradient tolerance, the pore-space CG cap,
73// and the interface tension.
74constexpr int kOptimizerSearchWindow = 5;
75constexpr int kPoreSearchWindow = 6;
76constexpr int kOptimizerMaxIter = 60;
77constexpr int kPoreMaxIter = 80;
78constexpr double kOptimizerTolerance = 1e-9;
79constexpr int kPoreCgIters = 400;
80constexpr double kInterfaceSigma = 1.0;
81// The host pore-cell oracle's cap on its counting-sort grid resolution (bins per axis).
82constexpr int kPoreMaxBins = 96;
83// Distributed repair driver (validated by tests/kokkos_mpi/bench_repair_mpi at np = 1, 2, 4): the
84// ghost cutoff, in mean spacings, and the ORB granularity per axis.
85constexpr double kDistributedRcut = 3.5;
86constexpr long kDistributedCells = 16;
87} // namespace defaults
88
89// A docstring assembled at module init from the defaults (nanobind keeps the pointer; the deque
90// keeps the storage stable).
91inline const char* doc(std::string s) {
92 static std::deque<std::string> keep;
93 keep.push_back(std::move(s));
94 return keep.back().c_str();
95}
96inline std::string fmt(double v) {
97 char b[32];
98 std::snprintf(b, sizeof b, "%g", v);
99 return b;
100}
101inline std::string fmt(int v) {
102 return std::to_string(v);
103}
104
105// (N,3) c-contiguous array -> flat row-major host vector of length 3N.
106inline std::vector<real_t> flatten3(nb::ndarray<real_t, nb::c_contig> a) {
107 if (a.ndim() != 2 || a.shape(1) != 3)
108 throw std::runtime_error("expected an (N,3) array");
109 const real_t* p = a.data();
110 return std::vector<real_t>(p, p + static_cast<std::size_t>(a.shape(0)) * 3);
111}
112
113// (N,) array -> host vector of length N.
114inline std::vector<real_t> flatten1(nb::ndarray<real_t, nb::c_contig> a) {
115 return peclet::core::python::ndarray_to_vector<real_t>(nb::ndarray<>(a));
116}
117
118// The union-of-spheres wall SDF is cubic-periodic (one L), so the pore-space functions take the
119// suite-wide `extent` triple and check it is a cube.
120inline real_t cubicExtent(std::array<real_t, 3> e, const char* fn) {
121 if (!(e[0] > real_t(0)) || e[0] != e[1] || e[1] != e[2])
122 throw std::invalid_argument(std::string("voro: ") + fn +
123 "(extent=...) must be a cubic box (Lx == Ly == Lz > 0) — the "
124 "union-of-spheres wall SDF is cubic-periodic.");
125 return e[0];
126}
127
128// ---- SDF geometry from Python (rung A0) ---------------------------------------------------------
131using Mem = peclet::core::MemSpace;
132
133// A device-resident core shape scene + the SdfScene provider over it. The node table comes from the
134// flat node encoding (3 int32 + 16 float64 per node) that peclet.geom.SceneBuilder.encode() returns
135// (the same arrays dem.add_analytic_wall takes); the encoding carries no sampled grids, so this is
136// the ANALYTIC vocabulary (primitives + CSG + transforms).
138 Kokkos::View<peclet::core::geom::ShapeNode<real_t>*, Mem> nodes;
139 Kokkos::View<peclet::core::geom::GridDesc<real_t>*, Mem> grids;
140 Kokkos::View<float*, Mem> pool;
142 bool set = false;
143 void clear() {
144 nodes = {};
145 grids = {};
146 pool = {};
147 scene = SceneT{};
148 set = false;
149 }
150};
151
152inline SceneHolder makeSceneHolder(nb::ndarray<int, nb::c_contig> node_ints,
153 nb::ndarray<real_t, nb::c_contig> node_reals, int root,
154 real_t grad_h) {
155 using namespace peclet::core::geom;
156 std::vector<int> ni(node_ints.data(), node_ints.data() + node_ints.size());
157 std::vector<real_t> nr(node_reals.data(), node_reals.data() + node_reals.size());
158 SceneBuilder<real_t> b = SceneBuilder<real_t>::decode(ni, nr, {}, {}, {}, {});
159 const auto& nodes = b.nodes();
160 if (nodes.empty())
161 throw std::runtime_error("set_geometry: empty node table");
162 if (root < 0 || root >= static_cast<int>(nodes.size()))
163 throw std::runtime_error("set_geometry: root node index out of range");
164 SceneHolder h;
165 h.nodes = Kokkos::View<ShapeNode<real_t>*, Mem>("scene.nodes", nodes.size());
166 {
167 auto hn = Kokkos::create_mirror_view(h.nodes);
168 for (std::size_t i = 0; i < nodes.size(); ++i)
169 hn(i) = nodes[i];
170 Kokkos::deep_copy(h.nodes, hn);
171 }
172 const auto& grids = b.grids();
173 const auto& pool = b.samples();
174 h.grids = Kokkos::View<GridDesc<real_t>*, Mem>("scene.grids", grids.empty() ? 1 : grids.size());
175 h.pool = Kokkos::View<float*, Mem>("scene.pool", pool.empty() ? 1 : pool.size());
176 if (!grids.empty()) {
177 auto hg = Kokkos::create_mirror_view(h.grids);
178 for (std::size_t i = 0; i < grids.size(); ++i)
179 hg(i) = grids[i];
180 Kokkos::deep_copy(h.grids, hg);
181 }
182 if (!pool.empty())
183 Kokkos::deep_copy(h.pool,
184 Kokkos::View<const float*, Kokkos::HostSpace>(pool.data(), pool.size()));
185 h.scene = SceneT{h.nodes, h.grids, h.pool, static_cast<int>(nodes.size()), root, grad_h};
186 h.set = true;
187 return h;
188}
189
190// The suite-wide domain contract of this engine (docs/NAMING.md 1.1): `extent` is the box SIZE;
191// the tessellator's periodic box always starts at the origin and is periodic on all three axes, so
192// `origin` and `periodic` exist to be CHECKED rather than stored — a caller who writes what every
193// other code in the suite writes gets an error naming the limitation instead of a silently
194// ignored argument.
195inline void checkDomain(const char* cls, std::array<real_t, 3> extent, std::array<real_t, 3> origin,
196 std::array<bool, 3> periodic) {
197 for (int a = 0; a < 3; ++a) {
198 if (!(extent[a] > real_t(0)))
199 throw std::invalid_argument(std::string("voro: ") + cls +
200 ".set_domain(extent=...) needs three positive lengths.");
201 if (origin[a] != real_t(0))
202 throw std::invalid_argument(std::string("voro: ") + cls +
203 ".set_domain(origin=...) must be (0, 0, 0) — the tessellator's "
204 "periodic box is anchored at the origin. Shift your points "
205 "instead.");
206 if (!periodic[a])
207 throw std::invalid_argument(std::string("voro: ") + cls +
208 ".set_domain(periodic=...) must be (True, True, True) — this "
209 "engine has no non-periodic axis. Use a wall SDF (set_geometry) "
210 "to bound the domain.");
211 }
212}
213
214// Wall re-gather policy of the incremental step, as a string mode.
215constexpr const char* kWallModeList = "'exact', 'skin'";
216inline bool parseWallMode(const std::string& mode) {
217 if (mode == "exact")
218 return true;
219 if (mode == "skin")
220 return false;
221 throw std::invalid_argument("voro: set_wall_mode(mode='" + mode +
222 "') is not a wall mode; accepted: " + kWallModeList + ".");
223}
224
225inline nb::ndarray<nb::numpy, real_t> toNumpy3(std::vector<real_t> v) {
226 const std::size_t N = v.size() / 3;
227 return peclet::core::python::vector_to_ndarray(std::move(v), {N, std::size_t(3)}, {3, 1});
228}
229inline nb::ndarray<nb::numpy, real_t> toNumpy1(std::vector<real_t> v) {
230 const std::size_t N = v.size();
231 return peclet::core::python::vector_to_ndarray(std::move(v), {N}, {1});
232}
233
234// --------------------------------------------------------------------------------------------------
235// Typed results of the mesh optimisers (no dict-of-strings, no field reuse across meanings).
236// --------------------------------------------------------------------------------------------------
238 nb::object positions;
239 nb::object weights;
240 int iters = 0;
241 double max_vol_err = 0, mean_vol_err = 0;
242 bool converged = false;
243 int num_empty = 0;
244};
245
247 nb::object positions;
248 double energy = 0, energy_ratio = 1;
249 int iters = 0;
250 bool converged = false;
251};
252
253// ---- the per-subsystem binding registrars (one translation unit each) ---------------------------
254// The module is split by subsystem so that nvcc compiles the device instantiations of the
255// tessellator, the FV solvers, the moving-cell fluid, the mesh optimisers and the pore-space
256// family CONCURRENTLY under `make -j` instead of serially inside one object. Each TU owns its
257// classes and registers them here; `voro_bindings.cpp` holds only NB_MODULE.
258void bindOptimizers(nb::module_& m);
259void bindPore(nb::module_& m);
260void bindTessellation(nb::module_& m);
261void bindFlow(nb::module_& m);
262void bindSimulation(nb::module_& m);
263#ifdef PECLET_VORO_MPI
264void bindMpi(nb::module_& m);
265#endif
266
267} // namespace peclet::voro::pybind
268
269#endif // PECLET_VORO_BINDINGS_COMMON_HPP
constexpr int kPoreCgIters
Definition voro_bindings_common.hpp:79
constexpr double kInterfaceSigma
Definition voro_bindings_common.hpp:80
constexpr int kPoreMaxIter
Definition voro_bindings_common.hpp:77
constexpr int kOptimizerSearchWindow
Definition voro_bindings_common.hpp:74
constexpr int kPoreSearchWindow
Definition voro_bindings_common.hpp:75
constexpr int kPoreMaxBins
Definition voro_bindings_common.hpp:82
constexpr long kDistributedCells
Definition voro_bindings_common.hpp:86
constexpr double kSdfGradientStep
Definition voro_bindings_common.hpp:69
constexpr int kOptimizerMaxIter
Definition voro_bindings_common.hpp:76
constexpr double kDistributedRcut
Definition voro_bindings_common.hpp:85
constexpr double kWallSkin
Definition voro_bindings_common.hpp:70
constexpr double kOptimizerTolerance
Definition voro_bindings_common.hpp:78
Definition voro_bindings_common.hpp:45
const char * doc(std::string s)
Definition voro_bindings_common.hpp:91
nb::ndarray< nb::numpy, real_t > toNumpy3(std::vector< real_t > v)
Definition voro_bindings_common.hpp:225
nb::ndarray< nb::numpy, real_t > toNumpy1(std::vector< real_t > v)
Definition voro_bindings_common.hpp:229
void checkDomain(const char *cls, std::array< real_t, 3 > extent, std::array< real_t, 3 > origin, std::array< bool, 3 > periodic)
Definition voro_bindings_common.hpp:195
std::vector< real_t > flatten1(nb::ndarray< real_t, nb::c_contig > a)
Definition voro_bindings_common.hpp:114
std::vector< real_t > flatten3(nb::ndarray< real_t, nb::c_contig > a)
Definition voro_bindings_common.hpp:106
std::string fmt(double v)
Definition voro_bindings_common.hpp:96
bool parseWallMode(const std::string &mode)
Definition voro_bindings_common.hpp:216
peclet::core::MemSpace Mem
Definition voro_bindings_common.hpp:131
void bindOptimizers(nb::module_ &m)
Definition voro_optimizers.cpp:7
void bindSimulation(nb::module_ &m)
Definition voro_simulation.cpp:232
SceneHolder makeSceneHolder(nb::ndarray< int, nb::c_contig > node_ints, nb::ndarray< real_t, nb::c_contig > node_reals, int root, real_t grad_h)
Definition voro_bindings_common.hpp:152
void bindFlow(nb::module_ &m)
Definition voro_flow.cpp:181
real_t cubicExtent(std::array< real_t, 3 > e, const char *fn)
Definition voro_bindings_common.hpp:120
constexpr const char * kWallModeList
Definition voro_bindings_common.hpp:215
void bindPore(nb::module_ &m)
Definition voro_pore.cpp:167
void bindTessellation(nb::module_ &m)
Definition voro_tessellation.cpp:26
constexpr int kPoreMaxTriangles
Definition params.hpp:24
constexpr int kOptimizerCgIters
Definition params.hpp:37
constexpr double kBarrierDecay
Definition params.hpp:38
constexpr int kMaxPlanes
Definition params.hpp:18
constexpr double kSkin
Definition params.hpp:33
constexpr double kCertificateTolerance
Definition params.hpp:32
constexpr int kMaxTriangles
Definition params.hpp:19
constexpr int kPoreMaxPlanes
Definition params.hpp:23
constexpr int kSearchWindow
Definition params.hpp:28
The named engine defaults of peclet.voro — one header-level home for every capacity and default the l...
Sentinel "no geometry" provider — the default; the clip stage is skipped.
Definition sdf.hpp:39
Definition voro_bindings_common.hpp:246
int iters
Definition voro_bindings_common.hpp:249
bool converged
Definition voro_bindings_common.hpp:250
double energy
Definition voro_bindings_common.hpp:248
double energy_ratio
Definition voro_bindings_common.hpp:248
nb::object positions
(N,3) float64
Definition voro_bindings_common.hpp:247
Definition voro_bindings_common.hpp:237
bool converged
Definition voro_bindings_common.hpp:242
nb::object positions
(N,3) float64
Definition voro_bindings_common.hpp:238
int iters
Definition voro_bindings_common.hpp:240
int num_empty
Definition voro_bindings_common.hpp:243
nb::object weights
(N,) float64 when the power weights were optimised, else None
Definition voro_bindings_common.hpp:239
double max_vol_err
Definition voro_bindings_common.hpp:241
double mean_vol_err
Definition voro_bindings_common.hpp:241
Definition voro_bindings_common.hpp:137
SceneT scene
Definition voro_bindings_common.hpp:141
void clear()
Definition voro_bindings_common.hpp:143
Kokkos::View< peclet::core::geom::ShapeNode< real_t > *, Mem > nodes
Definition voro_bindings_common.hpp:138
Kokkos::View< float *, Mem > pool
Definition voro_bindings_common.hpp:140
Kokkos::View< peclet::core::geom::GridDesc< real_t > *, Mem > grids
Definition voro_bindings_common.hpp:139
bool set
Definition voro_bindings_common.hpp:142
Kokkos::View< real_t *, peclet::core::MemSpace > DView
Definition voro_bindings_common.hpp:39
double real_t
Definition voro_bindings_common.hpp:38