warpSPHBoundaries 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- warpsphboundaries-0.1.0/LICENSE +21 -0
- warpsphboundaries-0.1.0/MANIFEST.in +3 -0
- warpsphboundaries-0.1.0/PKG-INFO +126 -0
- warpsphboundaries-0.1.0/README.md +92 -0
- warpsphboundaries-0.1.0/pyproject.toml +44 -0
- warpsphboundaries-0.1.0/setup.cfg +4 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/__init__.py +18 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/curvbound/__init__.py +24 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/curvbound/kernels.py +57 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/curvbound/oracle.py +133 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/curvbound/symbolic.py +178 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/data/symbolic/planar_export.txt +19 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/data/symbolic/sphere_export.txt +13 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/data/tables/disk_cone_3_3_12_12_6_3_12_12.npz +0 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/data/tables/disk_lw2_3_3_12_12_6_3_12_12.npz +0 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/data/tables/disk_lw4_3_3_12_12_6_3_12_12.npz +0 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/data/tables/disk_w2_3_3_12_12_6_3_12_12.npz +0 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/data/tables/disk_w2p5_3_3_12_12_6_3_12_12.npz +0 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/data/tables/disk_w4_3_3_12_12_6_3_12_12.npz +0 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/data/tables/disk_w4p5_3_3_12_12_6_3_12_12.npz +0 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/data/tables/tier3_b7.npz +0 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/data/tables/tier3_b8.npz +0 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/data/tables/tier3_cubic.npz +0 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/data/tables/tier3_poly6.npz +0 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/data/tables/tier3_quartic.npz +0 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/data/tables/tier3_quintic.npz +0 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/data/tables/tier3_w2.npz +0 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/data/tables/tier3_w4.npz +0 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/data/tables/tier3_w6.npz +0 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/__init__.py +2 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/core.py +257 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/disk.py +92 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/disktables.py +127 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/fem.py +222 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/fem_fixtures.py +93 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/fem_study.py +66 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/fixtures.py +287 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/geometry.py +177 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/kernels.py +243 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/mpq.py +40 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/np2d.py +680 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/np_fem.py +225 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/oracle.py +256 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/precision.py +68 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/primitives.py +94 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/tier3.py +131 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/tier4.py +160 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/tier_select.py +44 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/torch2d.py +321 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/warp2d.py +502 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/warpbc.py +953 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/warpdisk.py +199 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/edge/warpfused.py +210 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/paths.py +50 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/scene/__init__.py +20 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/scene/boundaryOps.py +403 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/scene/box.py +277 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/scene/cone_area.py +155 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/scene/cover.py +147 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/scene/fixedadj.py +250 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/scene/fused.py +751 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/scene/implicitBodies.py +315 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/scene/massprops.py +70 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/scene/particles.py +195 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/scene/periodic.py +41 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/scene/provider.py +113 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/scene/scene.py +1565 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/scene/tensile.py +89 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/scene/viscosity.py +109 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/__init__.py +2 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/bcclosures.py +40 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/cases.py +136 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/deltasph2d.py +1031 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/dfsph2d.py +1054 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/dfsph_cases.py +36 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/dfsph_kernels.py +124 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/dfsph_ref.py +76 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/fluidwarp.py +145 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/graphstep.py +159 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/loads.py +73 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/modules/__init__.py +6 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/modules/shifting/__init__.py +5 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/modules/shifting/wp_deltaShiftRaw.py +285 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/modules/shifting/wp_minNeighbourNormalDot.py +229 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/modules/surfaceDetection/__init__.py +5 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/modules/surfaceDetection/wp_barecascoCone.py +265 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/modules/surfaceDetection/wp_barecascoCover.py +244 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/modules/wallComplement/__init__.py +5 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/modules/wallComplement/wp_complementFirstMoment.py +259 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/modules/wallComplement/wp_complementMoment.py +291 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/pairs.py +84 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/pinned.py +32 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/probes.py +64 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/projection.py +182 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/references.py +58 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/solvers.py +202 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/system.py +141 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/validation.py +195 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/wallclosure.py +251 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries/sim/wallmoments.py +324 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries.egg-info/PKG-INFO +126 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries.egg-info/SOURCES.txt +103 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries.egg-info/dependency_links.txt +1 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries.egg-info/requires.txt +16 -0
- warpsphboundaries-0.1.0/src/warpSPHBoundaries.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rene Winchenbach
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: warpSPHBoundaries
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Exact SPH boundary integrals via divergence-theorem edge reductions (2D), scene layer and delta+-SPH / DFSPH solvers on analytic boundaries
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Repository, https://github.com/wi-re/warpSPHBoundaries
|
|
7
|
+
Project-URL: Issues, https://github.com/wi-re/warpSPHBoundaries/issues
|
|
8
|
+
Keywords: sph,smoothed-particle-hydrodynamics,boundary-conditions,warp,pytorch,analytic-boundaries
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: numpy
|
|
21
|
+
Requires-Dist: scipy
|
|
22
|
+
Requires-Dist: mpmath
|
|
23
|
+
Requires-Dist: torch
|
|
24
|
+
Requires-Dist: warp-lang
|
|
25
|
+
Requires-Dist: warpSPHCore>=0.6.0
|
|
26
|
+
Provides-Extra: solvers
|
|
27
|
+
Requires-Dist: warpSPH>=0.6.0; extra == "solvers"
|
|
28
|
+
Requires-Dist: warpSPHCore>=0.6.0; extra == "solvers"
|
|
29
|
+
Provides-Extra: scripts
|
|
30
|
+
Requires-Dist: matplotlib; extra == "scripts"
|
|
31
|
+
Provides-Extra: test
|
|
32
|
+
Requires-Dist: pytest; extra == "test"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# curvatureBoundaries
|
|
36
|
+
|
|
37
|
+
Curvature-aware closed forms for the semi-analytic SPH boundary integral of
|
|
38
|
+
Winchenbach, Akhunov & Kolb (2020),
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
lambda(x) = Int_{D_B(x)} W(|x-x'|, h) dx', W(r,h) = C_n/h^n W_hat(r/h)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
extended beyond the paper's 3-D planar cubic-spline result to:
|
|
45
|
+
|
|
46
|
+
* **2-D planar** boundaries for all four kernels — new arccos/ln closed forms
|
|
47
|
+
(`maple/01_planar.mpl`, §3 of `docs/derivation.md`);
|
|
48
|
+
* **3-D planar** Wendland kernels w2/w4/w6 — single polynomials of degree 2n
|
|
49
|
+
via the monomial basis `F(n,p,x)`;
|
|
50
|
+
* **curved boundaries**: solid sphere for the Wendland kernels — both
|
|
51
|
+
branches (`2R+d >= 1` and `2R+d < 1`) are rational `N(R,d)/(R+d)`, with
|
|
52
|
+
exact planar limit `R -> infinity`.
|
|
53
|
+
|
|
54
|
+
Kernels and conventions follow warpSPHCore
|
|
55
|
+
(`src/warpSPHCore/kernels/`, `scripts/kernels/kernel_specs.yaml`):
|
|
56
|
+
`q = r/h` with `h` the support radius.
|
|
57
|
+
|
|
58
|
+
## Layout
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
maple/00_setup.mpl kernel table, normalization, F(n,p,x) basis checks
|
|
62
|
+
maple/01_planar.mpl 2-D + 3-D planar, all kernels
|
|
63
|
+
maple/03_sphere.mpl solid sphere (Wendland w2/w4/w6)
|
|
64
|
+
maple/run_all.sh regenerate src/warpSPHBoundaries/data/symbolic/ (needs Maple 2026)
|
|
65
|
+
src/warpSPHBoundaries/curvbound/ PLAN track: kernel table, closed-form evaluators, oracles (curvature-aware planar / sphere integrals)
|
|
66
|
+
src/warpSPHBoundaries/edge/ HANDOFF track: exact 2D edge-reduction integrals (mpmath -> numpy -> torch -> Warp), FEM weights, tiers 3 / 4
|
|
67
|
+
src/warpSPHBoundaries/scene/ bodies and representations, boundary operations, wall operators of the delta+-SPH solver, the boundary provider (`AnalyticBoundary`, `scene/provider.py`)
|
|
68
|
+
src/warpSPHBoundaries/data/tables/ pre-tabulated tier-3 kernel tables (package data)
|
|
69
|
+
src/warpSPHBoundaries/sim/ DFSPH2D and DeltaSPH2D solvers, cases, validation runners
|
|
70
|
+
scripts/ command-line runners (regression harness, profiler, validation, videos), scripts/studies, scripts/bench
|
|
71
|
+
tests/ pytest suite: tests/{edge,scene,sim,curvbound} (940 tests)
|
|
72
|
+
notebooks/demo.ipynb executed demo: geometry, curves, validation
|
|
73
|
+
notebooks/examples/ nine example flows (Taylor-Green ... flow past a cylinder) on the delta+ and the DFSPH solver, against reference solutions
|
|
74
|
+
docs/derivation.md full mathematical write-up
|
|
75
|
+
PLAN.md project status & roadmap
|
|
76
|
+
src/warpSPHBoundaries/data/symbolic/ Maple exports (committed, shipped with the package; consumed by warpSPHBoundaries.curvbound)
|
|
77
|
+
results/figures/ notebook figures (generated, git-ignored)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Package layering
|
|
81
|
+
|
|
82
|
+
`edge` <- `scene` <- `sim`: the edge machinery and the scene layer (`pip install .`, needs `warpSPHCore` only) are the boundary library `warpSPHBoundaries` (renamed from `edgebound` on 2026-10-06); `sim` (the reference
|
|
83
|
+
solvers) additionally needs `warpSPH`. A scheme consumes the analytic bodies through `warpSPHBoundaries.scene.AnalyticBoundary` (the `BoundaryProvider` protocol: integrals at the fluid particles, signed distance,
|
|
84
|
+
body kinematics); see docs/audit-warpsph-boundary-hooks.md. The layering is enforced by `tests/test_package_layering.py`.
|
|
85
|
+
|
|
86
|
+
## Quick start
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# 1. (re)generate the symbolic results — needs Maple 2026
|
|
90
|
+
maple/run_all.sh # MAPLE=/path/to/maple to override
|
|
91
|
+
|
|
92
|
+
# 2. install (editable) and run the validation suite; the solvers need warpSPH / warpSPHCore in the same env
|
|
93
|
+
pip install -e . --no-deps # or: pip install -e .[test]
|
|
94
|
+
python -m pytest tests/ -q -n 4 # ~3 min with pytest-xdist; GPU tests use cuda:0
|
|
95
|
+
|
|
96
|
+
# 3. run the demo notebook (jupyter + matplotlib)
|
|
97
|
+
python -m jupyter nbconvert --to notebook --execute --inplace \
|
|
98
|
+
notebooks/demo.ipynb --ExecutePreprocessor.kernel_name=python3
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Python API sketch:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from fractions import Fraction
|
|
105
|
+
from curvbound import planar2d, planar3d, sphere
|
|
106
|
+
|
|
107
|
+
planar3d("w4", Fraction(1, 3)) # exact rational (polynomial in d)
|
|
108
|
+
planar2d("w4", "0.3") # mpf (arccos/ln closed form)
|
|
109
|
+
sphere("w4", Fraction(4, 10), Fraction(1, 5)) # solid ball R=0.4, d=0.2
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Validation
|
|
113
|
+
|
|
114
|
+
Every closed form is validated three ways (details in `docs/derivation.md`
|
|
115
|
+
§5): exact symbolic identities in Maple, 50-digit Maple quadrature, and
|
|
116
|
+
independent 40-digit mpmath oracles in `src/warpSPHBoundaries/curvbound/oracle.py`
|
|
117
|
+
(including a true 2-D Cartesian quadrature and the h-scaling identity).
|
|
118
|
+
Worst observed error: ~1e-39.
|
|
119
|
+
|
|
120
|
+
## Status
|
|
121
|
+
|
|
122
|
+
3-D planar (cubic + Wendland) and 3-D spherical (Wendland) were completed in
|
|
123
|
+
the original plan; the 2-D derivations, the promotion of the toy harnesses
|
|
124
|
+
to this repository layout, and the demo notebook are done (see `PLAN.md`
|
|
125
|
+
§18–§18c). Next: cylinder boundary, 2-D circle boundary, inside-solid
|
|
126
|
+
(`d < 0`) cases, gradient terms.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# curvatureBoundaries
|
|
2
|
+
|
|
3
|
+
Curvature-aware closed forms for the semi-analytic SPH boundary integral of
|
|
4
|
+
Winchenbach, Akhunov & Kolb (2020),
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
lambda(x) = Int_{D_B(x)} W(|x-x'|, h) dx', W(r,h) = C_n/h^n W_hat(r/h)
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
extended beyond the paper's 3-D planar cubic-spline result to:
|
|
11
|
+
|
|
12
|
+
* **2-D planar** boundaries for all four kernels — new arccos/ln closed forms
|
|
13
|
+
(`maple/01_planar.mpl`, §3 of `docs/derivation.md`);
|
|
14
|
+
* **3-D planar** Wendland kernels w2/w4/w6 — single polynomials of degree 2n
|
|
15
|
+
via the monomial basis `F(n,p,x)`;
|
|
16
|
+
* **curved boundaries**: solid sphere for the Wendland kernels — both
|
|
17
|
+
branches (`2R+d >= 1` and `2R+d < 1`) are rational `N(R,d)/(R+d)`, with
|
|
18
|
+
exact planar limit `R -> infinity`.
|
|
19
|
+
|
|
20
|
+
Kernels and conventions follow warpSPHCore
|
|
21
|
+
(`src/warpSPHCore/kernels/`, `scripts/kernels/kernel_specs.yaml`):
|
|
22
|
+
`q = r/h` with `h` the support radius.
|
|
23
|
+
|
|
24
|
+
## Layout
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
maple/00_setup.mpl kernel table, normalization, F(n,p,x) basis checks
|
|
28
|
+
maple/01_planar.mpl 2-D + 3-D planar, all kernels
|
|
29
|
+
maple/03_sphere.mpl solid sphere (Wendland w2/w4/w6)
|
|
30
|
+
maple/run_all.sh regenerate src/warpSPHBoundaries/data/symbolic/ (needs Maple 2026)
|
|
31
|
+
src/warpSPHBoundaries/curvbound/ PLAN track: kernel table, closed-form evaluators, oracles (curvature-aware planar / sphere integrals)
|
|
32
|
+
src/warpSPHBoundaries/edge/ HANDOFF track: exact 2D edge-reduction integrals (mpmath -> numpy -> torch -> Warp), FEM weights, tiers 3 / 4
|
|
33
|
+
src/warpSPHBoundaries/scene/ bodies and representations, boundary operations, wall operators of the delta+-SPH solver, the boundary provider (`AnalyticBoundary`, `scene/provider.py`)
|
|
34
|
+
src/warpSPHBoundaries/data/tables/ pre-tabulated tier-3 kernel tables (package data)
|
|
35
|
+
src/warpSPHBoundaries/sim/ DFSPH2D and DeltaSPH2D solvers, cases, validation runners
|
|
36
|
+
scripts/ command-line runners (regression harness, profiler, validation, videos), scripts/studies, scripts/bench
|
|
37
|
+
tests/ pytest suite: tests/{edge,scene,sim,curvbound} (940 tests)
|
|
38
|
+
notebooks/demo.ipynb executed demo: geometry, curves, validation
|
|
39
|
+
notebooks/examples/ nine example flows (Taylor-Green ... flow past a cylinder) on the delta+ and the DFSPH solver, against reference solutions
|
|
40
|
+
docs/derivation.md full mathematical write-up
|
|
41
|
+
PLAN.md project status & roadmap
|
|
42
|
+
src/warpSPHBoundaries/data/symbolic/ Maple exports (committed, shipped with the package; consumed by warpSPHBoundaries.curvbound)
|
|
43
|
+
results/figures/ notebook figures (generated, git-ignored)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Package layering
|
|
47
|
+
|
|
48
|
+
`edge` <- `scene` <- `sim`: the edge machinery and the scene layer (`pip install .`, needs `warpSPHCore` only) are the boundary library `warpSPHBoundaries` (renamed from `edgebound` on 2026-10-06); `sim` (the reference
|
|
49
|
+
solvers) additionally needs `warpSPH`. A scheme consumes the analytic bodies through `warpSPHBoundaries.scene.AnalyticBoundary` (the `BoundaryProvider` protocol: integrals at the fluid particles, signed distance,
|
|
50
|
+
body kinematics); see docs/audit-warpsph-boundary-hooks.md. The layering is enforced by `tests/test_package_layering.py`.
|
|
51
|
+
|
|
52
|
+
## Quick start
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# 1. (re)generate the symbolic results — needs Maple 2026
|
|
56
|
+
maple/run_all.sh # MAPLE=/path/to/maple to override
|
|
57
|
+
|
|
58
|
+
# 2. install (editable) and run the validation suite; the solvers need warpSPH / warpSPHCore in the same env
|
|
59
|
+
pip install -e . --no-deps # or: pip install -e .[test]
|
|
60
|
+
python -m pytest tests/ -q -n 4 # ~3 min with pytest-xdist; GPU tests use cuda:0
|
|
61
|
+
|
|
62
|
+
# 3. run the demo notebook (jupyter + matplotlib)
|
|
63
|
+
python -m jupyter nbconvert --to notebook --execute --inplace \
|
|
64
|
+
notebooks/demo.ipynb --ExecutePreprocessor.kernel_name=python3
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Python API sketch:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from fractions import Fraction
|
|
71
|
+
from curvbound import planar2d, planar3d, sphere
|
|
72
|
+
|
|
73
|
+
planar3d("w4", Fraction(1, 3)) # exact rational (polynomial in d)
|
|
74
|
+
planar2d("w4", "0.3") # mpf (arccos/ln closed form)
|
|
75
|
+
sphere("w4", Fraction(4, 10), Fraction(1, 5)) # solid ball R=0.4, d=0.2
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Validation
|
|
79
|
+
|
|
80
|
+
Every closed form is validated three ways (details in `docs/derivation.md`
|
|
81
|
+
§5): exact symbolic identities in Maple, 50-digit Maple quadrature, and
|
|
82
|
+
independent 40-digit mpmath oracles in `src/warpSPHBoundaries/curvbound/oracle.py`
|
|
83
|
+
(including a true 2-D Cartesian quadrature and the h-scaling identity).
|
|
84
|
+
Worst observed error: ~1e-39.
|
|
85
|
+
|
|
86
|
+
## Status
|
|
87
|
+
|
|
88
|
+
3-D planar (cubic + Wendland) and 3-D spherical (Wendland) were completed in
|
|
89
|
+
the original plan; the 2-D derivations, the promotion of the toy harnesses
|
|
90
|
+
to this repository layout, and the demo notebook are done (see `PLAN.md`
|
|
91
|
+
§18–§18c). Next: cylinder boundary, 2-D circle boundary, inside-solid
|
|
92
|
+
(`d < 0`) cases, gradient terms.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "warpSPHBoundaries"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Exact SPH boundary integrals via divergence-theorem edge reductions (2D), scene layer and delta+-SPH / DFSPH solvers on analytic boundaries"
|
|
9
|
+
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
keywords = ["sph", "smoothed-particle-hydrodynamics", "boundary-conditions", "warp", "pytorch", "analytic-boundaries"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"License :: OSI Approved :: MIT License",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.10",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
"Topic :: Scientific/Engineering",
|
|
21
|
+
"Topic :: Scientific/Engineering :: Physics",
|
|
22
|
+
]
|
|
23
|
+
dependencies = ["numpy", "scipy", "mpmath", "torch", "warp-lang", "warpSPHCore>=0.6.0"]
|
|
24
|
+
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
# the solvers (`sim/`: delta+-SPH and DFSPH on analytic boundaries) run on warpSPH; warpSPH in turn uses this package for `--wallRepresentation analytic` (its `boundaries` extra)
|
|
27
|
+
solvers = ["warpSPH>=0.6.0", "warpSPHCore>=0.6.0"]
|
|
28
|
+
scripts = ["matplotlib"]
|
|
29
|
+
test = ["pytest"]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Repository = "https://github.com/wi-re/warpSPHBoundaries"
|
|
33
|
+
Issues = "https://github.com/wi-re/warpSPHBoundaries/issues"
|
|
34
|
+
|
|
35
|
+
[tool.setuptools.packages.find]
|
|
36
|
+
where = ["src"]
|
|
37
|
+
include = ["warpSPHBoundaries*"]
|
|
38
|
+
|
|
39
|
+
[tool.setuptools.package-data]
|
|
40
|
+
warpSPHBoundaries = ["data/tables/*.npz", "data/symbolic/*.txt"]
|
|
41
|
+
|
|
42
|
+
[tool.pytest.ini_options]
|
|
43
|
+
testpaths = ["tests"]
|
|
44
|
+
markers = ["slow: builds / installs / long runs; deselect with -m \"not slow\""]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""warpSPHBoundaries -- exact 2D SPH boundary integrals via divergence-theorem edge reductions.
|
|
2
|
+
|
|
3
|
+
Sub-packages: `curvbound` (closed forms and quadrature oracles of the PLAN track), `edge` (the edge machinery), `scene` (bodies, boundary operations, wall operators), `sim` (DFSPH / delta+-SPH solvers); `paths`.
|
|
4
|
+
Derivations: docs/derivations/*.md. The 2D edge reductions (`edge`) and the PLAN-track closed forms (`curvbound`) are separate tracks that share the kernels.
|
|
5
|
+
"""
|
|
6
|
+
__version__ = "0.1.0" # keep equal to pyproject.toml (scripts/publish_pypi.sh checks it)
|
|
7
|
+
|
|
8
|
+
from .edge.precision import ensure_default as _ensure_default # warpSPHCore precision: warpSPHBoundaries defaults to float64 unless configured (edge/precision.py); before any warpSPHCore import
|
|
9
|
+
_ensure_default()
|
|
10
|
+
|
|
11
|
+
from .edge.core import gradient, moment, moment_gradient, value
|
|
12
|
+
from .edge.geometry import prepare
|
|
13
|
+
from .edge.kernels import KERNELS, disk_moment, kernel
|
|
14
|
+
from .edge.oracle import polar_disk_gradient, polar_disk_moment, polar_disk_value, polar_gradient, polar_moment, polar_value
|
|
15
|
+
|
|
16
|
+
__all__ = ["value", "gradient", "moment", "moment_gradient", "prepare",
|
|
17
|
+
"KERNELS", "kernel", "disk_moment",
|
|
18
|
+
"polar_value", "polar_gradient", "polar_moment", "polar_disk_value", "polar_disk_moment", "polar_disk_gradient"]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""warpSPHBoundaries.curvbound — curvature-aware SPH boundary integrals (the PLAN track; formerly the top-level package `curvbound`).
|
|
2
|
+
|
|
3
|
+
Closed forms (Maple-generated, data/symbolic/) plus independent
|
|
4
|
+
high-precision quadrature oracles, for the semi-analytic SPH boundary
|
|
5
|
+
handling of Winchenbach, Akhunov, Kolb (2020) extended to Wendland kernels
|
|
6
|
+
and to curved (solid-sphere) boundaries.
|
|
7
|
+
"""
|
|
8
|
+
from .kernels import KERNELS, Kernel, kernel
|
|
9
|
+
from .oracle import (
|
|
10
|
+
physical_planar3d,
|
|
11
|
+
quad_planar2d,
|
|
12
|
+
quad_planar2d_cartesian,
|
|
13
|
+
quad_planar3d,
|
|
14
|
+
quad_sphere,
|
|
15
|
+
)
|
|
16
|
+
from .symbolic import PLANAR_EXPORT, SPHERE_EXPORT, planar2d, planar3d, sphere
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"KERNELS", "Kernel", "kernel",
|
|
20
|
+
"PLANAR_EXPORT", "SPHERE_EXPORT",
|
|
21
|
+
"planar2d", "planar3d", "sphere",
|
|
22
|
+
"quad_planar2d", "quad_planar2d_cartesian", "quad_planar3d",
|
|
23
|
+
"quad_sphere", "physical_planar3d",
|
|
24
|
+
]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Kernel table for the boundary-integral derivations.
|
|
2
|
+
|
|
3
|
+
Conventions match warpSPHCore (src/warpSPHCore/kernels/kernel.py and
|
|
4
|
+
scripts/kernels/kernel_specs.yaml):
|
|
5
|
+
|
|
6
|
+
W(r, h) = C_n / h^n * W_hat(q), q = r / h, h = support radius,
|
|
7
|
+
|
|
8
|
+
with W_hat compactly supported on [0, 1]. The kernel shapes are identical
|
|
9
|
+
in 2D and 3D; only C_n differs (transcribed from kernel_specs.yaml):
|
|
10
|
+
|
|
11
|
+
cubic : W_hat = (1-q)^3 - 4*(1/2-q)_+^3 C2 = 80/(7*pi) C3 = 16/pi
|
|
12
|
+
w2 : (1-q)^4 (1+4q) C2 = 7/pi C3 = 21/(2*pi)
|
|
13
|
+
w4 : (1-q)^6 (1+6q+35/3*q^2) C2 = 9/pi C3 = 495/(32*pi)
|
|
14
|
+
w6 : (1-q)^8 (1+8q+25q^2+32q^3) C2 = 78/(7*pi) C3 = 1365/(64*pi)
|
|
15
|
+
"""
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from fractions import Fraction
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class Kernel:
|
|
22
|
+
name: str
|
|
23
|
+
kind: str # "cubic" (knot at q=1/2) or "wendland" (single poly)
|
|
24
|
+
c2_pi: Fraction # C2 = c2_pi / pi
|
|
25
|
+
c3_pi: Fraction # C3 = c3_pi / pi
|
|
26
|
+
n: int = 0 # Wendland: power of (1-q)
|
|
27
|
+
poly: tuple = () # Wendland: ((power, coeff), ...) of P(q), c in Q
|
|
28
|
+
|
|
29
|
+
def w_hat(self, q: Fraction) -> Fraction:
|
|
30
|
+
"""Exact kernel shape at a rational q (symbolic checks)."""
|
|
31
|
+
if self.kind == "cubic":
|
|
32
|
+
if q <= Fraction(1, 2):
|
|
33
|
+
return (1 - q) ** 3 - 4 * (Fraction(1, 2) - q) ** 3
|
|
34
|
+
return (1 - q) ** 3
|
|
35
|
+
v = Fraction(0)
|
|
36
|
+
for p, c in self.poly:
|
|
37
|
+
v += c * q ** p
|
|
38
|
+
return (1 - q) ** self.n * v
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
KERNELS = {
|
|
42
|
+
"cubic": Kernel("cubic", "cubic", Fraction(80, 7), Fraction(16)),
|
|
43
|
+
"w2": Kernel("w2", "wendland", Fraction(7), Fraction(21, 2), 4,
|
|
44
|
+
((0, Fraction(1)), (1, Fraction(4)))),
|
|
45
|
+
"w4": Kernel("w4", "wendland", Fraction(9), Fraction(495, 32), 6,
|
|
46
|
+
((0, Fraction(1)), (1, Fraction(6)), (2, Fraction(35, 3)))),
|
|
47
|
+
"w6": Kernel("w6", "wendland", Fraction(78, 7), Fraction(1365, 64), 8,
|
|
48
|
+
((0, Fraction(1)), (1, Fraction(8)),
|
|
49
|
+
(2, Fraction(25)), (3, Fraction(32)))),
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def kernel(name: str) -> Kernel:
|
|
54
|
+
try:
|
|
55
|
+
return KERNELS[name]
|
|
56
|
+
except KeyError:
|
|
57
|
+
raise KeyError(f"unknown kernel {name!r}; expected one of {sorted(KERNELS)}")
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""Independent high-precision quadrature oracles.
|
|
2
|
+
|
|
3
|
+
These re-derive lambda directly from the defining integrals (no shared code
|
|
4
|
+
with symbolic.py) so they can validate the exported closed forms:
|
|
5
|
+
|
|
6
|
+
3-D planar : lambda = Int_d^1 C3 W(q) 2 pi q (q-d) dq
|
|
7
|
+
2-D planar : lambda = Int_d^1 C2 W(q) 2 q arccos(d/q) dq
|
|
8
|
+
sphere : lambda = Int_d^{qmax} C3 W(q) pi q/D (R^2-D^2+2Dq-q^2) dq,
|
|
9
|
+
D = R+d, qmax = min(1, 2R+d)
|
|
10
|
+
2-D Cartesian: true double integral over the support disk cut by the
|
|
11
|
+
chord (no 1-D reduction) -- the most independent check.
|
|
12
|
+
|
|
13
|
+
Unit support h=1, particle outside the boundary, d in [0,1].
|
|
14
|
+
"""
|
|
15
|
+
from fractions import Fraction
|
|
16
|
+
|
|
17
|
+
import mpmath as mp
|
|
18
|
+
|
|
19
|
+
from .kernels import kernel
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _to_mpf(x) -> mp.mpf:
|
|
23
|
+
"""int / Fraction / str / mpf -> mp.mpf (this mpmath build rejects Fraction)."""
|
|
24
|
+
if isinstance(x, Fraction):
|
|
25
|
+
return mp.mpf(x.numerator) / mp.mpf(x.denominator)
|
|
26
|
+
return mp.mpf(x)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _c2(kd) -> mp.mpf:
|
|
30
|
+
return (mp.mpf(kd.c2_pi.numerator) / mp.mpf(kd.c2_pi.denominator)) / mp.pi
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _c3(kd) -> mp.mpf:
|
|
34
|
+
return (mp.mpf(kd.c3_pi.numerator) / mp.mpf(kd.c3_pi.denominator)) / mp.pi
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _w_hat_mp(kd, q: mp.mpf) -> mp.mpf:
|
|
38
|
+
if kd.kind == "cubic":
|
|
39
|
+
if q <= mp.mpf(1) / 2:
|
|
40
|
+
return (1 - q) ** 3 - 4 * (mp.mpf(1) / 2 - q) ** 3
|
|
41
|
+
return (1 - q) ** 3
|
|
42
|
+
v = mp.mpf(0)
|
|
43
|
+
for p, c in kd.poly:
|
|
44
|
+
v += (mp.mpf(c.numerator) / mp.mpf(c.denominator)) * q ** p
|
|
45
|
+
return (1 - q) ** kd.n * v
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def quad_planar3d(kernel_name: str, d, dps: int = 40) -> mp.mpf:
|
|
49
|
+
kd = kernel(kernel_name)
|
|
50
|
+
c3 = _c3(kd)
|
|
51
|
+
d = _to_mpf(d)
|
|
52
|
+
f = lambda q: c3 * _w_hat_mp(kd, q) * 2 * mp.pi * q * (q - d)
|
|
53
|
+
if kd.kind == "cubic" and d < mp.mpf(1) / 2:
|
|
54
|
+
return mp.quad(f, [d, mp.mpf(1) / 2, 1])
|
|
55
|
+
return mp.quad(f, [d, 1])
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def quad_planar2d(kernel_name: str, d, dps: int = 40) -> mp.mpf:
|
|
59
|
+
kd = kernel(kernel_name)
|
|
60
|
+
c2 = _c2(kd)
|
|
61
|
+
d = _to_mpf(d)
|
|
62
|
+
f = lambda q: c2 * _w_hat_mp(kd, q) * 2 * q * mp.acos(d / q)
|
|
63
|
+
if kd.kind == "cubic" and d < mp.mpf(1) / 2:
|
|
64
|
+
return mp.quad(f, [d, mp.mpf(1) / 2, 1])
|
|
65
|
+
return mp.quad(f, [d, 1])
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def quad_sphere(kernel_name: str, R, d, dps: int = 40) -> mp.mpf:
|
|
69
|
+
kd = kernel(kernel_name)
|
|
70
|
+
c3 = _c3(kd)
|
|
71
|
+
R = _to_mpf(R)
|
|
72
|
+
d = _to_mpf(d)
|
|
73
|
+
D = R + d
|
|
74
|
+
qmax = min(mp.mpf(1), 2 * R + d)
|
|
75
|
+
f = (lambda q: c3 * _w_hat_mp(kd, q)
|
|
76
|
+
* mp.pi * q / D * (R ** 2 - D ** 2 + 2 * D * q - q ** 2))
|
|
77
|
+
return mp.quad(f, [d, qmax])
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def quad_planar2d_cartesian(kernel_name: str, d, dps: int = 30) -> mp.mpf:
|
|
81
|
+
"""True 2-D Cartesian quadrature (no 1-D shell reduction).
|
|
82
|
+
|
|
83
|
+
Particle at the origin, boundary line y = d, boundary side y > d:
|
|
84
|
+
lambda = Int_{x} Int_{y=d}^{sqrt(1-x^2)} C2 W_hat(sqrt(x^2+y^2)) dy dx
|
|
85
|
+
over |x| <= sqrt(1-d^2). For the cubic spline the knot circle r = 1/2 is
|
|
86
|
+
added as a breakpoint wherever it crosses the inner interval.
|
|
87
|
+
"""
|
|
88
|
+
kd = kernel(kernel_name)
|
|
89
|
+
c2 = _c2(kd)
|
|
90
|
+
d = _to_mpf(d)
|
|
91
|
+
xmax = mp.sqrt(1 - d ** 2)
|
|
92
|
+
|
|
93
|
+
def inner(x: mp.mpf) -> mp.mpf:
|
|
94
|
+
ytop = mp.sqrt(1 - x ** 2)
|
|
95
|
+
pts = [d]
|
|
96
|
+
if kd.kind == "cubic" and x ** 2 < mp.mpf(1) / 4:
|
|
97
|
+
yk = mp.sqrt(mp.mpf(1) / 4 - x ** 2)
|
|
98
|
+
if d < yk < ytop:
|
|
99
|
+
pts.append(yk)
|
|
100
|
+
pts.append(ytop)
|
|
101
|
+
g = lambda y: c2 * _w_hat_mp(kd, mp.sqrt(x ** 2 + y ** 2))
|
|
102
|
+
return mp.quad(g, pts)
|
|
103
|
+
|
|
104
|
+
# dense outer breakpoints: inner(x) ~ sqrt(xmax - |x|) at the endpoints
|
|
105
|
+
# (the chord degenerates), which a 3-way split resolves only to ~3e-15
|
|
106
|
+
# for the cubic spline kernel (observed)
|
|
107
|
+
n = 16
|
|
108
|
+
return mp.quad(inner, [-xmax + 2 * xmax * mp.mpf(i) / n for i in range(n + 1)])
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def physical_planar3d(kernel_name: str, d, h, dps: int = 30) -> mp.mpf:
|
|
112
|
+
"""The PHYSICAL 3-D integral with explicit support radius h:
|
|
113
|
+
|
|
114
|
+
Int_{r=d}^{h} (C3/h^3) W_hat(r/h) 2 pi r (r-d) dr
|
|
115
|
+
|
|
116
|
+
which by the substitution r = h q must equal lambda_3(d/h). Used for the
|
|
117
|
+
h-scaling test.
|
|
118
|
+
"""
|
|
119
|
+
kd = kernel(kernel_name)
|
|
120
|
+
c3 = _c3(kd)
|
|
121
|
+
d = _to_mpf(d)
|
|
122
|
+
h = _to_mpf(h)
|
|
123
|
+
f = (lambda r: (c3 / h ** 3) * _w_hat_mp(kd, r / h)
|
|
124
|
+
* 2 * mp.pi * r * (r - d))
|
|
125
|
+
if kd.kind == "cubic" and d < h / 2:
|
|
126
|
+
return mp.quad(f, [d, h / 2, h])
|
|
127
|
+
return mp.quad(f, [d, h])
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
__all__ = [
|
|
131
|
+
"quad_planar3d", "quad_planar2d", "quad_sphere",
|
|
132
|
+
"quad_planar2d_cartesian", "physical_planar3d",
|
|
133
|
+
]
|