beta-parameterization 2.3.5__tar.gz → 4.0.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.
- beta_parameterization-4.0.0/CHANGELOG.md +332 -0
- {beta_parameterization-2.3.5 → beta_parameterization-4.0.0}/CMakeLists.txt +39 -11
- {beta_parameterization-2.3.5 → beta_parameterization-4.0.0}/PKG-INFO +1 -1
- beta_parameterization-4.0.0/README.md +191 -0
- beta_parameterization-4.0.0/include/beta_parameterization.h +243 -0
- beta_parameterization-4.0.0/include/beta_parameterization.hpp +348 -0
- beta_parameterization-4.0.0/python/beta_parameterization/__init__.py +22 -0
- beta_parameterization-4.0.0/python/beta_parameterization/_cdefs.py +89 -0
- beta_parameterization-4.0.0/python/beta_parameterization/api.py +458 -0
- beta_parameterization-4.0.0/python/tests/test_api.py +318 -0
- beta_parameterization-4.0.0/src/beta_parameterization_c_api_mod.f08 +543 -0
- beta_parameterization-4.0.0/src/beta_parameterization_mod.f08 +996 -0
- {beta_parameterization-2.3.5 → beta_parameterization-4.0.0}/src/beta_parameterization_workers_mod.f08 +90 -42
- beta_parameterization-4.0.0/tests/beta_param_boundary_test.f08 +256 -0
- beta_parameterization-4.0.0/tests/beta_param_equivalence_test.f08 +385 -0
- beta_parameterization-4.0.0/tests/beta_param_golden_test.f08 +152 -0
- beta_parameterization-4.0.0/tests/beta_param_lifecycle_test.f08 +115 -0
- beta_parameterization-4.0.0/tests/beta_param_outputs_test.f08 +187 -0
- beta_parameterization-4.0.0/tests/beta_param_property_test.f08 +157 -0
- beta_parameterization-4.0.0/tests/beta_param_resolve_test.f08 +81 -0
- beta_parameterization-4.0.0/tests/beta_param_standalone_test.f08 +124 -0
- beta_parameterization-4.0.0/tests/beta_param_statelessness_test.f08 +304 -0
- beta_parameterization-4.0.0/tests/beta_param_status_test.f08 +44 -0
- beta_parameterization-4.0.0/tests/beta_pes_sweep.f08 +231 -0
- beta_parameterization-4.0.0/tests/c_api_smoke_test.cpp +309 -0
- beta_parameterization-4.0.0/tests/golden_capture.f08 +112 -0
- {beta_parameterization-2.3.5 → beta_parameterization-4.0.0}/tests/test_utils_mod.f08 +34 -1
- beta_parameterization-4.0.0/tests/thread_stress_test.cpp +242 -0
- beta_parameterization-2.3.5/include/beta_parameterization.h +0 -207
- beta_parameterization-2.3.5/include/beta_parameterization.hpp +0 -292
- beta_parameterization-2.3.5/python/beta_parameterization/__init__.py +0 -23
- beta_parameterization-2.3.5/python/beta_parameterization/_cdefs.py +0 -59
- beta_parameterization-2.3.5/python/beta_parameterization/api.py +0 -297
- beta_parameterization-2.3.5/python/tests/test_api.py +0 -185
- beta_parameterization-2.3.5/src/beta_parameterization_c_api_mod.f08 +0 -425
- beta_parameterization-2.3.5/src/beta_parameterization_mod.f08 +0 -828
- beta_parameterization-2.3.5/tests/beta_param_breakdown_test.f08 +0 -126
- beta_parameterization-2.3.5/tests/beta_param_com_test.f08 +0 -97
- beta_parameterization-2.3.5/tests/beta_param_core_test.f08 +0 -105
- beta_parameterization-2.3.5/tests/beta_param_error_test.f08 +0 -154
- beta_parameterization-2.3.5/tests/beta_param_golden_test.f08 +0 -165
- beta_parameterization-2.3.5/tests/beta_param_node_set_test.f08 +0 -335
- beta_parameterization-2.3.5/tests/c_api_smoke_test.cpp +0 -214
- beta_parameterization-2.3.5/tests/golden_capture.f08 +0 -88
- beta_parameterization-2.3.5/tests/thread_stress_test.cpp +0 -60
- {beta_parameterization-2.3.5 → beta_parameterization-4.0.0}/.gitignore +0 -0
- {beta_parameterization-2.3.5 → beta_parameterization-4.0.0}/LICENSE +0 -0
- {beta_parameterization-2.3.5 → beta_parameterization-4.0.0}/ci/build-wheel.sh +0 -0
- {beta_parameterization-2.3.5 → beta_parameterization-4.0.0}/pyproject.toml +0 -0
- {beta_parameterization-2.3.5 → beta_parameterization-4.0.0}/python/beta_parameterization/_libloader.py +0 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. Versions follow
|
|
4
|
+
semantic versioning; the format follows [Keep a Changelog](https://keepachangelog.com).
|
|
5
|
+
|
|
6
|
+
## 4.0.0
|
|
7
|
+
|
|
8
|
+
Adoption of the two-tier shape parameterization contract on
|
|
9
|
+
`shape_core_mod` (fortran-foundations 3.0.0). The incremental tier is gone:
|
|
10
|
+
one-shot functions plus one read-only cache, built once and shared across
|
|
11
|
+
threads. Every public surface — Fortran, C, C++, Python — changes; nothing in
|
|
12
|
+
3.x compiles against 4.0.0 unchanged.
|
|
13
|
+
|
|
14
|
+
### Changed (breaking)
|
|
15
|
+
|
|
16
|
+
- **One cache type.** `tables_t` is renamed `cache_t`; the mutable 3.x
|
|
17
|
+
`cache_t`, `cache_init_shared_s`, the shared-tables mode and the `target`
|
|
18
|
+
requirement are removed. `cache_init_s(cache, max_params, thetas, status)`.
|
|
19
|
+
C: `beta_param_cache_create(max_params, thetas, n_thetas, status)`; the
|
|
20
|
+
`tables` handle and `beta_param_cache_create_shared` are removed. C++:
|
|
21
|
+
`Tables` is removed, `Cache(max_params, thetas)`. Python:
|
|
22
|
+
`Cache(max_params, thetas)`.
|
|
23
|
+
- **Caches are immutable and shareable.** Every compute takes the cache
|
|
24
|
+
read-only (`intent(in)`, `const` handle, `const` method) and is `pure` in
|
|
25
|
+
Fortran. Any number of threads may compute on one cache. The 3.0.0
|
|
26
|
+
thread-confinement rule is withdrawn.
|
|
27
|
+
- **Options are per call.** `conserve_volume` and `apply_com` move from cache
|
|
28
|
+
creation to each compute, after `params` and before the outputs. One cache
|
|
29
|
+
serves every combination. `cache_radius_grid_unchecked_s` takes `apply_com`
|
|
30
|
+
only: it never volume-scales.
|
|
31
|
+
- **`max_params` replaces `n_params`.** A cache accepts any vector of
|
|
32
|
+
`1 .. max_params` entries, up to 64 (the 8-parameter cap is gone). Missing
|
|
33
|
+
trailing parameters are zero.
|
|
34
|
+
- **Node sets are built from a cache**:
|
|
35
|
+
`node_set_build_s(node_set, cache, thetas, status)`; argument order of the
|
|
36
|
+
node compute is `(cache, params, node_set, conserve_volume, apply_com, radii,
|
|
37
|
+
dr_dthetas, status)`. A node set serves a cache when it was built from a
|
|
38
|
+
cache with at least that cache's `max_params`
|
|
39
|
+
(`BETA_PARAM_ERROR_NODE_SET_MISMATCH` otherwise) — judged on the two
|
|
40
|
+
objects, no longer on the vector length.
|
|
41
|
+
- **Status codes.** Code 6 (`SHAPE_ERROR_TABLES_NOT_INITIALIZED`) is retired
|
|
42
|
+
and never reused; an uninitialized cache passed to `node_set_build_s` now
|
|
43
|
+
returns 2. An empty one-shot vector returns 4 (was 5). `max_params > 64` at
|
|
44
|
+
init returns 1. C: a negative `n_thetas` returns 3 (was 5).
|
|
45
|
+
- **Getters.** `cache_n_params_f` becomes `cache_max_params_f`; C++
|
|
46
|
+
`Cache::n_params()` becomes `max_params()`; Python `Cache.n_params` becomes
|
|
47
|
+
`max_params`, and the `conserve_volume` / `apply_com` attributes are gone.
|
|
48
|
+
|
|
49
|
+
### Removed
|
|
50
|
+
|
|
51
|
+
- The recompute engine and everything built on it: `cache_recompute_count_f`,
|
|
52
|
+
the `BETA_PARAM_I_*` indices, the dependency map, the minimality test suite.
|
|
53
|
+
- `SHAPE_CACHE_MAX_PARAMS`, `SHAPE_STANDALONE_MAX_PARAMS`
|
|
54
|
+
(use `SHAPE_MAX_PARAMS`), `BETA_PARAM_CACHE_MAX_PARAMS`, Python
|
|
55
|
+
`CACHE_MAX_PARAMS`, `Status.tables_not_initialized`.
|
|
56
|
+
|
|
57
|
+
### Added
|
|
58
|
+
|
|
59
|
+
- **Trailing zeros are trimmed.** Both tiers reduce a vector to its last
|
|
60
|
+
nonzero entry before any arithmetic, so a short vector and its zero-padded
|
|
61
|
+
form return identical bits. Interior zeros are kept.
|
|
62
|
+
- **README.md** with the shape definition, both tiers in Fortran, C and
|
|
63
|
+
Python, build instructions and version pins.
|
|
64
|
+
- **CI on push and pull request** (`.github/workflows/tests.yml`): every suite
|
|
65
|
+
in Debug and Release, pytest against the built library, and the
|
|
66
|
+
manylinux2014 wheel build.
|
|
67
|
+
- Test suites for the contract's families: `equivalence` (one-shot ≡ cached,
|
|
68
|
+
short ≡ zero-padded), `statelessness`, `boundary`, and a concurrency suite
|
|
69
|
+
running eight threads on one shared cache.
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
|
|
73
|
+
- **COM correction on a non-positive volume integral.** With `apply_com`, a
|
|
74
|
+
shape far outside the valid domain (for example `[0, -20]`) made the Newton
|
|
75
|
+
step take the cube root of a negative number: a floating-point trap in Debug
|
|
76
|
+
builds, and in Release a result that depended on a NaN comparison. The
|
|
77
|
+
iteration now reports non-convergence (103). Without `apply_com` such a
|
|
78
|
+
shape is still rejected by the validity gate (100).
|
|
79
|
+
- **C API stack overflow on a large wrong size.** Marshalling buffers were
|
|
80
|
+
caller-sized automatic arrays; under Release a large wrong size argument
|
|
81
|
+
overflowed the stack before the size was checked. They are heap-allocated
|
|
82
|
+
now: the call returns `BETA_PARAM_ERROR_INVALID_BUFFER_SIZE`.
|
|
83
|
+
- **Allocation failure in cache or node-set build** returns
|
|
84
|
+
`SHAPE_ERROR_INVALID_GRID` (3) instead of terminating the process.
|
|
85
|
+
|
|
86
|
+
### Build
|
|
87
|
+
|
|
88
|
+
- The library's own objects are compiled with `-fno-lto`. Under
|
|
89
|
+
`-flto -ffast-math` the summation kernels were inlined into each caller and
|
|
90
|
+
optimized per call site, so the same call could return different last bits
|
|
91
|
+
from different call sites. Without LTO every caller executes one machine-code
|
|
92
|
+
body per kernel, which is what makes the bitwise guarantees hold. Cost: about
|
|
93
|
+
30% on shape evaluation time. Consumers keep LTO for their own code.
|
|
94
|
+
- fortran-foundations pin 2.4.0 → 3.0.0.
|
|
95
|
+
|
|
96
|
+
## 3.0.0
|
|
97
|
+
|
|
98
|
+
Adoption of the three-tier shape parameterization contract against
|
|
99
|
+
`shape_core_mod` (fortran-foundations 2.4.0). Every public surface — Fortran,
|
|
100
|
+
C, C++, Python — changes. Read the breaking-change list before upgrading;
|
|
101
|
+
nothing in 2.x compiles against 3.0.0 unchanged.
|
|
102
|
+
|
|
103
|
+
### BREAKING: thread-safety promise withdrawn
|
|
104
|
+
|
|
105
|
+
**The 2.x guarantee that one cache serves concurrent computes from many threads
|
|
106
|
+
is gone.** It was never true once the cache started tracking recompute state.
|
|
107
|
+
|
|
108
|
+
| object | 3.0.0 rule |
|
|
109
|
+
|---|---|
|
|
110
|
+
| `tables_t` / `beta_param_tables_t*` / `Tables` | immutable after creation; share across threads for concurrent reads |
|
|
111
|
+
| `node_set_t` / `beta_param_node_set_t*` / `NodeSet` | immutable after build; share across threads for concurrent reads |
|
|
112
|
+
| `cache_t` / `beta_param_cache_t*` / `Cache` / Python `Cache` | **THREAD-CONFINED**; every compute mutates it; concurrent use from more than one thread is undefined |
|
|
113
|
+
|
|
114
|
+
The supported pattern is one shared `tables_t` plus one cache per thread, built
|
|
115
|
+
with `cache_init_shared_s` / `beta_param_cache_create_shared()` /
|
|
116
|
+
`Tables`-taking `Cache` constructor. Callers that fanned out one 2.x cache
|
|
117
|
+
across an OpenMP region **must** hoist a per-thread cache; the old code will
|
|
118
|
+
race silently. C++ `Cache` compute methods are no longer `const` — the compiler
|
|
119
|
+
catches most of these at the call site.
|
|
120
|
+
|
|
121
|
+
**Fortran callers of `cache_init_shared_s` MUST declare their `tables_t` with
|
|
122
|
+
the `target` attribute.** The cache stores a pointer to it, and a pointer
|
|
123
|
+
associated with a non-target dummy becomes undefined when that procedure
|
|
124
|
+
returns (F2018 8.5.17). Without `target` the code compiles, links and usually
|
|
125
|
+
appears to work — then reads freed memory. Declare
|
|
126
|
+
`type(tables_t), target :: tables`.
|
|
127
|
+
|
|
128
|
+
### Added
|
|
129
|
+
|
|
130
|
+
- **In-library volume conservation.** `conserve_volume` is a cache/standalone
|
|
131
|
+
creation flag. The factor `c = (2 / Σᵢ wᵢ Rᵢ³)^(1/3)` is computed on the
|
|
132
|
+
internal GL-512 set over the COM-corrected, unscaled shape, after validation,
|
|
133
|
+
and applied to radii, dR/dθ and the polar radii — never to β-space values.
|
|
134
|
+
Consumers that rescaled shapes themselves (wmmm's
|
|
135
|
+
`compute_volume_factor_from_dense_f`) should drop their copy.
|
|
136
|
+
- `apply_com` as a creation flag, replacing the separate
|
|
137
|
+
`*_with_com_shift` entry points.
|
|
138
|
+
- `tables_t` — a shared, immutable tables level (normalization constants,
|
|
139
|
+
Gauss-Legendre set, Legendre tables at the primary thetas) with
|
|
140
|
+
`tables_init_s` / `tables_free_s` / `tables_max_l_f` / `tables_n_thetas_f`.
|
|
141
|
+
- `status_message_f(status)` (C: `beta_param_status_message`) — one pure lookup
|
|
142
|
+
returning a fixed string per code.
|
|
143
|
+
- `cache_radius_grid_unchecked_s` — the rendering/diagnostic path: no validity
|
|
144
|
+
gate, no volume scaling, usage errors only, so a rejected shape still yields
|
|
145
|
+
its outline. Its radii are UNSCALED even on a `conserve_volume` cache; mixing
|
|
146
|
+
it with the checked path draws two outlines of different size for one shape.
|
|
147
|
+
- `cache_recompute_count_f` plus the public intermediate indices
|
|
148
|
+
`BETA_PARAM_I_RESOLVED`, `_I_MIN_RADIUS`, `_I_VOLUME`, `_I_RADII`, `_I_DERIV`
|
|
149
|
+
— always-on recompute counters for cache-minimality testing.
|
|
150
|
+
- `BETA_PARAM_ERROR_NODE_SET_MISMATCH` (106) for an unbuilt node set or one
|
|
151
|
+
whose `max_l` is below the cache's `n_params`.
|
|
152
|
+
- Public constants for the two tier caps, so callers can check before creating
|
|
153
|
+
anything: `SHAPE_CACHE_MAX_PARAMS` (8) and `SHAPE_STANDALONE_MAX_PARAMS` (64)
|
|
154
|
+
re-exported from Fortran, `BETA_PARAM_CACHE_MAX_PARAMS` (8) and
|
|
155
|
+
`BETA_PARAM_MAX_PARAMS_LIMIT` (64) in the C header,
|
|
156
|
+
`beta_param::cache_max_params` / `beta_param::max_params_limit` in the C++
|
|
157
|
+
header, and `CACHE_MAX_PARAMS` (8) / `MAX_BETA_PARAMS_LIMIT` (64) in Python.
|
|
158
|
+
- The documented dependency map lives in the module header of
|
|
159
|
+
`src/beta_parameterization_mod.f08`: five intermediates, every mask = all
|
|
160
|
+
`n_params` bits.
|
|
161
|
+
|
|
162
|
+
### Changed
|
|
163
|
+
|
|
164
|
+
- **BREAKING: the cached tier now caps `n_params` at 8, down from 64.** A cache
|
|
165
|
+
carries the recompute engine, whose per-intermediate dependency masks are
|
|
166
|
+
fixed-width, so `shape_core`'s `SHAPE_CACHE_MAX_PARAMS` = 8 is the hard limit.
|
|
167
|
+
`cache_init_s` / `cache_init_shared_s` reject `n_params > 8` with
|
|
168
|
+
`SHAPE_ERROR_TOO_MANY_PARAMS` (1) and `n_params < 1` with
|
|
169
|
+
`SHAPE_ERROR_INVALID_INIT` (5) — the engine check runs before any table is
|
|
170
|
+
built, so nothing is allocated. **The standalone tier is unaffected and still
|
|
171
|
+
accepts up to 64** (`SHAPE_STANDALONE_MAX_PARAMS`): it constructs no engine.
|
|
172
|
+
2.x accepted `max_beta_params` up to 64 for the cache, so any consumer that
|
|
173
|
+
created a cache with more than 8 parameters — wmmm's
|
|
174
|
+
`number_of_deformation_parameters = 20` is the known case — fails at cache
|
|
175
|
+
creation and must either reduce its parameter count or move to the standalone
|
|
176
|
+
tier. Beta PES calculations realistically stay within 8 dimensions; wmmm's
|
|
177
|
+
20 existed for fos→beta conversion, which is obsolete now that fos works on
|
|
178
|
+
the radius grid directly.
|
|
179
|
+
- **BREAKING: the internal uniform grid is gone.** 2.x caches took `n_grid` and
|
|
180
|
+
generated their own uniform theta grid. 3.0.0 takes an explicit **primary
|
|
181
|
+
theta set** — `tables_init_s(tables, max_l, thetas, status)`,
|
|
182
|
+
`cache_init_s(cache, n_params, thetas, ...)`. The caller owns the grid.
|
|
183
|
+
Pole values (θ = 0, π) are rejected as cache input; get them analytically
|
|
184
|
+
from `cache_resolve_shape_s`'s `r_north` / `r_south`.
|
|
185
|
+
- **BREAKING: all `message` arguments removed** from every entry point in
|
|
186
|
+
Fortran, C, C++ and Python. Diagnostics are the integer status plus
|
|
187
|
+
`status_message_f`. No per-call message formatting exists anywhere.
|
|
188
|
+
- **BREAKING (bitwise): the COM correction is now Newton's method** on the
|
|
189
|
+
numerator of the COM integral (`N(β10) = Σᵢ wᵢ xᵢ Rᵢ⁴`,
|
|
190
|
+
`N'(β10) = 4·C₁·Σᵢ wᵢ xᵢ² Rᵢ³`), replacing 2.x fixed-point iteration. The
|
|
191
|
+
tolerance (`|z_cm| < 1e-5`) and iteration cap (50) are unchanged, so the same
|
|
192
|
+
shapes converge — but **corrected β₁₀ and every downstream radius differ in
|
|
193
|
+
the last bits from 2.x**. Goldens were re-baselined; consumers pinning exact
|
|
194
|
+
values must re-baseline too.
|
|
195
|
+
- **BREAKING: `beta_con` is off the public surface.** 2.x
|
|
196
|
+
`cache_resolve_shape` returned the resolved coefficient array and
|
|
197
|
+
`compute_radius_and_derivative` took it back as an argument. 3.0.0 keeps
|
|
198
|
+
resolved coefficients inside the cache as intermediate 1;
|
|
199
|
+
`cache_resolve_shape_s` returns only `corrected_beta10`, `r_north`, `r_south`
|
|
200
|
+
and `volume_factor`, and node evaluation takes `(cache, node_set, params)`.
|
|
201
|
+
- Type-bound methods (`cache%init`, `cache%compute_radius_grid`, …) replaced by
|
|
202
|
+
free subroutines (`cache_init_s`, `cache_radius_grid_s`, …), matching the
|
|
203
|
+
contract's naming.
|
|
204
|
+
- Standalone tier: `compute_radius_grid_standalone_s(params, thetas,
|
|
205
|
+
conserve_volume, apply_com, radii, status)` and
|
|
206
|
+
`compute_radius_and_derivative_standalone_s(...)`. `n_params = size(params)`,
|
|
207
|
+
accepted up to the **standalone** cap of 64
|
|
208
|
+
(`SHAPE_STANDALONE_MAX_PARAMS` = `MAX_BETA_PARAMS_LIMIT`); above →
|
|
209
|
+
`SHAPE_ERROR_TOO_MANY_PARAMS`, never silent truncation. The
|
|
210
|
+
`max_beta_params` padding argument is removed. Tier 1 never constructs an
|
|
211
|
+
engine, which is why it keeps the higher cap.
|
|
212
|
+
- Failure semantics are now uniform: on ANY nonzero status inside a checked
|
|
213
|
+
cached compute the library zero-fills every output and invalidates the whole
|
|
214
|
+
engine, so the next call runs cold. Usage errors follow the contract's
|
|
215
|
+
normative order — a buffer-size mismatch (104) is reported before a node-set
|
|
216
|
+
mismatch (106).
|
|
217
|
+
- Minimum fortran-foundations is **2.4.0** (`shape_core_mod`); gcc-opts stays at
|
|
218
|
+
**2.0.0**.
|
|
219
|
+
|
|
220
|
+
### Changed: status codes
|
|
221
|
+
|
|
222
|
+
Shared contract codes (0–99) come from `shape_core_mod`: `SHAPE_VALID` 0,
|
|
223
|
+
`SHAPE_ERROR_TOO_MANY_PARAMS` 1, `SHAPE_ERROR_CACHE_NOT_INITIALIZED` 2,
|
|
224
|
+
`SHAPE_ERROR_INVALID_GRID` 3 (theta floor: `size(thetas) >= 2`),
|
|
225
|
+
`SHAPE_ERROR_WRONG_PARAM_COUNT` 4, `SHAPE_ERROR_INVALID_INIT` 5,
|
|
226
|
+
`SHAPE_ERROR_TABLES_NOT_INITIALIZED` 6.
|
|
227
|
+
|
|
228
|
+
Library codes are renamed `LEGENDRE_*` → `BETA_PARAM_ERROR_*` and renumbered
|
|
229
|
+
≥ 100. This range is **append-only** from 3.0.0 on:
|
|
230
|
+
|
|
231
|
+
| old (2.x) | new (3.0.0) |
|
|
232
|
+
|---|---|
|
|
233
|
+
| `LEGENDRE_ERROR_NORTH_POLE` 1 | `BETA_PARAM_ERROR_NORTH_POLE` 100 |
|
|
234
|
+
| `LEGENDRE_ERROR_SOUTH_POLE` 2 | `BETA_PARAM_ERROR_SOUTH_POLE` 101 |
|
|
235
|
+
| `LEGENDRE_ERROR_INTERIOR_NEGATIVE` 4 | `BETA_PARAM_ERROR_INTERIOR_NEGATIVE` 102 |
|
|
236
|
+
| `LEGENDRE_ERROR_COM_NOT_CONVERGED` 7 | `BETA_PARAM_ERROR_COM_NOT_CONVERGED` 103 |
|
|
237
|
+
| `LEGENDRE_ERROR_INVALID_BUFFER_SIZE` 8 | `BETA_PARAM_ERROR_INVALID_BUFFER_SIZE` 104 |
|
|
238
|
+
| `LEGENDRE_ERROR_POLE_NODE` 9 | `BETA_PARAM_ERROR_POLE_NODE` 105 |
|
|
239
|
+
| — (new) | `BETA_PARAM_ERROR_NODE_SET_MISMATCH` 106 |
|
|
240
|
+
| `LEGENDRE_ERROR_EMPTY_PARAMS` 3 | → shared 4 (compute) / 5 (init) |
|
|
241
|
+
| `LEGENDRE_ERROR_INVALID_MAX_PARAMS` 5 | → shared 5 / 3 / 2 by cause |
|
|
242
|
+
| `LEGENDRE_ERROR_TOO_MANY_PARAMS` 6 | → shared 1 |
|
|
243
|
+
| `LEGENDRE_ERROR_NO_UNIFORM_GRID` 10 | removed (no uniform grid) |
|
|
244
|
+
|
|
245
|
+
Note that the old and new numbering overlap: 2.x code 1 meant "north pole",
|
|
246
|
+
3.0.0 code 1 means "too many params". Callers comparing raw integers must be
|
|
247
|
+
updated, not merely recompiled.
|
|
248
|
+
|
|
249
|
+
### Changed: C API
|
|
250
|
+
|
|
251
|
+
Handle-based, with statuses reported through out-parameters instead of
|
|
252
|
+
in-band returns:
|
|
253
|
+
|
|
254
|
+
- `beta_param_tables_create(max_l, thetas, n_thetas, int* status)` /
|
|
255
|
+
`beta_param_tables_destroy` — new handle type `beta_param_tables_t*`.
|
|
256
|
+
- `beta_param_cache_create(n_params, thetas, n_thetas, conserve_volume,
|
|
257
|
+
apply_com, int* status)` and
|
|
258
|
+
`beta_param_cache_create_shared(tables, n_params, conserve_volume, apply_com,
|
|
259
|
+
int* status)` — both return `NULL` on failure and write the rejecting code to
|
|
260
|
+
the nullable `status` out-param. The 2.x `n_grid` + `message` arguments are
|
|
261
|
+
gone.
|
|
262
|
+
- Computes drop the `compute_` infix and the `_with_com_shift` variants:
|
|
263
|
+
`beta_param_cache_radius_grid`, `_radius_and_derivative`,
|
|
264
|
+
`_radius_grid_unchecked`, `_resolve_shape`, `_node_radius_and_derivative`,
|
|
265
|
+
`beta_param_radius_grid_standalone`, `_radius_and_derivative_standalone`.
|
|
266
|
+
Each returns the status code directly.
|
|
267
|
+
- `beta_param_status_message(int)` returns a static string; no caller-supplied
|
|
268
|
+
message buffers anywhere.
|
|
269
|
+
- Lifetime rule: a tables handle passed to `beta_param_cache_create_shared()`
|
|
270
|
+
or `beta_param_node_set_create()` must outlive everything built from it;
|
|
271
|
+
`beta_param_cache_destroy()` never frees shared tables.
|
|
272
|
+
|
|
273
|
+
### Changed: C++ header
|
|
274
|
+
|
|
275
|
+
`beta_parameterization.hpp` (C++20, header-only) exposes `Tables`, `Cache` and
|
|
276
|
+
`NodeSet` RAII wrappers. `Cache` is move-only and thread-confined; its compute
|
|
277
|
+
methods are non-`const`. Status strings come from `beta_param_status_message`.
|
|
278
|
+
The 2.x concurrent-compute documentation is replaced, not amended.
|
|
279
|
+
|
|
280
|
+
### Changed: Python wheel
|
|
281
|
+
|
|
282
|
+
- **BREAKING: `NodeSet` is removed.** BetaRender's single-set usage maps onto
|
|
283
|
+
the primary theta set. Node sets return to Python only when a consumer needs
|
|
284
|
+
a genuine second set — the same principle that keeps `tables_t` out of
|
|
285
|
+
Python.
|
|
286
|
+
- **BREAKING: `theta_grid(n)` is now the OPEN uniform grid**
|
|
287
|
+
`theta_i = i·π/(n+1), i = 1..n`. The 2.x closed `linspace(0, π, n)` includes
|
|
288
|
+
both poles, which the pole guard now rejects as cache input. Close plotted
|
|
289
|
+
curves with the analytic `r_north` / `r_south` from `resolve_shape`.
|
|
290
|
+
- Module-level tier 1: `radius_grid(params, thetas, conserve_volume=False,
|
|
291
|
+
apply_com=False)`, `radius_and_derivative(...)`.
|
|
292
|
+
- `Cache(n_params, thetas, conserve_volume=False, apply_com=False)` with
|
|
293
|
+
`.radius_grid`, `.radius_and_derivative`, `.radius_grid_unchecked`,
|
|
294
|
+
`.resolve_shape`. Thread-confined.
|
|
295
|
+
- **Returns are result objects, not exceptions**: `RadiusGridResult`,
|
|
296
|
+
`RadiusDerivativeResult`, `ResolvedShape`, each with `.ok`, `.status` and a
|
|
297
|
+
`.message` property. Shape-validation failures come back as a status-carrying
|
|
298
|
+
result; `BetaParamError` is raised only for usage errors (closed handle,
|
|
299
|
+
non-1-D params, create failure).
|
|
300
|
+
- The `Status` enum is renumbered per the table above.
|
|
301
|
+
|
|
302
|
+
### Removed
|
|
303
|
+
|
|
304
|
+
- Concurrent-compute-on-one-cache support (see the breaking section above).
|
|
305
|
+
- The internally generated uniform theta grid and
|
|
306
|
+
`LEGENDRE_ERROR_NO_UNIFORM_GRID`.
|
|
307
|
+
- All `message` output arguments and the message-formatting code behind them.
|
|
308
|
+
- `*_with_com_shift` entry points (Fortran, C), superseded by the `apply_com`
|
|
309
|
+
creation flag.
|
|
310
|
+
- `beta_con` from every public signature.
|
|
311
|
+
- The `max_beta_params` padding argument on the standalone entry points.
|
|
312
|
+
- Python `NodeSet`.
|
|
313
|
+
|
|
314
|
+
### Migration sketch
|
|
315
|
+
|
|
316
|
+
```fortran
|
|
317
|
+
! 2.x
|
|
318
|
+
call cache%init(max_beta_params = 4_ik, n_grid = 80_ik, error_code = ec, message = msg)
|
|
319
|
+
call cache%compute_radius_grid_with_com_shift(params, radii, ec, msg)
|
|
320
|
+
call cache%destroy()
|
|
321
|
+
|
|
322
|
+
! 3.0.0 — the caller owns the theta set; flags are set once, at creation
|
|
323
|
+
thetas = [(i * PI / 81.0_rk, i = 1, 80)] ! open grid: no poles
|
|
324
|
+
call cache_init_s(cache, n_params = 4_ik, thetas = thetas, &
|
|
325
|
+
conserve_volume = .false., apply_com = .true., status = status)
|
|
326
|
+
call cache_radius_grid_s(cache, params, radii, status)
|
|
327
|
+
call cache_free_s(cache)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## 2.3.4 and earlier
|
|
331
|
+
|
|
332
|
+
No changelog was kept before 3.0.0. See the git history.
|
|
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.20)
|
|
|
3
3
|
include(FetchContent)
|
|
4
4
|
|
|
5
5
|
project(beta-parameterization
|
|
6
|
-
VERSION
|
|
6
|
+
VERSION 4.0.0
|
|
7
7
|
LANGUAGES Fortran CXX
|
|
8
8
|
DESCRIPTION "Beta parameterization for nuclear physics applications"
|
|
9
9
|
)
|
|
@@ -14,19 +14,19 @@ set(CMAKE_CXX_EXTENSIONS OFF)
|
|
|
14
14
|
|
|
15
15
|
# --- Fetch GCC compiler options package ---
|
|
16
16
|
FetchContent_Declare(
|
|
17
|
-
|
|
18
|
-
GIT_REPOSITORY https://github.com/AleksanderAugustyn/
|
|
17
|
+
gcc-compiler-options
|
|
18
|
+
GIT_REPOSITORY https://github.com/AleksanderAugustyn/gcc-compiler-options.git
|
|
19
19
|
GIT_TAG 2.0.0
|
|
20
20
|
)
|
|
21
|
-
FetchContent_MakeAvailable(
|
|
21
|
+
FetchContent_MakeAvailable(gcc-compiler-options)
|
|
22
22
|
|
|
23
23
|
# --- Fetch foundational Fortran modules ---
|
|
24
24
|
FetchContent_Declare(
|
|
25
|
-
|
|
26
|
-
GIT_REPOSITORY https://github.com/AleksanderAugustyn/
|
|
27
|
-
GIT_TAG
|
|
25
|
+
fortran-foundations
|
|
26
|
+
GIT_REPOSITORY https://github.com/AleksanderAugustyn/fortran-foundations.git
|
|
27
|
+
GIT_TAG 3.0.0
|
|
28
28
|
)
|
|
29
|
-
FetchContent_MakeAvailable(
|
|
29
|
+
FetchContent_MakeAvailable(fortran-foundations)
|
|
30
30
|
|
|
31
31
|
# --- Preprocessor & build type ---
|
|
32
32
|
set(CMAKE_Fortran_PREPROCESS ON)
|
|
@@ -35,7 +35,7 @@ if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
|
|
35
35
|
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "RelWithDebInfo" "Release")
|
|
36
36
|
endif ()
|
|
37
37
|
|
|
38
|
-
# --- Compiler flags via
|
|
38
|
+
# --- Compiler flags via gcc-compiler-options ---
|
|
39
39
|
include(GCCCompilerOptions/FortranCompilerOptions)
|
|
40
40
|
create_fortran_library_interface(TARGET beta_parameterization_flags)
|
|
41
41
|
|
|
@@ -46,6 +46,16 @@ set(BETA_PARAM_SOURCES
|
|
|
46
46
|
src/beta_parameterization_c_api_mod.f08
|
|
47
47
|
)
|
|
48
48
|
|
|
49
|
+
# --- No LTO for the library's own objects ---
|
|
50
|
+
# Release builds use -flto=auto -ffast-math. With LTO the summation kernels are
|
|
51
|
+
# inlined into every caller and optimized per call site, so the same call can
|
|
52
|
+
# return different last bits from different call sites — which breaks the
|
|
53
|
+
# contract's bitwise guarantees (one-shot == cached, statelessness,
|
|
54
|
+
# concurrency). Compiled without LTO, each kernel is ONE machine-code body that
|
|
55
|
+
# every caller executes. Consumers keep LTO for their own code.
|
|
56
|
+
# Source-file property so it lands after the flags-interface options (last wins).
|
|
57
|
+
set_source_files_properties(${BETA_PARAM_SOURCES} PROPERTIES COMPILE_OPTIONS "-fno-lto")
|
|
58
|
+
|
|
49
59
|
# --- Static library target ---
|
|
50
60
|
add_library(beta_parameterization STATIC ${BETA_PARAM_SOURCES})
|
|
51
61
|
target_link_libraries(beta_parameterization
|
|
@@ -118,7 +128,8 @@ set_target_properties(beta_param_test_utils PROPERTIES
|
|
|
118
128
|
target_include_directories(beta_param_test_utils PUBLIC
|
|
119
129
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/mod_files_tests>)
|
|
120
130
|
|
|
121
|
-
set(BETA_PARAM_TEST_SUITES
|
|
131
|
+
set(BETA_PARAM_TEST_SUITES status lifecycle resolve outputs standalone property golden
|
|
132
|
+
equivalence statelessness boundary)
|
|
122
133
|
foreach (suite IN LISTS BETA_PARAM_TEST_SUITES)
|
|
123
134
|
add_executable(beta_param_${suite}_test tests/beta_param_${suite}_test.f08)
|
|
124
135
|
target_link_libraries(beta_param_${suite}_test PRIVATE
|
|
@@ -134,19 +145,36 @@ endforeach ()
|
|
|
134
145
|
set_source_files_properties(tests/beta_param_golden_test.f08 PROPERTIES
|
|
135
146
|
COMPILE_OPTIONS "-Wno-conversion-extra;-Wno-error=conversion-extra")
|
|
136
147
|
|
|
148
|
+
# Capture tool for the golden baseline: an executable, never a test.
|
|
137
149
|
add_executable(golden_capture tests/golden_capture.f08)
|
|
138
150
|
target_link_libraries(golden_capture PRIVATE
|
|
139
151
|
beta_parameterization beta_param_test_utils beta_parameterization_flags)
|
|
140
152
|
set_target_properties(golden_capture PROPERTIES
|
|
141
153
|
Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/mod_files_tests/capture)
|
|
142
154
|
|
|
155
|
+
# PES-range sweep: 1.55M shapes, far too slow for ctest. An executable driven
|
|
156
|
+
# by an explicit custom target, never registered with add_test.
|
|
157
|
+
add_executable(beta_pes_sweep tests/beta_pes_sweep.f08)
|
|
158
|
+
target_link_libraries(beta_pes_sweep PRIVATE
|
|
159
|
+
beta_parameterization beta_param_test_utils beta_parameterization_flags)
|
|
160
|
+
set_target_properties(beta_pes_sweep PROPERTIES
|
|
161
|
+
Fortran_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/mod_files_tests/sweep)
|
|
162
|
+
add_custom_target(test_beta_pes_sweep
|
|
163
|
+
COMMAND beta_pes_sweep
|
|
164
|
+
DEPENDS beta_pes_sweep
|
|
165
|
+
COMMENT "Running the PES-range sweep (1.55M + 172.8k shapes)")
|
|
166
|
+
|
|
167
|
+
# C API smoke test: links the SHARED library, the binary the bindings load.
|
|
143
168
|
add_executable(c_api_smoke_test tests/c_api_smoke_test.cpp)
|
|
144
169
|
target_link_libraries(c_api_smoke_test PRIVATE beta_parameterization_cxx)
|
|
145
170
|
add_test(NAME c_api_smoke COMMAND c_api_smoke_test)
|
|
146
171
|
|
|
172
|
+
# Contract family 3 (concurrency): many threads on one shared cache, via the
|
|
173
|
+
# C++ wrapper.
|
|
147
174
|
find_package(Threads REQUIRED)
|
|
148
175
|
add_executable(thread_stress_test tests/thread_stress_test.cpp)
|
|
149
|
-
target_link_libraries(thread_stress_test PRIVATE
|
|
176
|
+
target_link_libraries(thread_stress_test PRIVATE
|
|
177
|
+
beta_parameterization_cxx Threads::Threads)
|
|
150
178
|
add_test(NAME thread_stress COMMAND thread_stress_test)
|
|
151
179
|
|
|
152
180
|
endif ()
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# beta-parameterization
|
|
2
|
+
|
|
3
|
+
Axially symmetric nuclear shapes in the spherical-harmonic (beta) expansion: a Fortran 2018 library with a C API, a C++20 wrapper and a Python wheel. It implements the two-tier shape parameterization contract shared with its sibling libraries.
|
|
4
|
+
|
|
5
|
+
## The shape
|
|
6
|
+
|
|
7
|
+
The surface radius, in units of the spherical radius R₀, is
|
|
8
|
+
|
|
9
|
+
R(θ) = c · [ 1 + Σ_{λ=1}^{n} β_λ · C_λ · P_λ(cos θ) ], C_λ = √((2λ+1) / 4π)
|
|
10
|
+
|
|
11
|
+
so that `C_λ · P_λ(cos θ)` is the spherical harmonic Y_λ0. `params(λ)` is β_λ0; position λ always means order λ. Up to 64 orders are supported.
|
|
12
|
+
|
|
13
|
+
Two per-call options change the shape:
|
|
14
|
+
|
|
15
|
+
- `apply_com` — β₁ is replaced by the value that puts the centre of mass at the origin (Newton iteration, tolerance 10⁻⁵ R₀).
|
|
16
|
+
- `conserve_volume` — the scale `c = (2 / ∫ R³ d cos θ)^(1/3)` restores the volume of the unit sphere. Without it, `c = 1`.
|
|
17
|
+
|
|
18
|
+
A shape is valid when R > 10⁻⁶ at both poles and on an internal 512-node Gauss-Legendre grid. An invalid shape returns a status code and zero-filled outputs.
|
|
19
|
+
|
|
20
|
+
## Two tiers
|
|
21
|
+
|
|
22
|
+
1. **One-shot.** One call computes one shape. All workspace is internal and discarded on return. For scripts and one-off plots.
|
|
23
|
+
2. **Read-only cache.** Build a cache once for a theta grid, then request any number of shapes against it. The cache holds only what does not depend on the parameters (Legendre tables, quadrature). It is immutable after creation and may be shared across threads. For loops and hot paths.
|
|
24
|
+
|
|
25
|
+
Both tiers return bitwise-identical results. Nothing parameter-dependent is stored: every call is independent of the calls before it.
|
|
26
|
+
|
|
27
|
+
Rules common to both tiers:
|
|
28
|
+
|
|
29
|
+
- A cache accepts `1 .. max_params` parameters per call; a one-shot call accepts `1 .. 64`.
|
|
30
|
+
- Missing trailing parameters are zero. A short vector and its zero-padded form give identical bits.
|
|
31
|
+
- Thetas are in radians, at least two, none at a pole (the open grid `θ_i = i·π/(n+1)` is the usual choice). Pole radii come from `resolve_shape`.
|
|
32
|
+
- Inputs must be finite and of physical magnitude. Release builds use fast-math and cannot detect NaN, and with `apply_com` the quadrature overflows for |β| beyond about 10⁷⁰.
|
|
33
|
+
- Every output is zero-filled on a nonzero status.
|
|
34
|
+
|
|
35
|
+
### Fortran
|
|
36
|
+
|
|
37
|
+
```fortran
|
|
38
|
+
program beta_example
|
|
39
|
+
use precision_utilities_mod, only: ik, rk
|
|
40
|
+
use beta_parameterization_mod, only: cache_t, cache_init_s, cache_free_s, &
|
|
41
|
+
cache_radius_grid_s, cache_resolve_shape_s, &
|
|
42
|
+
compute_radius_grid_standalone_s, SHAPE_VALID
|
|
43
|
+
implicit none
|
|
44
|
+
|
|
45
|
+
integer(kind = ik), parameter :: N = 180_ik
|
|
46
|
+
real(kind = rk), parameter :: PI = 3.141592653589793_rk
|
|
47
|
+
type(cache_t) :: cache
|
|
48
|
+
real(kind = rk) :: thetas(N), radii(N)
|
|
49
|
+
real(kind = rk) :: beta10, r_north, r_south, volume_factor
|
|
50
|
+
integer(kind = ik) :: i, status
|
|
51
|
+
|
|
52
|
+
do i = 1_ik, N
|
|
53
|
+
thetas(i) = real(i, rk) * PI / real(N + 1_ik, rk) ! open grid: no pole nodes
|
|
54
|
+
end do
|
|
55
|
+
|
|
56
|
+
! Tier 1: one-shot. params = (beta1, beta2), both options on.
|
|
57
|
+
call compute_radius_grid_standalone_s([0.0_rk, 0.25_rk], thetas, .true., .true., &
|
|
58
|
+
radii, status)
|
|
59
|
+
if (status /= SHAPE_VALID) error stop 'one-shot failed'
|
|
60
|
+
|
|
61
|
+
! Tier 2: build once, share, compute many.
|
|
62
|
+
call cache_init_s(cache, 8_ik, thetas, status) ! max_params = 8
|
|
63
|
+
call cache_radius_grid_s(cache, [0.0_rk, 0.25_rk], .true., .true., radii, status)
|
|
64
|
+
call cache_resolve_shape_s(cache, [0.0_rk, 0.25_rk], .true., .true., &
|
|
65
|
+
beta10, r_north, r_south, volume_factor, status)
|
|
66
|
+
call cache_free_s(cache)
|
|
67
|
+
end program beta_example
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Cached outputs: `cache_radius_grid_s`, `cache_radius_and_derivative_s`, `cache_resolve_shape_s` (corrected β₁, pole radii, volume factor), `cache_node_radius_and_derivative_s` (radius and derivative at a `node_set_t`: extra thetas built from the cache with `node_set_build_s`), and `cache_radius_grid_unchecked_s` (no validity gate and no volume scaling: the outline of a shape the checked path rejects). To evaluate several theta grids in one call, build one node set over their concatenation.
|
|
71
|
+
|
|
72
|
+
### C
|
|
73
|
+
|
|
74
|
+
```c
|
|
75
|
+
#include "beta_parameterization.h"
|
|
76
|
+
#include <stdio.h>
|
|
77
|
+
|
|
78
|
+
int main(void) {
|
|
79
|
+
enum { N = 180 };
|
|
80
|
+
const double pi = 3.141592653589793;
|
|
81
|
+
double thetas[N], radii[N];
|
|
82
|
+
const double params[2] = {0.0, 0.25};
|
|
83
|
+
for (int i = 0; i < N; ++i) thetas[i] = (i + 1) * pi / (N + 1);
|
|
84
|
+
|
|
85
|
+
/* Tier 1: one-shot. */
|
|
86
|
+
int status = beta_param_radius_grid_standalone(params, 2, thetas, N, 1, 1, radii);
|
|
87
|
+
if (status != BETA_PARAM_VALID) {
|
|
88
|
+
printf("%s\n", beta_param_status_message(status));
|
|
89
|
+
return 1;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Tier 2: build once, share, compute many. */
|
|
93
|
+
beta_param_cache_t* cache = beta_param_cache_create(8, thetas, N, &status);
|
|
94
|
+
if (cache == NULL) {
|
|
95
|
+
printf("%s\n", beta_param_status_message(status));
|
|
96
|
+
return 1;
|
|
97
|
+
}
|
|
98
|
+
status = beta_param_cache_radius_grid(cache, params, 2, 1, 1, radii, N);
|
|
99
|
+
beta_param_cache_destroy(cache);
|
|
100
|
+
return status;
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
C++20 callers can use the RAII wrapper in `beta_parameterization.hpp` (`beta_param::Cache`, `beta_param::NodeSet`, `const` compute methods, `std::span` arguments).
|
|
105
|
+
|
|
106
|
+
### Python
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
import beta_parameterization as bp
|
|
110
|
+
|
|
111
|
+
thetas = bp.theta_grid(180) # open grid, no pole nodes
|
|
112
|
+
|
|
113
|
+
# Tier 1: one-shot.
|
|
114
|
+
res = bp.radius_grid([0.0, 0.25], thetas, conserve_volume=True, apply_com=True)
|
|
115
|
+
assert res.ok, res.message
|
|
116
|
+
|
|
117
|
+
# Tier 2: build once, share, compute many.
|
|
118
|
+
with bp.Cache(8, thetas) as cache: # max_params = 8
|
|
119
|
+
res = cache.radius_and_derivative([0.0, 0.25], conserve_volume=True, apply_com=True)
|
|
120
|
+
shape = cache.resolve_shape([0.0, 0.25], conserve_volume=True, apply_com=True)
|
|
121
|
+
print(res.radii[:3], shape.volume_factor)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Shape-validation failures come back as results carrying a `Status`; `BetaParamError` is raised only for usage errors (failed create, closed handle, non-1-D input).
|
|
125
|
+
|
|
126
|
+
## Status codes
|
|
127
|
+
|
|
128
|
+
| Code | Name | Meaning |
|
|
129
|
+
|---|---|---|
|
|
130
|
+
| 0 | `SHAPE_VALID` | success |
|
|
131
|
+
| 1 | `SHAPE_ERROR_TOO_MANY_PARAMS` | `max_params` or a one-shot vector exceeds 64 |
|
|
132
|
+
| 2 | `SHAPE_ERROR_CACHE_NOT_INITIALIZED` | uninitialized cache (or NULL handle) |
|
|
133
|
+
| 3 | `SHAPE_ERROR_INVALID_GRID` | fewer than 2 thetas, or a table that cannot be allocated |
|
|
134
|
+
| 4 | `SHAPE_ERROR_WRONG_PARAM_COUNT` | vector length outside `1..max_params`, or an empty one-shot vector |
|
|
135
|
+
| 5 | `SHAPE_ERROR_INVALID_INIT` | `max_params < 1` |
|
|
136
|
+
| 100 | `BETA_PARAM_ERROR_NORTH_POLE` | radius not positive at θ = 0 |
|
|
137
|
+
| 101 | `BETA_PARAM_ERROR_SOUTH_POLE` | radius not positive at θ = π |
|
|
138
|
+
| 102 | `BETA_PARAM_ERROR_INTERIOR_NEGATIVE` | radius not positive in the interior |
|
|
139
|
+
| 103 | `BETA_PARAM_ERROR_COM_NOT_CONVERGED` | centre-of-mass correction did not converge |
|
|
140
|
+
| 104 | `BETA_PARAM_ERROR_INVALID_BUFFER_SIZE` | output buffer size does not match the theta or node count |
|
|
141
|
+
| 105 | `BETA_PARAM_ERROR_POLE_NODE` | a theta at or beyond a pole |
|
|
142
|
+
| 106 | `BETA_PARAM_ERROR_NODE_SET_MISMATCH` | node set unbuilt, or built for a smaller cache |
|
|
143
|
+
|
|
144
|
+
Codes 0–5 are shared by every shape parameterization library; the C header and the Python `Status` enum mirror all of them under `BETA_PARAM_*` names.
|
|
145
|
+
|
|
146
|
+
## Building
|
|
147
|
+
|
|
148
|
+
Requires GCC (gfortran, g++) and CMake ≥ 3.20. Dependencies are fetched by CMake.
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
|
152
|
+
cmake --build build -j
|
|
153
|
+
ctest --test-dir build --output-on-failure
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
As a CMake dependency:
|
|
157
|
+
|
|
158
|
+
```cmake
|
|
159
|
+
FetchContent_Declare(
|
|
160
|
+
beta-parameterization
|
|
161
|
+
GIT_REPOSITORY https://github.com/AleksanderAugustyn/beta-parameterization.git
|
|
162
|
+
GIT_TAG 4.0.0
|
|
163
|
+
)
|
|
164
|
+
FetchContent_MakeAvailable(beta-parameterization)
|
|
165
|
+
target_link_libraries(my_target PRIVATE BetaParameterization::beta_parameterization)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Targets: `BetaParameterization::beta_parameterization` (static, Fortran), `::beta_parameterization_shared` (shared, C API), `::beta_parameterization_cxx` (header-only C++ wrapper over the shared library).
|
|
169
|
+
|
|
170
|
+
Python:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
pip install beta-parameterization==4.0.0
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
The wheel is self-contained (manylinux2014, x86-64). To run the Python tests against a local build instead: `BETA_PARAM_LIB=$PWD/build/libbeta_parameterization.so PYTHONPATH=python python -m pytest python/tests`.
|
|
177
|
+
|
|
178
|
+
## Dependencies
|
|
179
|
+
|
|
180
|
+
| Dependency | Version |
|
|
181
|
+
|---|---|
|
|
182
|
+
| [fortran-foundations](https://github.com/AleksanderAugustyn/fortran-foundations) | 3.0.0 |
|
|
183
|
+
| [gcc-compiler-options](https://github.com/AleksanderAugustyn/gcc-compiler-options) | 2.0.0 |
|
|
184
|
+
| numpy (wheel only) | ≥ 1.21 |
|
|
185
|
+
|
|
186
|
+
## Design
|
|
187
|
+
|
|
188
|
+
- **Stateless computes.** A cache holds only parameter-independent tables. Every compute takes it read-only and is `pure`; per-call scratch lives on the stack. One cache serves every thread and every option combination.
|
|
189
|
+
- **One pipeline.** A one-shot call builds a local cache and runs the cached routine, so the two tiers cannot drift apart.
|
|
190
|
+
- **Reproducible bits.** The library's own objects are compiled without link-time optimization. Under `-flto -ffast-math` a kernel inlined into two call sites may round differently in each; without LTO every caller executes the same machine code, and equal inputs give equal bits.
|
|
191
|
+
- **No stops.** Every failure is a status code; nothing in the library calls `error stop`.
|