bijective-gamut 2.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.
- bijective_gamut-2.1.0/.gitignore +30 -0
- bijective_gamut-2.1.0/CHANGELOG.md +450 -0
- bijective_gamut-2.1.0/CITATION.cff +34 -0
- bijective_gamut-2.1.0/CMakeLists.txt +182 -0
- bijective_gamut-2.1.0/CONTRIBUTING.md +104 -0
- bijective_gamut-2.1.0/LICENSE +202 -0
- bijective_gamut-2.1.0/PKG-INFO +264 -0
- bijective_gamut-2.1.0/README.md +219 -0
- bijective_gamut-2.1.0/SPEC.md +936 -0
- bijective_gamut-2.1.0/examples/00_readme.py +29 -0
- bijective_gamut-2.1.0/examples/01_run_table.py +36 -0
- bijective_gamut-2.1.0/examples/02_one_mesh.py +55 -0
- bijective_gamut-2.1.0/examples/03_geometry_family.py +39 -0
- bijective_gamut-2.1.0/examples/04_changing_mesh.py +32 -0
- bijective_gamut-2.1.0/examples/05_time_series.py +29 -0
- bijective_gamut-2.1.0/examples/06_uncertainty_gradients.py +35 -0
- bijective_gamut-2.1.0/examples/07_models.py +81 -0
- bijective_gamut-2.1.0/examples/08_selecting_parts.py +38 -0
- bijective_gamut-2.1.0/examples/09_combining_files.py +35 -0
- bijective_gamut-2.1.0/examples/10_vtk.py +37 -0
- bijective_gamut-2.1.0/examples/_cylinder.py +26 -0
- bijective_gamut-2.1.0/examples/c/one_mesh.c +97 -0
- bijective_gamut-2.1.0/examples/cli/second_entry.json +1 -0
- bijective_gamut-2.1.0/examples/cli/second_entry_runs.csv +3 -0
- bijective_gamut-2.1.0/examples/cli/sweep.json +9 -0
- bijective_gamut-2.1.0/examples/cli/sweep_runs.csv +5 -0
- bijective_gamut-2.1.0/examples/cpp/combining.cpp +47 -0
- bijective_gamut-2.1.0/examples/cpp/cylinder.hpp +51 -0
- bijective_gamut-2.1.0/examples/cpp/family.cpp +54 -0
- bijective_gamut-2.1.0/examples/cpp/nearest.cpp +29 -0
- bijective_gamut-2.1.0/examples/cpp/one_mesh.cpp +84 -0
- bijective_gamut-2.1.0/examples/cpp/parts.cpp +58 -0
- bijective_gamut-2.1.0/examples/cpp/refinement.cpp +50 -0
- bijective_gamut-2.1.0/examples/cpp/run_table.cpp +46 -0
- bijective_gamut-2.1.0/examples/cpp/sweep.cpp +36 -0
- bijective_gamut-2.1.0/examples/cpp/transient.cpp +44 -0
- bijective_gamut-2.1.0/examples/cpp/uncertain.cpp +44 -0
- bijective_gamut-2.1.0/examples/rust/combining.rs +43 -0
- bijective_gamut-2.1.0/examples/rust/cylinder.rs +47 -0
- bijective_gamut-2.1.0/examples/rust/family.rs +50 -0
- bijective_gamut-2.1.0/examples/rust/nearest.rs +23 -0
- bijective_gamut-2.1.0/examples/rust/one_mesh.rs +69 -0
- bijective_gamut-2.1.0/examples/rust/parts.rs +49 -0
- bijective_gamut-2.1.0/examples/rust/refinement.rs +46 -0
- bijective_gamut-2.1.0/examples/rust/run_table.rs +41 -0
- bijective_gamut-2.1.0/examples/rust/sweep.rs +28 -0
- bijective_gamut-2.1.0/examples/rust/transient.rs +37 -0
- bijective_gamut-2.1.0/examples/rust/uncertain.rs +39 -0
- bijective_gamut-2.1.0/exports.map +4 -0
- bijective_gamut-2.1.0/exports.txt +1 -0
- bijective_gamut-2.1.0/include/gamut/gamut.h +399 -0
- bijective_gamut-2.1.0/include/gamut/gamut.hpp +695 -0
- bijective_gamut-2.1.0/licenses/hdf5-lbnl.txt +61 -0
- bijective_gamut-2.1.0/licenses/hdf5.txt +106 -0
- bijective_gamut-2.1.0/licenses/zlib.txt +22 -0
- bijective_gamut-2.1.0/llms.txt +115 -0
- bijective_gamut-2.1.0/mkdocs.yml +120 -0
- bijective_gamut-2.1.0/overrides/partials/logo.html +3 -0
- bijective_gamut-2.1.0/packaging/gamut-format/README.md +6 -0
- bijective_gamut-2.1.0/packaging/gamut-format/pyproject.toml +30 -0
- bijective_gamut-2.1.0/pyproject.toml +88 -0
- bijective_gamut-2.1.0/python/gamut/__init__.py +48 -0
- bijective_gamut-2.1.0/python/gamut/__main__.py +6 -0
- bijective_gamut-2.1.0/python/gamut/_files.py +44 -0
- bijective_gamut-2.1.0/python/gamut/_lib.py +430 -0
- bijective_gamut-2.1.0/python/gamut/_models.py +507 -0
- bijective_gamut-2.1.0/python/gamut/_study.py +785 -0
- bijective_gamut-2.1.0/python/gamut/_units.py +64 -0
- bijective_gamut-2.1.0/python/gamut/_validate.py +68 -0
- bijective_gamut-2.1.0/python/gamut/cli.py +922 -0
- bijective_gamut-2.1.0/python/gamut/vtk.py +554 -0
- bijective_gamut-2.1.0/python/tests/_pages.py +156 -0
- bijective_gamut-2.1.0/python/tests/test_binding.py +331 -0
- bijective_gamut-2.1.0/python/tests/test_cli.py +433 -0
- bijective_gamut-2.1.0/python/tests/test_docs.py +140 -0
- bijective_gamut-2.1.0/python/tests/test_edges.py +1172 -0
- bijective_gamut-2.1.0/python/tests/test_examples.py +54 -0
- bijective_gamut-2.1.0/python/tests/test_existing_destinations.py +105 -0
- bijective_gamut-2.1.0/python/tests/test_files.py +346 -0
- bijective_gamut-2.1.0/python/tests/test_guide_cli.py +61 -0
- bijective_gamut-2.1.0/python/tests/test_guide_languages.py +77 -0
- bijective_gamut-2.1.0/python/tests/test_layouts.py +156 -0
- bijective_gamut-2.1.0/python/tests/test_models.py +604 -0
- bijective_gamut-2.1.0/python/tests/test_readme.py +84 -0
- bijective_gamut-2.1.0/python/tests/test_validate.py +553 -0
- bijective_gamut-2.1.0/python/tests/test_vtk.py +270 -0
- bijective_gamut-2.1.0/rust/.gitignore +2 -0
- bijective_gamut-2.1.0/rust/Cargo.toml +10 -0
- bijective_gamut-2.1.0/rust/gamut/Cargo.toml +59 -0
- bijective_gamut-2.1.0/rust/gamut/build.rs +8 -0
- bijective_gamut-2.1.0/rust/gamut/src/lib.rs +1176 -0
- bijective_gamut-2.1.0/rust/gamut/tests/corpus.rs +265 -0
- bijective_gamut-2.1.0/rust/gamut/tests/study.rs +115 -0
- bijective_gamut-2.1.0/rust/gamut-sys/Cargo.toml +16 -0
- bijective_gamut-2.1.0/rust/gamut-sys/build.rs +10 -0
- bijective_gamut-2.1.0/rust/gamut-sys/generate.py +180 -0
- bijective_gamut-2.1.0/rust/gamut-sys/src/lib.rs +166 -0
- bijective_gamut-2.1.0/src/capi.cpp +1560 -0
- bijective_gamut-2.1.0/src/chunk.cpp +58 -0
- bijective_gamut-2.1.0/src/chunk.hpp +43 -0
- bijective_gamut-2.1.0/src/chunks.cpp +668 -0
- bijective_gamut-2.1.0/src/codec.cpp +253 -0
- bijective_gamut-2.1.0/src/codec.hpp +36 -0
- bijective_gamut-2.1.0/src/copy.cpp +485 -0
- bijective_gamut-2.1.0/src/copy.hpp +32 -0
- bijective_gamut-2.1.0/src/evaluate.cpp +477 -0
- bijective_gamut-2.1.0/src/evaluate.hpp +115 -0
- bijective_gamut-2.1.0/src/h5.cpp +1452 -0
- bijective_gamut-2.1.0/src/h5.hpp +345 -0
- bijective_gamut-2.1.0/src/names.cpp +157 -0
- bijective_gamut-2.1.0/src/names.hpp +49 -0
- bijective_gamut-2.1.0/src/nearest.cpp +164 -0
- bijective_gamut-2.1.0/src/nearest.hpp +49 -0
- bijective_gamut-2.1.0/src/reader.cpp +849 -0
- bijective_gamut-2.1.0/src/reader.hpp +95 -0
- bijective_gamut-2.1.0/src/schema.cpp +86 -0
- bijective_gamut-2.1.0/src/schema.hpp +143 -0
- bijective_gamut-2.1.0/src/select.cpp +314 -0
- bijective_gamut-2.1.0/src/select.hpp +23 -0
- bijective_gamut-2.1.0/src/sha256.cpp +150 -0
- bijective_gamut-2.1.0/src/sha256.hpp +40 -0
- bijective_gamut-2.1.0/src/types.cpp +152 -0
- bijective_gamut-2.1.0/src/types.hpp +133 -0
- bijective_gamut-2.1.0/src/units.cpp +316 -0
- bijective_gamut-2.1.0/src/units.hpp +14 -0
- bijective_gamut-2.1.0/src/validate.cpp +1687 -0
- bijective_gamut-2.1.0/src/validate.hpp +38 -0
- bijective_gamut-2.1.0/src/value.cpp +136 -0
- bijective_gamut-2.1.0/src/value.hpp +78 -0
- bijective_gamut-2.1.0/src/writer.cpp +1923 -0
- bijective_gamut-2.1.0/src/writer.hpp +197 -0
- bijective_gamut-2.1.0/tests/header_tests.cpp +543 -0
- bijective_gamut-2.1.0/tests/unit_tests.cpp +466 -0
- bijective_gamut-2.1.0/tests/writer_reader_tests.cpp +1198 -0
- bijective_gamut-2.1.0/tools/build_core.sh +30 -0
- bijective_gamut-2.1.0/tools/build_deps.sh +87 -0
- bijective_gamut-2.1.0/tools/damaged_files.py +284 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.pyc
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
wheelhouse/
|
|
7
|
+
.pytest_cache/
|
|
8
|
+
.mypy_cache/
|
|
9
|
+
.ruff_cache/
|
|
10
|
+
|
|
11
|
+
# Build trees and built libraries
|
|
12
|
+
build/
|
|
13
|
+
build-*/
|
|
14
|
+
python/gamut/_native/
|
|
15
|
+
matlab/*.mex*
|
|
16
|
+
|
|
17
|
+
# Julia
|
|
18
|
+
julia/Manifest.toml
|
|
19
|
+
|
|
20
|
+
# Files the tests and benchmarks write
|
|
21
|
+
*.gamut
|
|
22
|
+
!docs/**/*.gamut
|
|
23
|
+
bench/results/raw/
|
|
24
|
+
|
|
25
|
+
.DS_Store
|
|
26
|
+
matlab/libgamut.*
|
|
27
|
+
matlab/gamut.dll
|
|
28
|
+
matlab/gamut.lib
|
|
29
|
+
*.mltbx
|
|
30
|
+
site/
|
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 2.1.0 (2026-10-30)
|
|
4
|
+
|
|
5
|
+
**Upgrading from 2.0.** The file format (`gamut/1`) is unchanged, and the
|
|
6
|
+
C interface only adds calls, so programs written for 2.0 build and run
|
|
7
|
+
against 2.1; what `evaluate` now does differently is listed below. Install
|
|
8
|
+
`bijective-gamut` where you installed `gamut-format`.
|
|
9
|
+
|
|
10
|
+
- The Python package is now `bijective-gamut` on PyPI, and the Rust crates
|
|
11
|
+
are `bijective-gamut` and `bijective-gamut-sys`; the code still says
|
|
12
|
+
`import gamut` and `gamut::`. `gamut-format`, the name through 2.0.0,
|
|
13
|
+
installs `bijective-gamut` at the same version, extras included.
|
|
14
|
+
- Rust: the `bijective-gamut` crate (`gamut` in code), over
|
|
15
|
+
`bijective-gamut-sys`, the declarations of `gamut.h` that
|
|
16
|
+
`rust/gamut-sys/generate.py` writes from it. It writes and reads
|
|
17
|
+
data files with the calls of the other languages, evaluates model files
|
|
18
|
+
whose models are the built-in nearest neighbour, and is held to the
|
|
19
|
+
corpus. Every example has a Rust tab, and the reference a Rust page.
|
|
20
|
+
The crates are added from this repository's release tag (not yet from
|
|
21
|
+
crates.io).
|
|
22
|
+
- `related` in every language, read in the core: what describes a result
|
|
23
|
+
(its standard deviation, quantiles and derivatives, and the parts of its
|
|
24
|
+
vector) comes from `gamut_related` in the C interface. MATLAB
|
|
25
|
+
(`study.related(name)`) and C++ (`study.related(name)`, a
|
|
26
|
+
`gamut::Related`) gain it, and Python's and Julia's now read it from the
|
|
27
|
+
core, so the four agree.
|
|
28
|
+
- `evaluate` is one implementation, in the core: the `gamut_evaluation_*`
|
|
29
|
+
calls check the inputs (E2, E5, E9, and the W4 finding, with one
|
|
30
|
+
message in every language), run the built-in models and write the data
|
|
31
|
+
file. Python, MATLAB and Julia only call the registered model types, and
|
|
32
|
+
a C program can evaluate a model file itself. `gamut_evaluate_begin`
|
|
33
|
+
stays for 2.0 callers. Along the way:
|
|
34
|
+
- coordinates given to `evaluate` are the data file's, per run, as
|
|
35
|
+
section 7.4 says; they were dropped for a model file whose mesh is
|
|
36
|
+
fixed;
|
|
37
|
+
- an int64 input past 2^53 is compared with its bounds exactly and
|
|
38
|
+
evaluated, where it was refused (E4);
|
|
39
|
+
- W4 says in how many rows an input lies outside, the same in every
|
|
40
|
+
language;
|
|
41
|
+
- a model file whose results read one model output at two widths (a
|
|
42
|
+
scalar and a field) is refused (E17) before any model runs;
|
|
43
|
+
- in Python, a complex, boolean or text input or coordinate is refused
|
|
44
|
+
(E4), where it was cast.
|
|
45
|
+
|
|
46
|
+
## 2.0.0 (2026-10-29)
|
|
47
|
+
|
|
48
|
+
**Upgrading from 1.3.** The file format is unchanged (`gamut/1`), so
|
|
49
|
+
files move between 1.3 and 2.0, short of the new limits below. Code
|
|
50
|
+
written for 1.3 can stop at, or behave differently after, these changes;
|
|
51
|
+
the fixes further down also correct results that were wrong, such as a
|
|
52
|
+
`select` on int64 values past 2⁵³.
|
|
53
|
+
|
|
54
|
+
- Creating a file never replaces an existing one: `create`,
|
|
55
|
+
`create_model`, `extract`, `write_copy`, `evaluate` with a path,
|
|
56
|
+
`gamut build`, `gamut-vtk import`, and `gamut get` without `--force`.
|
|
57
|
+
- A parameter is float64 unless declared int64; integers given to it no
|
|
58
|
+
longer make it int64.
|
|
59
|
+
- In MATLAB, `[]` as rows, nodes or cells selects none; `':'`, or leaving
|
|
60
|
+
them out, selects every one.
|
|
61
|
+
- Input that was stored wrongly is refused: a field given sites × runs (a
|
|
62
|
+
square array cannot be told apart, and is taken as runs × sites), a
|
|
63
|
+
label given `None` or NaN, a fractional, out-of-range, text or boolean
|
|
64
|
+
value for an int64 model input, an integer that float64 would round, a
|
|
65
|
+
group member given two different geometries in one batch, and a
|
|
66
|
+
negative `--rows` position on the command line.
|
|
67
|
+
- A file whose category tables together, or a model whose arrays
|
|
68
|
+
together, pass the cap one table or array had is refused (E26) when it
|
|
69
|
+
is opened or read; 1.3 allocated for them without limit.
|
|
70
|
+
- In C, every array argument comes with its length, and every array the
|
|
71
|
+
core fills with its capacity: `gamut_batch_values` takes `first_axis`;
|
|
72
|
+
`gamut_geometry_points`, `gamut_geometry_cells`, `gamut_tree_set_array`
|
|
73
|
+
and `gamut_nearest_call` take counts; `gamut_shape` and
|
|
74
|
+
`gamut_tree_array_info` take `rank, shape, capacity`.
|
|
75
|
+
`gamut_units_parse` is `gamut_units_valid`.
|
|
76
|
+
- On the command line, every failure under `--json` is
|
|
77
|
+
`{"problems": [{"rule", "path", "message"}]}`; it was `{"error": {...}}`
|
|
78
|
+
or `{"problems": [{"key", "message"}]}` by command, and argparse's own
|
|
79
|
+
mistakes printed text (`validate` still prints its findings). Success
|
|
80
|
+
documents no longer carry an empty `problems`. A misused `gamut get`
|
|
81
|
+
exits 2 before creating its output.
|
|
82
|
+
- In Python, `related(name)["vector"]` lists the vector's parts only, as
|
|
83
|
+
Julia did; a statistic or derivative of a part that carries the same
|
|
84
|
+
`vector` was listed among them.
|
|
85
|
+
- Python 3.11 or later is required (3.10 reaches its end of life this
|
|
86
|
+
month), NumPy 1.26 or later (it was 1.24, never tested) and Julia 1.10
|
|
87
|
+
or later (it was 1.9, never tested). MATLAB R2023b or later is
|
|
88
|
+
unchanged. Each minimum is now tested, with Python 3.14 too, and **none
|
|
89
|
+
of them rises within 2.x**.
|
|
90
|
+
- In C++, `get_parameter<T>` and `get_constant<T>` name the type: `double`,
|
|
91
|
+
or `int64_t` for a value declared int64. Another type is refused rather
|
|
92
|
+
than rounded or truncated.
|
|
93
|
+
- Two rules each split in two: appending to a file whose growable
|
|
94
|
+
dimensions are fixed is E25 (it was E20, which keeps storage the format
|
|
95
|
+
forbids), and a limit reached is E26 (it was E23, which keeps storage a
|
|
96
|
+
reader cannot read). Code that tests for E20 or E23 checks the new
|
|
97
|
+
codes too.
|
|
98
|
+
|
|
99
|
+
The rest of this section says what changed and why.
|
|
100
|
+
|
|
101
|
+
Creating a Gamut file never replaces an existing one. `create`,
|
|
102
|
+
`create_model`, `evaluate`, `extract` and `write_copy`, in every language
|
|
103
|
+
and in C, `gamut build` and `gamut-vtk import` refuse a destination that
|
|
104
|
+
exists and leave it as it was; 1.3.0 and earlier replaced it. A script that
|
|
105
|
+
writes the same name on each run now stops at that refusal on its second
|
|
106
|
+
run: choose a new name, or remove the previous output yourself first. To
|
|
107
|
+
add runs to an existing file in Python, open it with
|
|
108
|
+
`gamut.open(path, mode="a")`. A `gamut build` that failed part way used to
|
|
109
|
+
delete the file it had replaced.
|
|
110
|
+
|
|
111
|
+
A parameter is float64 unless declared int64 (`"dtype": "int64"`; in C++,
|
|
112
|
+
`declare_parameter<int64_t>`). 1.3.0 and earlier stored an undeclared
|
|
113
|
+
parameter as int64 when its first values were integers, so a Python sweep
|
|
114
|
+
begun with `range(0, 11, 2)` refused 2.5 when appended to, and the same
|
|
115
|
+
program wrote int64 from Python and float64 from MATLAB. Files are
|
|
116
|
+
unchanged: an int64 parameter stays valid and reads as before. Code that
|
|
117
|
+
gives integers to an undeclared parameter now writes float64; declare
|
|
118
|
+
int64 where whole numbers are meant.
|
|
119
|
+
|
|
120
|
+
A refused replacement of an attribute (a value of about 64 KiB or more)
|
|
121
|
+
keeps the attribute's previous value. Before, the previous value was
|
|
122
|
+
deleted first, so the refusal lost it.
|
|
123
|
+
|
|
124
|
+
A field given the wrong way round, sites × runs instead of runs × sites,
|
|
125
|
+
is refused. It was stored scrambled, each run holding a mix of every run's
|
|
126
|
+
values. The C interface's `gamut_batch_values` takes a new `first_axis`
|
|
127
|
+
argument, the length of the caller's first axis or 0 for a flat array,
|
|
128
|
+
and the core refuses a first axis that is not the batch's runs. A square
|
|
129
|
+
array cannot be told apart and is taken as runs × sites.
|
|
130
|
+
|
|
131
|
+
A label given as `None` or NaN in Python is refused (E5); it used to
|
|
132
|
+
become a category named "None" or "nan". A category name that is not valid
|
|
133
|
+
UTF-8 is refused when the file opens (E4), in every language, where Python
|
|
134
|
+
raised `UnicodeDecodeError`. A position above 2⁶³ − 1 is reported as given
|
|
135
|
+
rather than wrapped to a negative number. `get_points` on a file with no
|
|
136
|
+
mesh says so, in every language, instead of asking for a row.
|
|
137
|
+
|
|
138
|
+
A group member given two different geometries in one batch is refused by
|
|
139
|
+
the core, in every language. MATLAB kept the first geometry silently.
|
|
140
|
+
|
|
141
|
+
`open` with a refused `threads` value closes the file it opened, in Python
|
|
142
|
+
and MATLAB; the file stayed open, and a later append to it failed. MATLAB's
|
|
143
|
+
`'compression', false` works; it always failed with an unknown command.
|
|
144
|
+
|
|
145
|
+
Appending two files in which one name is a parameter in one and a label
|
|
146
|
+
in the other is refused; it crashed the process. A `where` text with
|
|
147
|
+
parentheses nested deeper than 64 levels is refused; deep enough nesting
|
|
148
|
+
overflowed the stack and crashed. Appending to a file whose label codes
|
|
149
|
+
are int64, as §10.1 allows another writer to store them, works; it failed
|
|
150
|
+
with an internal error and left the file's columns of different lengths.
|
|
151
|
+
|
|
152
|
+
Values read from the file that locate others (a changing mesh's run
|
|
153
|
+
starts and instance numbers, cell offsets) are range-checked before any
|
|
154
|
+
arithmetic on them, so a damaged file is refused or reported (E21, E10)
|
|
155
|
+
instead of overflowing into wrong answers. `select` compares an int64
|
|
156
|
+
parameter or scalar exactly with an integer literal, one outside int64's
|
|
157
|
+
range included; as doubles, values above 2⁵³ that differ compared equal.
|
|
158
|
+
Appending a changing mesh's geometry to int64 mesh labels takes codes
|
|
159
|
+
beyond int32. BitRound no longer rounds a value near the largest finite float up
|
|
160
|
+
to infinity. Names are checked against ASCII letters and digits whatever
|
|
161
|
+
locale the host program has set.
|
|
162
|
+
|
|
163
|
+
E20 and E23 each meant two things a caller handles differently. E20 is
|
|
164
|
+
now storage the format forbids, and appending to a valid file whose
|
|
165
|
+
growable dimensions are fixed is E25: the file is read-only, and
|
|
166
|
+
`write_copy` makes one that grows. E23 is now an object a reader cannot
|
|
167
|
+
read (a chunk index it cannot use, a chunk of the wrong size, a filter
|
|
168
|
+
that fails), and E26 is a count past one of the caps in
|
|
169
|
+
`docs/security.md`. SPEC.md, the corpus and every language follow.
|
|
170
|
+
|
|
171
|
+
The C interface states the length of every array it reads and the
|
|
172
|
+
capacity of every array it fills, so the core checks what a binding passes
|
|
173
|
+
instead of trusting it: the points' count, the offsets' count (one more
|
|
174
|
+
than the cells), a model array's count against its shape, and each
|
|
175
|
+
nearest-model column's length. `gamut_shape` and `gamut_tree_array_info`
|
|
176
|
+
take the rank, the shape and the shape's capacity, in that order, and
|
|
177
|
+
refuse a capacity below the rank. `gamut_units_parse` is renamed
|
|
178
|
+
`gamut_units_valid`, which says what its 1 means; it is the one function
|
|
179
|
+
that does not return 0 on success.
|
|
180
|
+
|
|
181
|
+
A group reachable by more than one hard link is walked once; a chain of
|
|
182
|
+
such groups made validation, append, extract and `write_copy` take time
|
|
183
|
+
that doubled with every level. `/private` may hold them, as §8 lets it
|
|
184
|
+
hold any structure; model data, a tree, refuses them (E18). The category
|
|
185
|
+
tables read when a file opens share one budget, the cap one table had, and
|
|
186
|
+
so does validation; a model's arrays read together share one budget, the
|
|
187
|
+
cap one array had (E26). Many tables, or many arrays, each under its own
|
|
188
|
+
cap could make opening a file, or reading or validating a model,
|
|
189
|
+
allocate gigabytes.
|
|
190
|
+
|
|
191
|
+
The command line reports malformed input as `error: …` with the exit
|
|
192
|
+
status the guide gives instead of a Python traceback: a build or append
|
|
193
|
+
document that is not JSON exits 1, and `--zone` without `=`, a zone name
|
|
194
|
+
not in the label's table, or a `--rows` position outside the file exits 2.
|
|
195
|
+
`--rows` positions are checked by the core for every output type; for a
|
|
196
|
+
`.gamut` output, `-1` meant the last row.
|
|
197
|
+
|
|
198
|
+
The validator checks what SPEC.md says and it did not: a model-data
|
|
199
|
+
array without its dimension scale (E19); an integer that is not int64 or
|
|
200
|
+
a number that is not float64 in model data (E18, which reading model data
|
|
201
|
+
now refuses too); a string attribute stored wider than its UTF-8 bytes
|
|
202
|
+
(E3); a derivative with respect to `coordinates` along an axis the
|
|
203
|
+
coordinates do not have (E13); and the vector and axis of a result whose
|
|
204
|
+
statistic this version does not define. A required object or attribute
|
|
205
|
+
that is absent is E2, whatever rule its contents would break: a label's
|
|
206
|
+
`category`, the mesh's `kind` and `support_id`, a changing mesh's
|
|
207
|
+
`instance_nodes`, the cell arrays. A table's repeated entry is reported
|
|
208
|
+
once.
|
|
209
|
+
|
|
210
|
+
SPEC.md settles where an independent reader could diverge. A units string
|
|
211
|
+
holding a space outside ASCII, such as a no-break space pasted from a web
|
|
212
|
+
page, does not parse (E7). Names use ASCII letters and digits. A scale's
|
|
213
|
+
encoding is what a writer produces, not a rule. A root dataset without a
|
|
214
|
+
`role` is ignored (W5). A file whose growable dimensions are fixed, as
|
|
215
|
+
some netCDF writers leave them, is valid and read-only: appending says so
|
|
216
|
+
and points to `write_copy`, which makes a copy that grows.
|
|
217
|
+
|
|
218
|
+
The corpus requires each rule case to break exactly its rules, in Python,
|
|
219
|
+
Julia and MATLAB; it accepted any extra finding. Its rule cases no longer
|
|
220
|
+
break rules by accident, every valid data file is appended to itself, and
|
|
221
|
+
it gains a valid file of int64 columns and hostile files of several large
|
|
222
|
+
tables and a deeply nested `where`.
|
|
223
|
+
|
|
224
|
+
MATLAB takes `"double-quoted"` strings wherever it takes text, notes and
|
|
225
|
+
model numbers included, refuses sparse arrays, and takes cell arrays given
|
|
226
|
+
as any integer class, read by their class; integer arrays
|
|
227
|
+
that were not double were read as int64, past the end of their buffer.
|
|
228
|
+
An open study keeps the MATLAB gateway loaded, so `clear mex` no longer
|
|
229
|
+
orphans it, and MATLAB finishes open files when it exits. A refused call
|
|
230
|
+
frees what it built before MATLAB reports the error. In Julia, the
|
|
231
|
+
library's symbols are looked up under a lock from any thread, and a study
|
|
232
|
+
left to the garbage collector removes its temporary file as `close` does.
|
|
233
|
+
|
|
234
|
+
The guide and SPEC.md say what an append costs when it is interrupted: a
|
|
235
|
+
batch refused for its own data leaves the file as it was, every such
|
|
236
|
+
check being made before the first write, but a write that fails or a
|
|
237
|
+
process killed while it appends can leave the whole file unreadable. Keep a copy of a file you cannot regenerate, or append each
|
|
238
|
+
job's own file at the end.
|
|
239
|
+
|
|
240
|
+
In MATLAB, `[]` as rows, nodes or cells selects none and `':'` (or leaving
|
|
241
|
+
them out) every one; `[]` meant every row, so the rows of a `select` that
|
|
242
|
+
matched nothing read the whole file. MATLAB passes integer arrays to the
|
|
243
|
+
core in their own class, so int64 codes above 2⁵³ are not rounded through
|
|
244
|
+
double. An int64 model input given fractional or out-of-range values is
|
|
245
|
+
refused in Python and MATLAB, as in Julia, instead of being truncated; an
|
|
246
|
+
unsigned array above int64's range is refused instead of wrapping. Julia's
|
|
247
|
+
`open` closes the file when its `threads` value is refused. An integer
|
|
248
|
+
written to a float64 parameter, scalar, field or coordinate, or given to a
|
|
249
|
+
float64 model input, that a float64 cannot hold exactly (past 2⁵³) is
|
|
250
|
+
refused (E4) in every language; it was rounded silently. Opening a file that does not exist says so, rather than
|
|
251
|
+
that it is not HDF5. A column with the wrong number of values names it and
|
|
252
|
+
both counts. A Python `Study` prints its path, kind, type and row count.
|
|
253
|
+
|
|
254
|
+
A group member repeated with the same values in another shape (4 × 2 and
|
|
255
|
+
2 × 4 points) is refused as a different geometry. Replacing an attribute
|
|
256
|
+
works however many one-character names the object already holds. The
|
|
257
|
+
validator checks a model-data scale's extent and attribute creation order
|
|
258
|
+
as it checks every scale. A `where` nested past the cap is E26, as the
|
|
259
|
+
limits in `docs/security.md` are. `gamut get` refuses to write over an
|
|
260
|
+
existing `.csv`, `.npy` or `.npz` output unless given `--force`, and
|
|
261
|
+
checks `--rows` without reading the rows.
|
|
262
|
+
|
|
263
|
+
A file whose `DIMENSION_LIST` attribute is not HDF5's list of dimension
|
|
264
|
+
scales (an integer, a string) is reported (E19) instead of crashing
|
|
265
|
+
`validate` and `open`: HDF5's scale calls follow the attribute as
|
|
266
|
+
references, and Gamut now checks its type before they read it.
|
|
267
|
+
|
|
268
|
+
## 1.3.0 (2026-10-25)
|
|
269
|
+
|
|
270
|
+
A file may carry a portion marking: a new optional root attribute,
|
|
271
|
+
`portion_marking`, holding free text the model's author set, which the
|
|
272
|
+
format does not interpret (specification sections 8 and 10.1). `evaluate`
|
|
273
|
+
copies it into every file evaluated from a model file that has one
|
|
274
|
+
(section 7.4), and `extract` and `write_copy` keep it. It is set with
|
|
275
|
+
`create` and `create_model` in Python (`portion_marking`), MATLAB
|
|
276
|
+
(`'portionMarking'`) and Julia (`portion_marking`), with `set_attribute`
|
|
277
|
+
in C and C++, and with the `portion_marking` key of a `gamut build`
|
|
278
|
+
document; `gamut info` shows it. Readers of earlier versions report it as
|
|
279
|
+
an attribute this version does not define (W5) and go on; they do not copy
|
|
280
|
+
it into an evaluation or a copy, and their writers refuse it.
|
|
281
|
+
|
|
282
|
+
## 1.2.0 (2026-10-23)
|
|
283
|
+
|
|
284
|
+
A file can no longer make a reader or a writer touch another path. A
|
|
285
|
+
dataset whose values HDF5 keeps outside the file, through external storage
|
|
286
|
+
or as a virtual dataset, passed validation and was read from that other
|
|
287
|
+
file (a constant as soon as the file opened), and appending wrote to it,
|
|
288
|
+
creating it if need be. Such a dataset is now an error (E22): the
|
|
289
|
+
validator reports it, opening refuses the file, and appending refuses it
|
|
290
|
+
before the file is opened to write. Appending also refuses a run-table
|
|
291
|
+
column that cannot grow (E20), before writing anything.
|
|
292
|
+
|
|
293
|
+
A read is refused (E23) before any buffer is sized when it would return
|
|
294
|
+
more than 2³⁰ values (8 GiB at 8 bytes a value); a file declaring billions
|
|
295
|
+
of rows made the Python, Julia and MATLAB layers allocate for them first.
|
|
296
|
+
The threaded reader no longer reads past its arrays when a field has fewer
|
|
297
|
+
axes than its selections.
|
|
298
|
+
|
|
299
|
+
The core reads compressed datasets itself, a chunk at a time, with one
|
|
300
|
+
path for one thread or many, and HDF5 is never handed a chunk it has not
|
|
301
|
+
checked. A chunk that is not stored whole, or that gzip does not inflate
|
|
302
|
+
to its full size, is refused (E23) on reading and before appending
|
|
303
|
+
rewrites it; HDF5 trusts these sizes, and a crafted chunk made it read past
|
|
304
|
+
its memory and return what lay there, or write past it when appending. A
|
|
305
|
+
filter other than gzip and shuffle is not read (E20). Reading a few
|
|
306
|
+
scattered rows or nodes is several times faster, up to twenty times on a
|
|
307
|
+
million-row table, and other reads and writes take the time they did.
|
|
308
|
+
Building the core needs HDF5 1.14 or later (every package carries 1.14.6).
|
|
309
|
+
|
|
310
|
+
MATLAB installs from a toolbox, `Gamut.mltbx`, attached to each release:
|
|
311
|
+
MATLAB R2023b or later on Linux, Windows or an Apple silicon Mac, and no
|
|
312
|
+
compiler. Each release also carries the core on its own for each platform
|
|
313
|
+
(`gamut-core-<version>-<platform>`, with its headers and CMake package) for
|
|
314
|
+
Julia, C and C++. Every package now includes the licences of HDF5 and zlib,
|
|
315
|
+
which the core links in.
|
|
316
|
+
|
|
317
|
+
Evaluating a damaged model file is refused in every language when the file
|
|
318
|
+
lacks what evaluation reads: a nearest model's numbers (`columns`, `lower`,
|
|
319
|
+
`upper`, `x`, `outputs`; E17), or a declaration such as a parameter's
|
|
320
|
+
bounds, a result's model or output, or a model's type or inputs. Python
|
|
321
|
+
and Julia raised a key error and MATLAB a field error instead. In Python
|
|
322
|
+
and Julia, `related` likewise refuses a quantile without its `q`.
|
|
323
|
+
|
|
324
|
+
Calls from several threads no longer crash. HDF5 keeps state for the whole
|
|
325
|
+
process and is built without its thread-safety option, so two threads each
|
|
326
|
+
reading its own file could bring it down; every call that reaches a file
|
|
327
|
+
now takes its turn. `threads=` remains the way to read one large field
|
|
328
|
+
faster.
|
|
329
|
+
|
|
330
|
+
Appending from C, C++ or MATLAB refuses a file of another major format
|
|
331
|
+
version, as reading already did, and does so before opening the file to
|
|
332
|
+
write, which leaves it byte for byte as it was.
|
|
333
|
+
|
|
334
|
+
Messages say more about what to fix. A number left as text (`"4.0"` from
|
|
335
|
+
a spreadsheet) is named as such, rather than reported as a missing value;
|
|
336
|
+
an illegal name is refused with what a legal one is; W2 counts the rows
|
|
337
|
+
of each status and says when no status is named `converged`, the one
|
|
338
|
+
name that marks a run as usable; W1 names the group a split divides.
|
|
339
|
+
|
|
340
|
+
The languages agree in more places. `gamut_create` from C, or
|
|
341
|
+
`gamut::create` from C++, with a mesh and no `per` is refused (E6), as
|
|
342
|
+
Python, MATLAB and Julia already refused it, where it took `fixed`. Cell
|
|
343
|
+
offsets of the wrong length are refused (E5) in Python, MATLAB and Julia
|
|
344
|
+
before the core reads them, as the C++ layer already did, and so are a
|
|
345
|
+
nearest model's inputs of unequal length. A parameter given only one of
|
|
346
|
+
`lower` and `upper` is refused (E16) in Python, MATLAB and Julia, where the
|
|
347
|
+
other was taken as 0 or the one given was dropped. An evaluation that
|
|
348
|
+
fails leaves no file behind, in every language. In Julia, a `true` note is stored as the integer 1,
|
|
349
|
+
and `related` keys a derivative along each coordinate axis separately, as
|
|
350
|
+
in Python.
|
|
351
|
+
|
|
352
|
+
A run table from a spreadsheet goes straight in: Python's `add_runs`
|
|
353
|
+
takes a pandas DataFrame and `add_run` one of its rows, and MATLAB's
|
|
354
|
+
`addRuns` takes a table, as `readtable` gives, with labels as text or
|
|
355
|
+
categorical.
|
|
356
|
+
|
|
357
|
+
The README and the guide start from the simplest study, a table of runs,
|
|
358
|
+
shown in Python, MATLAB, Julia and C++, and step up to a mesh from there;
|
|
359
|
+
the guide opens on the pattern every file follows and lists its pages by
|
|
360
|
+
what a study has. It shows how to publish a train and test split, says
|
|
361
|
+
that only `converged` marks a run as usable, that a new category name
|
|
362
|
+
extends a label's categories, and that unit names are checked only when a
|
|
363
|
+
read converts them. MATLAB can read a file with `ncread` and nothing
|
|
364
|
+
installed. Installing covers xarray, Xcode for MATLAB on macOS, and adds
|
|
365
|
+
the Julia package at the release tag that matches the wheel.
|
|
366
|
+
|
|
367
|
+
`help(gamut)` opens on the whole pattern, `help(gamut.create)` documents
|
|
368
|
+
every declaration, and `llms.txt` gives coding agents what Gamut is for,
|
|
369
|
+
the pattern, the mistakes that are easy to make, and every page.
|
|
370
|
+
|
|
371
|
+
A model in a file has its own step in the README and leads the models
|
|
372
|
+
page: a quadratic, of a model type defined in a few lines, is fitted in
|
|
373
|
+
Python to the README's sweep, saved, and evaluated in MATLAB by a MATLAB
|
|
374
|
+
class of the same type, with no Python involved; the built-in nearest
|
|
375
|
+
model is evaluated from Julia and C++. `help(gamut.create_model)` and
|
|
376
|
+
`llms.txt` show the whole model pattern and its easy mistakes, and
|
|
377
|
+
`help gamut.registerModelType` in MATLAB says what `call` receives and
|
|
378
|
+
returns. A model's `save` that returns an object which is not model data
|
|
379
|
+
is refused with the object named, where Python reported a
|
|
380
|
+
zero-dimensional array; so are a list mixing text with other values and
|
|
381
|
+
text with more than one axis, which Python flattened into strings (E18).
|
|
382
|
+
`gamut info` names the studies a model was trained on, and a model's
|
|
383
|
+
one-line description. `bench/scaling.py` times writes and reads as run
|
|
384
|
+
tables, one mesh and a mesh per run grow.
|
|
385
|
+
|
|
386
|
+
An answer from `evaluate` says, row by row, which inputs lie outside the
|
|
387
|
+
range the model was fitted on, and how far. The answer already kept the
|
|
388
|
+
model's bounds on its parameters; `outside(name)` measures each row's
|
|
389
|
+
value against them, in the parameter's units: 0 inside or on a bound,
|
|
390
|
+
negative below, positive above, NaN for a missing value. A parameter with
|
|
391
|
+
no bounds is refused rather than reported as inside. The core computes it
|
|
392
|
+
once (`gamut_outside` in C) for any data file, and it is `study.outside`
|
|
393
|
+
in Python, MATLAB and C++ and `Gamut.outside` in Julia. `evaluate` still
|
|
394
|
+
warns as well (W4). The models page and the MATLAB, Julia and C++ pages
|
|
395
|
+
show the call.
|
|
396
|
+
|
|
397
|
+
The validator holds a mesh's computed coordinates to the writer's rules: a
|
|
398
|
+
computing model only in a model file whose mesh is per run, which must
|
|
399
|
+
name one (E16), and that model existing with its output named (E17). The
|
|
400
|
+
writer now also refuses a computing model without an output. An array
|
|
401
|
+
holding more values than the validator reads, 2²⁶, is now reported as
|
|
402
|
+
unchecked (W11) rather than passed over in silence.
|
|
403
|
+
|
|
404
|
+
Opening a file takes about a third less time (0.81 to 0.51 ms on the
|
|
405
|
+
benchmarks' capsule study): the reader opens each object once, for its
|
|
406
|
+
attributes, type and shape together, and leaves the storage settings to the
|
|
407
|
+
validator.
|
|
408
|
+
|
|
409
|
+
## 1.1.0 (2026-10-19)
|
|
410
|
+
|
|
411
|
+
A model file whose coordinates are computed by a model now evaluates. The
|
|
412
|
+
reader looked for the model's name only where coordinates are a group,
|
|
413
|
+
and section 10.3 stores them as a zero-length dataset, so evaluation asked
|
|
414
|
+
the caller for coordinates instead, and a copy of the file lost which
|
|
415
|
+
model computes them. The format is unchanged.
|
|
416
|
+
|
|
417
|
+
`study.model_numbers(id)` reads a model's stored numbers back as the
|
|
418
|
+
dictionary its `save` gave, whether or not its type is registered:
|
|
419
|
+
`modelNumbers` in MATLAB and `Gamut.model_numbers` in Julia. C programs
|
|
420
|
+
already had `gamut_model_numbers`.
|
|
421
|
+
|
|
422
|
+
## 1.0.0 (2026-10-18)
|
|
423
|
+
|
|
424
|
+
Version 1 of the format, `gamut/1`, and a new implementation. Before
|
|
425
|
+
version 1 the format and its libraries were called mestra (the 0.1 series
|
|
426
|
+
on PyPI).
|
|
427
|
+
|
|
428
|
+
The format ([SPEC.md](https://bijective-systems-org.github.io/gamut/reference/specification.html)) is redesigned around the study: a run
|
|
429
|
+
table with roles, statuses and splits; a shared mesh whose geometry is
|
|
430
|
+
fixed, per run or per member of a family, or a mesh per run; fields as one
|
|
431
|
+
block of runs × nodes; uncertainty and derivatives as relations; and model
|
|
432
|
+
files of the same shape as data. Files of the earlier `mestra/0` are
|
|
433
|
+
refused with a message naming their version.
|
|
434
|
+
|
|
435
|
+
A C++ core with a C interface implements the format, and Python, MATLAB
|
|
436
|
+
and Julia are layers over it, so every language gives the same answers.
|
|
437
|
+
The Python wheels link HDF5 statically, so the core never meets another
|
|
438
|
+
HDF5 in the same process.
|
|
439
|
+
|
|
440
|
+
Python has `create`, `open`, `append`, `extract`, `write_copy`,
|
|
441
|
+
`validate`, `create_model` and `evaluate`; the `gamut` command line
|
|
442
|
+
(`info`, `validate`, `build`, `append`, `get`); and `gamut-vtk`, which
|
|
443
|
+
turns a folder of VTK runs into a study and back. MATLAB, Julia and C++
|
|
444
|
+
(the header-only `gamut/gamut.hpp`) have the same calls in each language's
|
|
445
|
+
spelling, and C programs use the C interface, `gamut/gamut.h`.
|
|
446
|
+
|
|
447
|
+
Readers check sizes before using memory, never follow links and load no
|
|
448
|
+
HDF5 plugins ([security](https://bijective-systems-org.github.io/gamut/security.html)). The guide has fourteen pages,
|
|
449
|
+
each held to a runnable example by the tests, and the
|
|
450
|
+
[benchmarks](https://bijective-systems-org.github.io/gamut/benchmarks.html) cover two studies.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: >-
|
|
3
|
+
If you use Gamut in published work, please cite it as below, and
|
|
4
|
+
name the format version your files declare (the root attribute
|
|
5
|
+
`format`, for example "gamut/1").
|
|
6
|
+
title: "Gamut: an open file format for simulation studies"
|
|
7
|
+
type: software
|
|
8
|
+
version: 2.1.0
|
|
9
|
+
date-released: 2026-10-30
|
|
10
|
+
authors:
|
|
11
|
+
- name: "Bijective Systems"
|
|
12
|
+
repository-code: "https://github.com/bijective-systems-org/gamut"
|
|
13
|
+
url: "https://github.com/bijective-systems-org/gamut"
|
|
14
|
+
license: Apache-2.0
|
|
15
|
+
abstract: >-
|
|
16
|
+
Gamut is an open file format for the results of a parametric
|
|
17
|
+
simulation study and the models fitted to it. One HDF5 file, laid
|
|
18
|
+
out as a valid netCDF-4 file, holds the run table (parameters with
|
|
19
|
+
roles, labels, statuses, splits and scalar results, every number with
|
|
20
|
+
its units), the mesh the fields live on, the fields themselves as one
|
|
21
|
+
block of runs by nodes when the runs share a mesh, uncertainty and
|
|
22
|
+
gradients beside the values they describe, and fitted models in the
|
|
23
|
+
same terms as the data. One C++ core, with Python, MATLAB, Julia, C and
|
|
24
|
+
Rust interfaces, implements the specification, and a shared corpus of
|
|
25
|
+
valid, broken and hostile files holds every language to it.
|
|
26
|
+
keywords:
|
|
27
|
+
- simulation data
|
|
28
|
+
- parametric study
|
|
29
|
+
- surrogate model
|
|
30
|
+
- HDF5
|
|
31
|
+
- netCDF-4
|
|
32
|
+
- mesh
|
|
33
|
+
- scientific machine learning
|
|
34
|
+
- uncertainty quantification
|