pyvcell 0.0.1__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.
- pyvcell-0.0.1/LICENSE +21 -0
- pyvcell-0.0.1/PKG-INFO +75 -0
- pyvcell-0.0.1/README.md +50 -0
- pyvcell-0.0.1/pyproject.toml +118 -0
- pyvcell-0.0.1/pyvcell/__init__.py +0 -0
- pyvcell-0.0.1/pyvcell/foo.py +17 -0
- pyvcell-0.0.1/pyvcell/simdata/__init__.py +0 -0
- pyvcell-0.0.1/pyvcell/simdata/main.py +34 -0
- pyvcell-0.0.1/pyvcell/simdata/mesh.py +224 -0
- pyvcell-0.0.1/pyvcell/simdata/postprocessing.py +191 -0
- pyvcell-0.0.1/pyvcell/simdata/simdata_models.py +292 -0
- pyvcell-0.0.1/pyvcell/simdata/vtk/__init__.py +0 -0
- pyvcell-0.0.1/pyvcell/simdata/vtk/fv_mesh_mapping.py +222 -0
- pyvcell-0.0.1/pyvcell/simdata/vtk/vismesh.py +149 -0
- pyvcell-0.0.1/pyvcell/simdata/vtk/vtkmesh_chombo.py +98 -0
- pyvcell-0.0.1/pyvcell/simdata/vtk/vtkmesh_fv.py +61 -0
- pyvcell-0.0.1/pyvcell/simdata/vtk/vtkmesh_mb.py +72 -0
- pyvcell-0.0.1/pyvcell/simdata/vtk/vtkmesh_utils.py +322 -0
- pyvcell-0.0.1/pyvcell/simdata/zarr_writer.py +127 -0
- pyvcell-0.0.1/pyvcell/solvers/__init__.py +0 -0
- pyvcell-0.0.1/pyvcell/solvers/fvsolver.py +20 -0
pyvcell-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024, Jim Schaff
|
|
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.
|
pyvcell-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pyvcell
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: This is the python wrapper for vcell modeling and simulation
|
|
5
|
+
Home-page: https://github.com/virtualcell/pyvcell
|
|
6
|
+
Author: Jim Schaff
|
|
7
|
+
Author-email: fschaff@uchc.edu
|
|
8
|
+
Requires-Python: >=3.10,<4.0
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Requires-Dist: h5py (>=3.11.0,<4.0.0)
|
|
14
|
+
Requires-Dist: numexpr (>=2.10.0,<3.0.0)
|
|
15
|
+
Requires-Dist: numpy (>=1.26.4,<2.0.0)
|
|
16
|
+
Requires-Dist: orjson (>=3.10.3,<4.0.0)
|
|
17
|
+
Requires-Dist: pyvcell-fvsolver (>=0.0.4,<0.0.5)
|
|
18
|
+
Requires-Dist: typer (>=0.12.3,<0.13.0)
|
|
19
|
+
Requires-Dist: vtk (>=9.3.1,<10.0.0)
|
|
20
|
+
Requires-Dist: zarr (>=2.17.2,<3.0.0)
|
|
21
|
+
Project-URL: Documentation, https://virtualcell.github.io/pyvcell/
|
|
22
|
+
Project-URL: Repository, https://github.com/virtualcell/pyvcell
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# pyvcell
|
|
26
|
+
|
|
27
|
+
[](https://img.shields.io/github/v/release/virtualcell/pyvcell)
|
|
28
|
+
[](https://github.com/virtualcell/pyvcell/actions/workflows/main.yml?query=branch%3Amain)
|
|
29
|
+
[](https://codecov.io/gh/virtualcell/pyvcell)
|
|
30
|
+
[](https://img.shields.io/github/commit-activity/m/virtualcell/pyvcell)
|
|
31
|
+
[](https://img.shields.io/github/license/virtualcell/pyvcell)
|
|
32
|
+
|
|
33
|
+
This is the python wrapper for vcell modeling and simulation
|
|
34
|
+
|
|
35
|
+
- **Github repository**: <https://github.com/virtualcell/pyvcell/>
|
|
36
|
+
- **Documentation** <https://virtualcell.github.io/pyvcell/>
|
|
37
|
+
|
|
38
|
+
## Getting started with your project
|
|
39
|
+
|
|
40
|
+
First, create a repository on GitHub with the same name as this project, and then run the following commands:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
git init -b main
|
|
44
|
+
git add .
|
|
45
|
+
git commit -m "init commit"
|
|
46
|
+
git remote add origin git@github.com:virtualcell/pyvcell.git
|
|
47
|
+
git push -u origin main
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Finally, install the environment and the pre-commit hooks with
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
make install
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
You are now ready to start development on your project!
|
|
57
|
+
The CI/CD pipeline will be triggered when you open a pull request, merge to main, or when you create a new release.
|
|
58
|
+
|
|
59
|
+
To finalize the set-up for publishing to PyPi or Artifactory, see [here](https://fpgmaas.github.io/cookiecutter-poetry/features/publishing/#set-up-for-pypi).
|
|
60
|
+
For activating the automatic documentation with MkDocs, see [here](https://fpgmaas.github.io/cookiecutter-poetry/features/mkdocs/#enabling-the-documentation-on-github).
|
|
61
|
+
To enable the code coverage reports, see [here](https://fpgmaas.github.io/cookiecutter-poetry/features/codecov/).
|
|
62
|
+
|
|
63
|
+
## Releasing a new version
|
|
64
|
+
|
|
65
|
+
- Create an API Token on [Pypi](https://pypi.org/).
|
|
66
|
+
- Add the API Token to your projects secrets with the name `PYPI_TOKEN` by visiting [this page](https://github.com/virtualcell/pyvcell/settings/secrets/actions/new).
|
|
67
|
+
- Create a [new release](https://github.com/virtualcell/pyvcell/releases/new) on Github.
|
|
68
|
+
- Create a new tag in the form `*.*.*`.
|
|
69
|
+
|
|
70
|
+
For more details, see [here](https://fpgmaas.github.io/cookiecutter-poetry/features/cicd/#how-to-trigger-a-release).
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
Repository initiated with [fpgmaas/cookiecutter-poetry](https://github.com/fpgmaas/cookiecutter-poetry).
|
|
75
|
+
|
pyvcell-0.0.1/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# pyvcell
|
|
2
|
+
|
|
3
|
+
[](https://img.shields.io/github/v/release/virtualcell/pyvcell)
|
|
4
|
+
[](https://github.com/virtualcell/pyvcell/actions/workflows/main.yml?query=branch%3Amain)
|
|
5
|
+
[](https://codecov.io/gh/virtualcell/pyvcell)
|
|
6
|
+
[](https://img.shields.io/github/commit-activity/m/virtualcell/pyvcell)
|
|
7
|
+
[](https://img.shields.io/github/license/virtualcell/pyvcell)
|
|
8
|
+
|
|
9
|
+
This is the python wrapper for vcell modeling and simulation
|
|
10
|
+
|
|
11
|
+
- **Github repository**: <https://github.com/virtualcell/pyvcell/>
|
|
12
|
+
- **Documentation** <https://virtualcell.github.io/pyvcell/>
|
|
13
|
+
|
|
14
|
+
## Getting started with your project
|
|
15
|
+
|
|
16
|
+
First, create a repository on GitHub with the same name as this project, and then run the following commands:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
git init -b main
|
|
20
|
+
git add .
|
|
21
|
+
git commit -m "init commit"
|
|
22
|
+
git remote add origin git@github.com:virtualcell/pyvcell.git
|
|
23
|
+
git push -u origin main
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Finally, install the environment and the pre-commit hooks with
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
make install
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
You are now ready to start development on your project!
|
|
33
|
+
The CI/CD pipeline will be triggered when you open a pull request, merge to main, or when you create a new release.
|
|
34
|
+
|
|
35
|
+
To finalize the set-up for publishing to PyPi or Artifactory, see [here](https://fpgmaas.github.io/cookiecutter-poetry/features/publishing/#set-up-for-pypi).
|
|
36
|
+
For activating the automatic documentation with MkDocs, see [here](https://fpgmaas.github.io/cookiecutter-poetry/features/mkdocs/#enabling-the-documentation-on-github).
|
|
37
|
+
To enable the code coverage reports, see [here](https://fpgmaas.github.io/cookiecutter-poetry/features/codecov/).
|
|
38
|
+
|
|
39
|
+
## Releasing a new version
|
|
40
|
+
|
|
41
|
+
- Create an API Token on [Pypi](https://pypi.org/).
|
|
42
|
+
- Add the API Token to your projects secrets with the name `PYPI_TOKEN` by visiting [this page](https://github.com/virtualcell/pyvcell/settings/secrets/actions/new).
|
|
43
|
+
- Create a [new release](https://github.com/virtualcell/pyvcell/releases/new) on Github.
|
|
44
|
+
- Create a new tag in the form `*.*.*`.
|
|
45
|
+
|
|
46
|
+
For more details, see [here](https://fpgmaas.github.io/cookiecutter-poetry/features/cicd/#how-to-trigger-a-release).
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
Repository initiated with [fpgmaas/cookiecutter-poetry](https://github.com/fpgmaas/cookiecutter-poetry).
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "pyvcell"
|
|
3
|
+
version = "0.0.1"
|
|
4
|
+
description = "This is the python wrapper for vcell modeling and simulation"
|
|
5
|
+
authors = ["Jim Schaff <fschaff@uchc.edu>"]
|
|
6
|
+
repository = "https://github.com/virtualcell/pyvcell"
|
|
7
|
+
documentation = "https://virtualcell.github.io/pyvcell/"
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
packages = [
|
|
10
|
+
{include = "pyvcell"}
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[tool.poetry.dependencies]
|
|
14
|
+
python = ">=3.10,<4.0"
|
|
15
|
+
numexpr = "^2.10.0"
|
|
16
|
+
zarr = "^2.17.2"
|
|
17
|
+
h5py = "^3.11.0"
|
|
18
|
+
numpy = "^1.26.4"
|
|
19
|
+
orjson = "^3.10.3"
|
|
20
|
+
vtk = "^9.3.1"
|
|
21
|
+
pyvcell-fvsolver = "^0.0.4"
|
|
22
|
+
typer = "^0.12.3"
|
|
23
|
+
|
|
24
|
+
[tool.poetry.group.dev.dependencies]
|
|
25
|
+
pytest = "^7.2.0"
|
|
26
|
+
pytest-cov = "^4.0.0"
|
|
27
|
+
deptry = "^0.16.2"
|
|
28
|
+
mypy = "^1.5.1"
|
|
29
|
+
pre-commit = "^3.8.0"
|
|
30
|
+
tox = "^4.11.1"
|
|
31
|
+
h5py-stubs = "^0.1.1"
|
|
32
|
+
|
|
33
|
+
[tool.poetry.group.docs.dependencies]
|
|
34
|
+
mkdocs = "^1.4.2"
|
|
35
|
+
mkdocs-material = "^9.2.7"
|
|
36
|
+
mkdocstrings = {extras = ["python"], version = "^0.23.0"}
|
|
37
|
+
|
|
38
|
+
[build-system]
|
|
39
|
+
requires = ["poetry-core>=1.0.0"]
|
|
40
|
+
build-backend = "poetry.core.masonry.api"
|
|
41
|
+
|
|
42
|
+
[tool.mypy]
|
|
43
|
+
files = ["pyvcell"]
|
|
44
|
+
disallow_untyped_defs = "True"
|
|
45
|
+
disallow_any_unimported = "True"
|
|
46
|
+
no_implicit_optional = "True"
|
|
47
|
+
check_untyped_defs = "True"
|
|
48
|
+
warn_return_any = "True"
|
|
49
|
+
warn_unused_ignores = "True"
|
|
50
|
+
show_error_codes = "True"
|
|
51
|
+
|
|
52
|
+
[tool.pytest.ini_options]
|
|
53
|
+
testpaths = ["tests"]
|
|
54
|
+
|
|
55
|
+
[tool.ruff]
|
|
56
|
+
target-version = "py39"
|
|
57
|
+
line-length = 120
|
|
58
|
+
fix = true
|
|
59
|
+
select = [
|
|
60
|
+
# flake8-2020
|
|
61
|
+
"YTT",
|
|
62
|
+
# flake8-bandit
|
|
63
|
+
"S",
|
|
64
|
+
# flake8-bugbear
|
|
65
|
+
"B",
|
|
66
|
+
# flake8-builtins
|
|
67
|
+
"A",
|
|
68
|
+
# flake8-comprehensions
|
|
69
|
+
"C4",
|
|
70
|
+
# flake8-debugger
|
|
71
|
+
"T10",
|
|
72
|
+
# flake8-simplify
|
|
73
|
+
"SIM",
|
|
74
|
+
# isort
|
|
75
|
+
"I",
|
|
76
|
+
# mccabe
|
|
77
|
+
"C90",
|
|
78
|
+
# pycodestyle
|
|
79
|
+
"E", "W",
|
|
80
|
+
# pyflakes
|
|
81
|
+
"F",
|
|
82
|
+
# pygrep-hooks
|
|
83
|
+
"PGH",
|
|
84
|
+
# pyupgrade
|
|
85
|
+
"UP",
|
|
86
|
+
# ruff
|
|
87
|
+
"RUF",
|
|
88
|
+
# tryceratops
|
|
89
|
+
"TRY",
|
|
90
|
+
]
|
|
91
|
+
ignore = [
|
|
92
|
+
# LineTooLong
|
|
93
|
+
"E501",
|
|
94
|
+
# DoNotAssignLambda
|
|
95
|
+
"E731",
|
|
96
|
+
# avoid specifiying long messages outside the exception class
|
|
97
|
+
"TRY003",
|
|
98
|
+
# avoid functions which are too complex
|
|
99
|
+
"C901",
|
|
100
|
+
# avoid uses of Uses of `tarfile.extractall()
|
|
101
|
+
"S202",
|
|
102
|
+
# B008 Do not perform function call `typer.Argument` in argument defaults
|
|
103
|
+
"B008",
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
[tool.ruff.format]
|
|
107
|
+
preview = true
|
|
108
|
+
|
|
109
|
+
[tool.coverage.report]
|
|
110
|
+
skip_empty = true
|
|
111
|
+
|
|
112
|
+
[tool.coverage.run]
|
|
113
|
+
branch = true
|
|
114
|
+
source = ["pyvcell"]
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
[tool.ruff.per-file-ignores]
|
|
118
|
+
"tests/*" = ["S101"]
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
def foo(bar: str) -> str:
|
|
2
|
+
"""Summary line.
|
|
3
|
+
|
|
4
|
+
Extended description of function.
|
|
5
|
+
|
|
6
|
+
Args:
|
|
7
|
+
bar: Description of input argument.
|
|
8
|
+
|
|
9
|
+
Returns:
|
|
10
|
+
Description of return value
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
return bar
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
if __name__ == "__main__": # pragma: no cover
|
|
17
|
+
pass
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from pyvcell.simdata.mesh import CartesianMesh
|
|
6
|
+
from pyvcell.simdata.simdata_models import DataFunctions, PdeDataSet
|
|
7
|
+
from pyvcell.simdata.zarr_writer import write_zarr
|
|
8
|
+
|
|
9
|
+
app = typer.Typer()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@app.command(name="vc_to_zarr", help="Convert a VCell FiniteVolume simulation dataset to Zarr")
|
|
13
|
+
def n5_to_zarr(
|
|
14
|
+
sim_data_dir: Path = typer.Argument(..., help="path to vcell dataset directory"),
|
|
15
|
+
sim_id: int = typer.Argument(..., help="simulation id (e.g. 946368938)"),
|
|
16
|
+
job_id: int = typer.Argument(..., help="job id (e.g. 0"),
|
|
17
|
+
zarr_path: Path = typer.Argument(..., help="path to zarr dataset to write to"),
|
|
18
|
+
) -> None:
|
|
19
|
+
pde_dataset = PdeDataSet(base_dir=sim_data_dir, log_filename=f"SimID_{sim_id}_{job_id}_.log")
|
|
20
|
+
pde_dataset.read()
|
|
21
|
+
data_functions = DataFunctions(function_file=sim_data_dir / f"SimID_{sim_id}_{job_id}_.functions")
|
|
22
|
+
data_functions.read()
|
|
23
|
+
mesh = CartesianMesh(mesh_file=sim_data_dir / f"SimID_{sim_id}_{job_id}_.mesh")
|
|
24
|
+
mesh.read()
|
|
25
|
+
|
|
26
|
+
write_zarr(pde_dataset=pde_dataset, data_functions=data_functions, mesh=mesh, zarr_dir=zarr_path)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def main() -> None:
|
|
30
|
+
app()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
if __name__ == "__main__":
|
|
34
|
+
main()
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import zlib
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
from pyvcell.simdata.vtk.vismesh import Box3D
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CartesianMesh:
|
|
10
|
+
"""
|
|
11
|
+
reads the .mesh file and extracts the mesh information
|
|
12
|
+
|
|
13
|
+
Example .mesh file:
|
|
14
|
+
Version 1.2
|
|
15
|
+
CartesianMesh {
|
|
16
|
+
// X Y Z
|
|
17
|
+
Size 71 71 25
|
|
18
|
+
Extent 74.239999999999995 74.239999999999995 26
|
|
19
|
+
Origin 0 0 0
|
|
20
|
+
|
|
21
|
+
VolumeRegionsMapSubvolume {
|
|
22
|
+
6
|
|
23
|
+
//VolRegID SubvolID Volume
|
|
24
|
+
0 0 124767.54117864356 //ec
|
|
25
|
+
1 1 14855.904388351477 //cytosol
|
|
26
|
+
2 1 1.2185460680272107 //cytosol
|
|
27
|
+
3 1 1.2185460680272107 //cytosol
|
|
28
|
+
4 1 1.2185460680272107 //cytosol
|
|
29
|
+
5 2 3673.9163951019395 //Nucleus
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
MembraneRegionsMapVolumeRegion {
|
|
33
|
+
5
|
|
34
|
+
//MemRegID VolReg1 VolReg2 Surface
|
|
35
|
+
0 1 0 4512.8782874369472
|
|
36
|
+
1 2 0 1.7113582585034091
|
|
37
|
+
2 3 0 1.7113582585033937
|
|
38
|
+
3 4 0 1.711358258503394
|
|
39
|
+
4 5 1 1306.5985272332098
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
VolumeElementsMapVolumeRegion {
|
|
43
|
+
126025 Compressed
|
|
44
|
+
789CEDDD8D72DBC81100612389DFFF9573572A5912B9BF2066A66176B32A57B12CE22B8022E5DD11
|
|
45
|
+
F5EB9799999999999999999999999999999999999999999999999999999999999999999999999999
|
|
46
|
+
...
|
|
47
|
+
3333338B8F3625C09A5BE069281EE2BC0BC543D530FA907034666666666666666666666666666666
|
|
48
|
+
6666666666666666666666667F67FF07ABF56A9C
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
MembraneElements {
|
|
52
|
+
7817
|
|
53
|
+
//Indx Vol1 Vol2 Conn0 Conn1 Conn2 Conn3 MemRegID
|
|
54
|
+
0 6710 11751 5 507 493 1 0
|
|
55
|
+
1 6711 11752 6 0 494 510 0
|
|
56
|
+
2 6771 11812 10 524 503 3 0
|
|
57
|
+
3 6772 11813 11 2 505 527 0
|
|
58
|
+
4 6780 11821 16 533 508 5 0
|
|
59
|
+
....
|
|
60
|
+
7808 109155 104114 7807 7805 7792 7806 4
|
|
61
|
+
7809 104179 104180 7810 7551 7798 7811 4
|
|
62
|
+
7810 104251 104180 7812 7551 7809 -1 4
|
|
63
|
+
7811 109221 104180 -1 7809 7799 7813 4
|
|
64
|
+
7812 104252 104181 7815 7553 7810 -1 4
|
|
65
|
+
7813 109222 104181 -1 7811 7800 7816 4
|
|
66
|
+
7814 104183 104182 7815 7556 7802 7816 4
|
|
67
|
+
7815 104253 104182 7814 7554 7812 -1 4
|
|
68
|
+
7816 109223 104182 -1 7813 7801 7814 4
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
mesh_file: Path
|
|
74
|
+
size: list[int] # [x, y, z]
|
|
75
|
+
extent: list[float] # [x, y, z]
|
|
76
|
+
origin: list[float] # [x, y, z]
|
|
77
|
+
volume_regions: list[tuple[int, int, float, str]] # list of tuples (vol_reg_id, subvol_id, volume, domain_name)
|
|
78
|
+
membrane_regions: list[tuple[int, int, int, float]] # list of tuples (mem_reg_id, vol_reg1, vol_reg2, surface)
|
|
79
|
+
|
|
80
|
+
# membrane_element[m,:] = [idx, vol1, vol2, conn0, conn1, conn2, conn3, mem_reg_id]
|
|
81
|
+
membrane_elements: np.ndarray # shape (num_membrane_elements, 8)
|
|
82
|
+
|
|
83
|
+
# volume_region_map[m] = vol_reg_id
|
|
84
|
+
volume_region_map: np.ndarray # shape (size[0] * size[1] * size[2],)
|
|
85
|
+
|
|
86
|
+
def __init__(self, mesh_file: Path) -> None:
|
|
87
|
+
self.mesh_file = mesh_file
|
|
88
|
+
self.size = []
|
|
89
|
+
self.extent = []
|
|
90
|
+
self.origin = []
|
|
91
|
+
self.volume_regions = []
|
|
92
|
+
self.membrane_regions = []
|
|
93
|
+
# self.membrane_elements
|
|
94
|
+
self.volume_region_map = np.array([], dtype=np.uint8)
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def dimension(self) -> int:
|
|
98
|
+
if self.size[1] == 1 and self.size[2] == 1:
|
|
99
|
+
return 1
|
|
100
|
+
elif self.size[2] == 1:
|
|
101
|
+
return 2
|
|
102
|
+
else:
|
|
103
|
+
return 3
|
|
104
|
+
|
|
105
|
+
def read(self) -> None:
|
|
106
|
+
# read file as lines and parse
|
|
107
|
+
with self.mesh_file.open("r") as f:
|
|
108
|
+
# get line enumerator from f
|
|
109
|
+
|
|
110
|
+
iter_lines = iter(f.readlines())
|
|
111
|
+
|
|
112
|
+
if next(iter_lines) != "Version 1.2\n":
|
|
113
|
+
raise RuntimeError("Expected 'Version 1.2' at the beginning of the file")
|
|
114
|
+
if next(iter_lines) != "CartesianMesh {\n":
|
|
115
|
+
raise RuntimeError("Expected 'CartesianMesh {' after version")
|
|
116
|
+
if next(iter_lines) != "\t// X Y Z\n":
|
|
117
|
+
raise RuntimeError("Expected coordinate comment line")
|
|
118
|
+
|
|
119
|
+
size_line = next(iter_lines).split()
|
|
120
|
+
if size_line[0] == "Size":
|
|
121
|
+
self.size = [int(size_line[1]), int(size_line[2]), int(size_line[3])]
|
|
122
|
+
|
|
123
|
+
extent_line = next(iter_lines).split()
|
|
124
|
+
if extent_line[0] == "Extent":
|
|
125
|
+
self.extent = [float(extent_line[1]), float(extent_line[2]), float(extent_line[3])]
|
|
126
|
+
|
|
127
|
+
origin_line = next(iter_lines).split()
|
|
128
|
+
if origin_line[0] == "Origin":
|
|
129
|
+
self.origin = [float(origin_line[1]), float(origin_line[2]), float(origin_line[3])]
|
|
130
|
+
|
|
131
|
+
while next(iter_lines) != "\tVolumeRegionsMapSubvolume {\n":
|
|
132
|
+
pass
|
|
133
|
+
num_volume_regions = int(next(iter_lines))
|
|
134
|
+
_header_line = next(iter_lines)
|
|
135
|
+
self.volume_regions = []
|
|
136
|
+
for _i in range(num_volume_regions):
|
|
137
|
+
parts = next(iter_lines).split()
|
|
138
|
+
self.volume_regions.append((int(parts[0]), int(parts[1]), float(parts[2]), parts[3].strip("/")))
|
|
139
|
+
|
|
140
|
+
while next(iter_lines) != "\tMembraneRegionsMapVolumeRegion {\n":
|
|
141
|
+
pass
|
|
142
|
+
num_membrane_regions = int(next(iter_lines))
|
|
143
|
+
_header_line = next(iter_lines)
|
|
144
|
+
self.membrane_regions = []
|
|
145
|
+
for _i in range(num_membrane_regions):
|
|
146
|
+
parts = next(iter_lines).split()
|
|
147
|
+
self.membrane_regions.append((int(parts[0]), int(parts[1]), int(parts[2]), float(parts[3])))
|
|
148
|
+
|
|
149
|
+
while next(iter_lines) != "\tVolumeElementsMapVolumeRegion {\n":
|
|
150
|
+
pass
|
|
151
|
+
compressed_line = next(iter_lines).split()
|
|
152
|
+
num_volume_elements = int(compressed_line[0])
|
|
153
|
+
if compressed_line[1] != "Compressed":
|
|
154
|
+
raise ValueError("Expected 'Compressed' in VolumeElementsMapVolumeRegion")
|
|
155
|
+
# read HEX lines until "}" line, and concatenate into one string, then convert to bytes and decompress
|
|
156
|
+
hex_lines = []
|
|
157
|
+
while True:
|
|
158
|
+
line = next(iter_lines)
|
|
159
|
+
if line.strip() == "}":
|
|
160
|
+
break
|
|
161
|
+
hex_lines.append(line.strip())
|
|
162
|
+
hex_string: str = "".join(hex_lines).strip()
|
|
163
|
+
compressed_bytes = bytes.fromhex(hex_string)
|
|
164
|
+
uncompressed_bytes: bytes = zlib.decompress(compressed_bytes)
|
|
165
|
+
self.volume_region_map = np.frombuffer(uncompressed_bytes, dtype="<u2") # unsigned 2-byte integers
|
|
166
|
+
if self.volume_region_map.shape[0] != self.size[0] * self.size[1] * self.size[2]:
|
|
167
|
+
raise ValueError("Expected number of volume elements to match the size of volume region map")
|
|
168
|
+
if num_volume_elements != self.volume_region_map.shape[0]:
|
|
169
|
+
raise ValueError("Expected number of volume elements to match the size of volume region map")
|
|
170
|
+
if set(np.unique(self.volume_region_map)) != {v[0] for v in self.volume_regions}:
|
|
171
|
+
raise ValueError("Expected volume region map to have the same unique values as volume regions")
|
|
172
|
+
|
|
173
|
+
while next(iter_lines).strip() != "MembraneElements {":
|
|
174
|
+
pass
|
|
175
|
+
num_membrane_elements = int(next(iter_lines))
|
|
176
|
+
self.membrane_elements = np.zeros((num_membrane_elements, 8), dtype=np.int32)
|
|
177
|
+
_header_line = next(iter_lines)
|
|
178
|
+
mem_index = 0
|
|
179
|
+
while True:
|
|
180
|
+
line = next(iter_lines)
|
|
181
|
+
if line.strip() == "}":
|
|
182
|
+
break
|
|
183
|
+
parts = line.split()
|
|
184
|
+
idx = int(parts[0])
|
|
185
|
+
vol1 = int(parts[1])
|
|
186
|
+
vol2 = int(parts[2])
|
|
187
|
+
conn0 = int(parts[3])
|
|
188
|
+
conn1 = int(parts[4])
|
|
189
|
+
conn2 = int(parts[5])
|
|
190
|
+
conn3 = int(parts[6])
|
|
191
|
+
mem_reg_id = int(parts[7])
|
|
192
|
+
self.membrane_elements[mem_index, :] = [idx, vol1, vol2, conn0, conn1, conn2, conn3, mem_reg_id]
|
|
193
|
+
mem_index += 1
|
|
194
|
+
if self.membrane_elements.shape != (num_membrane_elements, 8):
|
|
195
|
+
raise RuntimeError("Expected membrane elements to have the correct shape")
|
|
196
|
+
if set(np.unique(self.membrane_elements[:, 7])) != {v[0] for v in self.membrane_regions}:
|
|
197
|
+
raise RuntimeError("Expected volume region ids in membrane elements to match volume regions")
|
|
198
|
+
|
|
199
|
+
def get_volume_element_box(self, i: int, j: int, k: int) -> Box3D:
|
|
200
|
+
x_lo = self.origin[0] + i * self.extent[0] / self.size[0]
|
|
201
|
+
y_lo = self.origin[1] + j * self.extent[1] / self.size[1]
|
|
202
|
+
z_lo = self.origin[2] + k * self.extent[2] / self.size[2]
|
|
203
|
+
x_hi = self.origin[0] + (i + 1) * self.extent[0] / self.size[0]
|
|
204
|
+
y_hi = self.origin[1] + (j + 1) * self.extent[1] / self.size[1]
|
|
205
|
+
z_hi = self.origin[2] + (k + 1) * self.extent[2] / self.size[2]
|
|
206
|
+
return Box3D(x_lo, y_lo, z_lo, x_hi, y_hi, z_hi)
|
|
207
|
+
|
|
208
|
+
def get_membrane_region_index(self, mem_element_index: int) -> int:
|
|
209
|
+
return int(self.membrane_elements[mem_element_index, 7])
|
|
210
|
+
|
|
211
|
+
def get_membrane_region_ids(self, volume_domain_name: str) -> set[int]:
|
|
212
|
+
return {
|
|
213
|
+
mem_reg_id
|
|
214
|
+
for mem_reg_id, vol_reg1, vol_reg2, surface in self.membrane_regions
|
|
215
|
+
if self.volume_regions[vol_reg1][3] == volume_domain_name
|
|
216
|
+
or self.volume_regions[vol_reg2][3] == volume_domain_name
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
def get_volume_region_ids(self, volume_domain_name: str) -> set[int]:
|
|
220
|
+
return {
|
|
221
|
+
vol_reg_id
|
|
222
|
+
for vol_reg_id, subvol_id, volume, domain_name in self.volume_regions
|
|
223
|
+
if domain_name == volume_domain_name
|
|
224
|
+
}
|