cmb-format 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.
Files changed (50) hide show
  1. cmb_format-0.1.0/.github/workflows/ci.yml +85 -0
  2. cmb_format-0.1.0/.github/workflows/release.yml +61 -0
  3. cmb_format-0.1.0/.gitignore +32 -0
  4. cmb_format-0.1.0/CHANGELOG.md +14 -0
  5. cmb_format-0.1.0/LICENSE +21 -0
  6. cmb_format-0.1.0/PKG-INFO +131 -0
  7. cmb_format-0.1.0/README.md +106 -0
  8. cmb_format-0.1.0/docs/binary-format.md +320 -0
  9. cmb_format-0.1.0/docs/discretize.md +171 -0
  10. cmb_format-0.1.0/pyproject.toml +67 -0
  11. cmb_format-0.1.0/src/cmb_format/__init__.py +58 -0
  12. cmb_format-0.1.0/src/cmb_format/_codec.py +861 -0
  13. cmb_format-0.1.0/src/cmb_format/_detect.py +32 -0
  14. cmb_format-0.1.0/src/cmb_format/_file.py +189 -0
  15. cmb_format-0.1.0/src/cmb_format/_padding.py +114 -0
  16. cmb_format-0.1.0/src/cmb_format/py.typed +0 -0
  17. cmb_format-0.1.0/tests/cases.py +183 -0
  18. cmb_format-0.1.0/tests/generate_goldens.py +35 -0
  19. cmb_format-0.1.0/tests/goldens/all_dtypes.cmb +0 -0
  20. cmb_format-0.1.0/tests/goldens/all_dtypes.header.json +120 -0
  21. cmb_format-0.1.0/tests/goldens/octree_base_padding_models.cmb +0 -0
  22. cmb_format-0.1.0/tests/goldens/octree_base_padding_models.header.json +86 -0
  23. cmb_format-0.1.0/tests/goldens/octree_embedded.cmb +0 -0
  24. cmb_format-0.1.0/tests/goldens/octree_embedded.header.json +61 -0
  25. cmb_format-0.1.0/tests/goldens/octree_rectangular_models.cmb +0 -0
  26. cmb_format-0.1.0/tests/goldens/octree_rectangular_models.header.json +76 -0
  27. cmb_format-0.1.0/tests/goldens/reference_explicit_n_cells.cmb +0 -0
  28. cmb_format-0.1.0/tests/goldens/reference_explicit_n_cells.header.json +24 -0
  29. cmb_format-0.1.0/tests/goldens/reference_models_only.cmb +0 -0
  30. cmb_format-0.1.0/tests/goldens/reference_models_only.header.json +24 -0
  31. cmb_format-0.1.0/tests/goldens/reference_with_base_mesh.cmb +0 -0
  32. cmb_format-0.1.0/tests/goldens/reference_with_base_mesh.header.json +56 -0
  33. cmb_format-0.1.0/tests/goldens/tensor_embedded.cmb +0 -0
  34. cmb_format-0.1.0/tests/goldens/tensor_embedded.header.json +47 -0
  35. cmb_format-0.1.0/tests/goldens/tensor_padding.cmb +0 -0
  36. cmb_format-0.1.0/tests/goldens/tensor_padding.header.json +55 -0
  37. cmb_format-0.1.0/tests/goldens/tensor_with_models.cmb +0 -0
  38. cmb_format-0.1.0/tests/goldens/tensor_with_models.header.json +79 -0
  39. cmb_format-0.1.0/tests/goldens/uniform_embedded.cmb +0 -0
  40. cmb_format-0.1.0/tests/goldens/uniform_embedded.header.json +38 -0
  41. cmb_format-0.1.0/tests/goldens/uniform_padding_models.cmb +0 -0
  42. cmb_format-0.1.0/tests/goldens/uniform_padding_models.header.json +61 -0
  43. cmb_format-0.1.0/tests/test_codec.py +233 -0
  44. cmb_format-0.1.0/tests/test_detect.py +71 -0
  45. cmb_format-0.1.0/tests/test_file.py +122 -0
  46. cmb_format-0.1.0/tests/test_goldens.py +134 -0
  47. cmb_format-0.1.0/tests/test_helpers.py +58 -0
  48. cmb_format-0.1.0/tests/test_octree_order.py +72 -0
  49. cmb_format-0.1.0/tests/test_public_api.py +52 -0
  50. cmb_format-0.1.0/tests/test_validation.py +570 -0
@@ -0,0 +1,85 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ${{ github.workflow }}-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ test:
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - uses: actions/setup-python@v5
23
+ with:
24
+ python-version: ${{ matrix.python-version }}
25
+ cache: pip
26
+
27
+ - name: Install
28
+ run: |
29
+ python -m pip install --upgrade pip
30
+ python -m pip install --group test -e .
31
+
32
+ - name: Test
33
+ run: python -m pytest -q
34
+
35
+ lint:
36
+ runs-on: ubuntu-latest
37
+ steps:
38
+ - uses: actions/checkout@v4
39
+
40
+ - uses: actions/setup-python@v5
41
+ with:
42
+ python-version: "3.12"
43
+ cache: pip
44
+
45
+ - name: Install
46
+ run: |
47
+ python -m pip install --upgrade pip
48
+ python -m pip install --group lint -e .
49
+
50
+ - name: Lint
51
+ run: python -m ruff check .
52
+
53
+ - name: Format
54
+ run: python -m ruff format --check .
55
+
56
+ package:
57
+ runs-on: ubuntu-latest
58
+ steps:
59
+ - uses: actions/checkout@v4
60
+
61
+ - uses: actions/setup-python@v5
62
+ with:
63
+ python-version: "3.12"
64
+ cache: pip
65
+
66
+ - name: Install build tools
67
+ run: |
68
+ python -m pip install --upgrade pip
69
+ python -m pip install build twine
70
+
71
+ - name: Build distributions
72
+ # Build the wheel from the sdist to verify both distribution paths.
73
+ run: python -m build
74
+
75
+ - name: Check distribution metadata
76
+ run: python -m twine check --strict dist/*
77
+
78
+ - name: Install wheel and test dependencies
79
+ run: python -m pip install --group test dist/*.whl
80
+
81
+ - name: Test installed wheel
82
+ run: |
83
+ cd "$RUNNER_TEMP"
84
+ python -c 'from importlib.resources import files; assert files("cmb_format").joinpath("py.typed").is_file()'
85
+ python -m pytest -q "$GITHUB_WORKSPACE/tests"
@@ -0,0 +1,61 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ jobs:
8
+ build:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+
13
+ - uses: actions/setup-python@v5
14
+ with:
15
+ python-version: "3.12"
16
+ cache: pip
17
+
18
+ - name: Install build tools
19
+ run: |
20
+ python -m pip install --upgrade pip
21
+ python -m pip install build twine
22
+
23
+ - name: Verify the tag matches the packaged version
24
+ # PyPI versions are immutable, so catch a tag/version mismatch here
25
+ # rather than after an upload that cannot be replaced.
26
+ run: |
27
+ version=$(python -c 'import tomllib; print(tomllib.load(open("pyproject.toml","rb"))["project"]["version"])')
28
+ if [ "$version" != "${GITHUB_REF_NAME#v}" ]; then
29
+ echo "::error::tag ${GITHUB_REF_NAME} does not match pyproject version ${version}"
30
+ exit 1
31
+ fi
32
+
33
+ - name: Build distributions
34
+ # Build the wheel from the sdist to verify both distribution paths.
35
+ run: python -m build
36
+
37
+ - name: Check distribution metadata
38
+ run: python -m twine check --strict dist/*
39
+
40
+ - uses: actions/upload-artifact@v4
41
+ with:
42
+ name: dist
43
+ path: dist/
44
+
45
+ publish:
46
+ needs: build
47
+ runs-on: ubuntu-latest
48
+ # Must match the pending publisher configured at
49
+ # https://pypi.org/manage/account/publishing/
50
+ environment: pypi
51
+ permissions:
52
+ # Required for Trusted Publishing; the job requests an OIDC token
53
+ # instead of holding a long-lived API token.
54
+ id-token: write
55
+ steps:
56
+ - uses: actions/download-artifact@v4
57
+ with:
58
+ name: dist
59
+ path: dist/
60
+
61
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,32 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+
9
+ # Environments (the workspace venv lives in the parent dev/ directory)
10
+ .venv/
11
+ venv/
12
+ .env
13
+
14
+ # Tooling caches
15
+ .pytest_cache/
16
+ .ruff_cache/
17
+ .mypy_cache/
18
+ .coverage
19
+ .coverage.*
20
+ htmlcov/
21
+ coverage.xml
22
+
23
+ # Editors / OS
24
+ .vscode/
25
+ .idea/
26
+ .DS_Store
27
+
28
+ # NOTE: tests/goldens/ is deliberately NOT ignored. Those committed bytes are
29
+ # the format's reference -- they are the whole point of this repository.
30
+
31
+ # Local Claude Code instructions -- not meant to be committed
32
+ /CLAUDE.md
@@ -0,0 +1,14 @@
1
+ # Changelog
2
+
3
+ Notable changes to `cmb-format`, the Python reference implementation.
4
+
5
+ This file tracks the **package**. The CMB **format** versions separately —
6
+ see `docs/binary-format.md`'s Versioning section. Which format versions a
7
+ build handles is stated in code, as `WRITTEN_FORMAT_VERSION` and
8
+ `READABLE_FORMAT_VERSIONS`.
9
+
10
+ ## [0.1.0]
11
+
12
+ Initial release.
13
+
14
+ [0.1.0]: https://github.com/dwfmarchant/cmb-format/releases/tag/v0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 David Marchant
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,131 @@
1
+ Metadata-Version: 2.5
2
+ Name: cmb-format
3
+ Version: 0.1.0
4
+ Summary: Specification and reference implementation of CMB (Cell Model Binary), a file format for cell-based meshes and the models defined on them
5
+ Project-URL: Homepage, https://github.com/dwfmarchant/cmb-format
6
+ Project-URL: Repository, https://github.com/dwfmarchant/cmb-format
7
+ Project-URL: Changelog, https://github.com/dwfmarchant/cmb-format/blob/main/CHANGELOG.md
8
+ Project-URL: Issues, https://github.com/dwfmarchant/cmb-format/issues
9
+ Project-URL: Specification, https://github.com/dwfmarchant/cmb-format/blob/main/docs/binary-format.md
10
+ Author-email: David Marchant <dwfmarchant@gmail.com>
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: binary-format,cmb,file-format,geophysics,mesh,octree
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: Scientific/Engineering
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: numpy>=1.26
24
+ Description-Content-Type: text/markdown
25
+
26
+ # cmb-format
27
+
28
+ **CMB (Cell Model Binary)** is a binary file format and Python I/O library
29
+ for storing UBC GIF–style tensor and octree meshes and their associated
30
+ models. It provides an alternative to the ASCII mesh and model files used
31
+ by UBC GIF software. CMB supports uniform and variable-spacing tensor
32
+ meshes, octree meshes, multiple named models, and file- and model-level
33
+ metadata.
34
+
35
+ CMB stores mesh geometry and model values as typed arrays, with metadata in
36
+ JSON, so consumers can avoid parsing millions of numbers from text. Large
37
+ octree consumers that need only arrays can also avoid allocating a full
38
+ consumer mesh; see the [discretize round trips and benchmarks](https://github.com/dwfmarchant/cmb-format/blob/main/docs/discretize.md).
39
+
40
+ ## Capabilities
41
+
42
+ - Store mesh geometry and per-cell model arrays together in a `.cmb` file.
43
+ - Store models separately from geometry to avoid duplicating large meshes.
44
+ - Read individual arrays without loading the whole file.
45
+ - Verify each array's integrity with a SHA-256 checksum.
46
+
47
+ ## Installation
48
+
49
+ Requires Python 3.11 or newer. NumPy is the only runtime dependency.
50
+ From a local checkout:
51
+
52
+ ```bash
53
+ python -m pip install .
54
+ ```
55
+
56
+ ## Usage
57
+
58
+ The API accepts dictionaries of NumPy arrays describing meshes and models.
59
+ CMB uses a different cell ordering from UBC GIF; these routines do not
60
+ convert between the two. See
61
+ [Cell numbering / ordering](https://github.com/dwfmarchant/cmb-format/blob/main/docs/binary-format.md#cell-numbering-ordering)
62
+ in the format specification.
63
+
64
+ Write a four-cell tensor mesh and a resistivity model, then read them back:
65
+
66
+ ```python
67
+ import numpy as np
68
+
69
+ import cmb_format as cmb
70
+
71
+ mesh = {
72
+ "mode": "embedded",
73
+ "mesh_class": "TensorMesh",
74
+ "arrays": {
75
+ "origin": np.zeros(3),
76
+ "h_x": np.array([1.0, 2.0]),
77
+ "h_y": np.array([1.0, 1.0]),
78
+ "h_z": np.array([3.0]),
79
+ },
80
+ }
81
+ models = {
82
+ "rho": {
83
+ "metadata": {"units": "ohm-m"},
84
+ "array": np.array([10.0, 20.0, 30.0, 40.0]),
85
+ }
86
+ }
87
+ cmb.write_file("example.cmb", mesh, models)
88
+
89
+ mesh, models, metadata = cmb.read_file("example.cmb")
90
+ rho = models["rho"]["array"]
91
+ ```
92
+
93
+ `read_file` loads and checksum-verifies all geometry and model arrays,
94
+ including nested base-mesh geometry. Its three results match `write_file`'s
95
+ `mesh`, `models`, and `metadata` parameters, so passing them straight back
96
+ preserves the mesh geometry, model arrays, and metadata. The NumPy arrays are
97
+ read-only; use `.copy()` if you need to modify them.
98
+
99
+ To read individual arrays without loading the whole file:
100
+
101
+ ```python
102
+ with open("example.cmb", "rb") as f:
103
+ header, data_start = cmb.read_header(f)
104
+ metadata = header["metadata"]
105
+ geometry = cmb.read_arrays(f, data_start, header["mesh"]["arrays"])
106
+ rho = cmb.read_array(f, data_start, header["models"]["rho"]["array"])
107
+ ```
108
+
109
+ For measured large-octree and tensor round trips and timing methodology, see
110
+ [the discretize interoperability notes](https://github.com/dwfmarchant/cmb-format/blob/main/docs/discretize.md). On the measured
111
+ 2.18-million-leaf sample, the generated CMB file is 10.4 MiB versus 28.8 MiB
112
+ for UBC, and conversion plus CMB writing is about 21× faster.
113
+
114
+ ## Development
115
+
116
+ From a local checkout, with pip 25.1 or newer:
117
+
118
+ ```bash
119
+ python -m pip install --group dev -e .
120
+ python -m pytest
121
+ python -m ruff check .
122
+ python -m ruff format --check .
123
+ ```
124
+
125
+ Committed reference files in `tests/goldens/` test compatibility with the
126
+ binary format alongside round-trip tests.
127
+
128
+ The [format specification](https://github.com/dwfmarchant/cmb-format/blob/main/docs/binary-format.md) defines the file layout
129
+ and mesh schemas. Package and format versions are independent; see
130
+ [versioning](https://github.com/dwfmarchant/cmb-format/blob/main/docs/binary-format.md#versioning) and the
131
+ [package changelog](https://github.com/dwfmarchant/cmb-format/blob/main/CHANGELOG.md).
@@ -0,0 +1,106 @@
1
+ # cmb-format
2
+
3
+ **CMB (Cell Model Binary)** is a binary file format and Python I/O library
4
+ for storing UBC GIF–style tensor and octree meshes and their associated
5
+ models. It provides an alternative to the ASCII mesh and model files used
6
+ by UBC GIF software. CMB supports uniform and variable-spacing tensor
7
+ meshes, octree meshes, multiple named models, and file- and model-level
8
+ metadata.
9
+
10
+ CMB stores mesh geometry and model values as typed arrays, with metadata in
11
+ JSON, so consumers can avoid parsing millions of numbers from text. Large
12
+ octree consumers that need only arrays can also avoid allocating a full
13
+ consumer mesh; see the [discretize round trips and benchmarks](https://github.com/dwfmarchant/cmb-format/blob/main/docs/discretize.md).
14
+
15
+ ## Capabilities
16
+
17
+ - Store mesh geometry and per-cell model arrays together in a `.cmb` file.
18
+ - Store models separately from geometry to avoid duplicating large meshes.
19
+ - Read individual arrays without loading the whole file.
20
+ - Verify each array's integrity with a SHA-256 checksum.
21
+
22
+ ## Installation
23
+
24
+ Requires Python 3.11 or newer. NumPy is the only runtime dependency.
25
+ From a local checkout:
26
+
27
+ ```bash
28
+ python -m pip install .
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ The API accepts dictionaries of NumPy arrays describing meshes and models.
34
+ CMB uses a different cell ordering from UBC GIF; these routines do not
35
+ convert between the two. See
36
+ [Cell numbering / ordering](https://github.com/dwfmarchant/cmb-format/blob/main/docs/binary-format.md#cell-numbering-ordering)
37
+ in the format specification.
38
+
39
+ Write a four-cell tensor mesh and a resistivity model, then read them back:
40
+
41
+ ```python
42
+ import numpy as np
43
+
44
+ import cmb_format as cmb
45
+
46
+ mesh = {
47
+ "mode": "embedded",
48
+ "mesh_class": "TensorMesh",
49
+ "arrays": {
50
+ "origin": np.zeros(3),
51
+ "h_x": np.array([1.0, 2.0]),
52
+ "h_y": np.array([1.0, 1.0]),
53
+ "h_z": np.array([3.0]),
54
+ },
55
+ }
56
+ models = {
57
+ "rho": {
58
+ "metadata": {"units": "ohm-m"},
59
+ "array": np.array([10.0, 20.0, 30.0, 40.0]),
60
+ }
61
+ }
62
+ cmb.write_file("example.cmb", mesh, models)
63
+
64
+ mesh, models, metadata = cmb.read_file("example.cmb")
65
+ rho = models["rho"]["array"]
66
+ ```
67
+
68
+ `read_file` loads and checksum-verifies all geometry and model arrays,
69
+ including nested base-mesh geometry. Its three results match `write_file`'s
70
+ `mesh`, `models`, and `metadata` parameters, so passing them straight back
71
+ preserves the mesh geometry, model arrays, and metadata. The NumPy arrays are
72
+ read-only; use `.copy()` if you need to modify them.
73
+
74
+ To read individual arrays without loading the whole file:
75
+
76
+ ```python
77
+ with open("example.cmb", "rb") as f:
78
+ header, data_start = cmb.read_header(f)
79
+ metadata = header["metadata"]
80
+ geometry = cmb.read_arrays(f, data_start, header["mesh"]["arrays"])
81
+ rho = cmb.read_array(f, data_start, header["models"]["rho"]["array"])
82
+ ```
83
+
84
+ For measured large-octree and tensor round trips and timing methodology, see
85
+ [the discretize interoperability notes](https://github.com/dwfmarchant/cmb-format/blob/main/docs/discretize.md). On the measured
86
+ 2.18-million-leaf sample, the generated CMB file is 10.4 MiB versus 28.8 MiB
87
+ for UBC, and conversion plus CMB writing is about 21× faster.
88
+
89
+ ## Development
90
+
91
+ From a local checkout, with pip 25.1 or newer:
92
+
93
+ ```bash
94
+ python -m pip install --group dev -e .
95
+ python -m pytest
96
+ python -m ruff check .
97
+ python -m ruff format --check .
98
+ ```
99
+
100
+ Committed reference files in `tests/goldens/` test compatibility with the
101
+ binary format alongside round-trip tests.
102
+
103
+ The [format specification](https://github.com/dwfmarchant/cmb-format/blob/main/docs/binary-format.md) defines the file layout
104
+ and mesh schemas. Package and format versions are independent; see
105
+ [versioning](https://github.com/dwfmarchant/cmb-format/blob/main/docs/binary-format.md#versioning) and the
106
+ [package changelog](https://github.com/dwfmarchant/cmb-format/blob/main/CHANGELOG.md).