cardiac-geometriesx 0.4.7__tar.gz → 0.10.5__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 (32) hide show
  1. {cardiac_geometriesx-0.4.7/src/cardiac_geometriesx.egg-info → cardiac_geometriesx-0.10.5}/PKG-INFO +14 -7
  2. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5}/README.md +5 -1
  3. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5}/pyproject.toml +10 -7
  4. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5}/src/cardiac_geometries/__init__.py +2 -2
  5. cardiac_geometriesx-0.10.5/src/cardiac_geometries/aha.py +240 -0
  6. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5}/src/cardiac_geometries/cli.py +513 -329
  7. cardiac_geometriesx-0.10.5/src/cardiac_geometries/fibers/__init__.py +72 -0
  8. cardiac_geometriesx-0.10.5/src/cardiac_geometries/fibers/cylinder.py +125 -0
  9. cardiac_geometriesx-0.10.5/src/cardiac_geometries/fibers/cylinder_flat.py +164 -0
  10. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5}/src/cardiac_geometries/fibers/lv_ellipsoid.py +2 -1
  11. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5}/src/cardiac_geometries/fibers/slab.py +53 -20
  12. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5}/src/cardiac_geometries/fibers/utils.py +46 -36
  13. cardiac_geometriesx-0.10.5/src/cardiac_geometries/geometry.py +650 -0
  14. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5}/src/cardiac_geometries/gui.py +0 -2
  15. cardiac_geometriesx-0.10.5/src/cardiac_geometries/mesh.py +1451 -0
  16. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5}/src/cardiac_geometries/utils.py +226 -45
  17. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5/src/cardiac_geometriesx.egg-info}/PKG-INFO +14 -7
  18. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5}/src/cardiac_geometriesx.egg-info/SOURCES.txt +4 -0
  19. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5}/src/cardiac_geometriesx.egg-info/requires.txt +7 -4
  20. cardiac_geometriesx-0.10.5/tests/test_cli.py +212 -0
  21. cardiac_geometriesx-0.10.5/tests/test_refinement.py +100 -0
  22. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5}/tests/test_save_load.py +7 -5
  23. cardiac_geometriesx-0.4.7/src/cardiac_geometries/fibers/__init__.py +0 -4
  24. cardiac_geometriesx-0.4.7/src/cardiac_geometries/geometry.py +0 -197
  25. cardiac_geometriesx-0.4.7/src/cardiac_geometries/mesh.py +0 -912
  26. cardiac_geometriesx-0.4.7/tests/test_cli.py +0 -95
  27. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5}/LICENSE +0 -0
  28. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5}/setup.cfg +0 -0
  29. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5}/src/cardiac_geometriesx.egg-info/dependency_links.txt +0 -0
  30. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5}/src/cardiac_geometriesx.egg-info/entry_points.txt +0 -0
  31. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5}/src/cardiac_geometriesx.egg-info/not-zip-safe +0 -0
  32. {cardiac_geometriesx-0.4.7 → cardiac_geometriesx-0.10.5}/src/cardiac_geometriesx.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cardiac-geometriesx
3
- Version: 0.4.7
3
+ Version: 0.10.5
4
4
  Summary: A python library for cardiac geometries
5
5
  Author-email: Henrik Finsberg <henriknf@simula.no>
6
6
  License: MIT
@@ -9,12 +9,12 @@ Keywords: cardiac,geometry
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3 :: Only
12
- Requires-Python: >=3.8
12
+ Requires-Python: >=3.10
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
- Requires-Dist: fenics-dolfinx>=0.8.0
15
+ Requires-Dist: fenics-dolfinx>=0.9.0
16
16
  Requires-Dist: structlog
17
- Requires-Dist: cardiac-geometries-core
17
+ Requires-Dist: cardiac-geometries-core>=0.4
18
18
  Requires-Dist: rich-click
19
19
  Requires-Dist: adios4dolfinx
20
20
  Requires-Dist: scifem
@@ -26,14 +26,17 @@ Requires-Dist: pre-commit; extra == "dev"
26
26
  Requires-Dist: twine; extra == "dev"
27
27
  Requires-Dist: wheel; extra == "dev"
28
28
  Provides-Extra: docs
29
- Requires-Dist: jupyter-book; extra == "docs"
29
+ Requires-Dist: jupyter-book<2.0; extra == "docs"
30
30
  Requires-Dist: jupytext; extra == "docs"
31
31
  Requires-Dist: jupyter; extra == "docs"
32
- Requires-Dist: pyvista[all]>=0.43.0; extra == "docs"
32
+ Requires-Dist: pyvista[all]>=0.45.0; extra == "docs"
33
+ Requires-Dist: vtk>=9.5.0; extra == "docs"
33
34
  Requires-Dist: trame-vuetify; extra == "docs"
34
35
  Requires-Dist: ipywidgets; extra == "docs"
35
36
  Requires-Dist: fenicsx-ldrb; extra == "docs"
36
37
  Requires-Dist: ukb-atlas; extra == "docs"
38
+ Requires-Dist: sphinx-codeautolink; extra == "docs"
39
+ Requires-Dist: sphinx-copybutton; extra == "docs"
37
40
  Provides-Extra: test
38
41
  Requires-Dist: pre-commit; extra == "test"
39
42
  Requires-Dist: pytest; extra == "test"
@@ -77,7 +80,11 @@ To install the package you can use `pip`
77
80
  ```
78
81
  python3 -m pip install cardiac-geometriesx
79
82
  ```
80
- however, this assumes that you already have `dolfinx` pre-installed. You can also use the provided docker image e.g
83
+ however, this assumes that you already have `dolfinx` pre-installed. You can also use `conda`
84
+ ```
85
+ conda install -c conda-forge cardiac-geometriesx
86
+ ```
87
+ or the provided docker image
81
88
  ```
82
89
  docker pull ghcr.io/computationalphysiology/cardiac-geometriesx:latest
83
90
  ```
@@ -26,7 +26,11 @@ To install the package you can use `pip`
26
26
  ```
27
27
  python3 -m pip install cardiac-geometriesx
28
28
  ```
29
- however, this assumes that you already have `dolfinx` pre-installed. You can also use the provided docker image e.g
29
+ however, this assumes that you already have `dolfinx` pre-installed. You can also use `conda`
30
+ ```
31
+ conda install -c conda-forge cardiac-geometriesx
32
+ ```
33
+ or the provided docker image
30
34
  ```
31
35
  docker pull ghcr.io/computationalphysiology/cardiac-geometriesx:latest
32
36
  ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cardiac-geometriesx"
7
- version = "0.4.7"
7
+ version = "0.10.5"
8
8
  description = "A python library for cardiac geometries"
9
9
  authors = [{name = "Henrik Finsberg", email = "henriknf@simula.no"}]
10
10
  license = {text = "MIT"}
@@ -15,11 +15,11 @@ classifiers = [
15
15
  ]
16
16
  keywords = ["cardiac", "geometry"]
17
17
  urls = {Homepage = "https://github.com/finsberg/cardiac-geometriesx" }
18
- requires-python = ">=3.8"
18
+ requires-python = ">=3.10"
19
19
  dependencies = [
20
- "fenics-dolfinx>=0.8.0",
20
+ "fenics-dolfinx>=0.9.0",
21
21
  "structlog",
22
- "cardiac-geometries-core",
22
+ "cardiac-geometries-core>=0.4",
23
23
  "rich-click",
24
24
  "adios4dolfinx",
25
25
  "scifem",
@@ -39,14 +39,17 @@ dev = [
39
39
  "wheel",
40
40
  ]
41
41
  docs = [
42
- "jupyter-book",
42
+ "jupyter-book<2.0",
43
43
  "jupytext",
44
44
  "jupyter",
45
- "pyvista[all]>=0.43.0",
45
+ "pyvista[all]>=0.45.0",
46
+ "vtk>=9.5.0",
46
47
  "trame-vuetify",
47
48
  "ipywidgets",
48
49
  "fenicsx-ldrb",
49
50
  "ukb-atlas",
51
+ "sphinx-codeautolink",
52
+ "sphinx-copybutton",
50
53
  ]
51
54
  test = [
52
55
  "pre-commit",
@@ -177,7 +180,7 @@ tag = true
177
180
  sign_tags = false
178
181
  tag_name = "v{new_version}"
179
182
  tag_message = "Bump version: {current_version} → {new_version}"
180
- current_version = "0.4.7"
183
+ current_version = "0.10.5"
181
184
 
182
185
 
183
186
  [[tool.bumpversion.files]]
@@ -1,4 +1,4 @@
1
- from . import cli, fibers, geometry, mesh, utils
1
+ from . import aha, cli, fibers, geometry, mesh, utils
2
2
  from .geometry import Geometry
3
3
 
4
- __all__ = ["mesh", "fibers", "geometry", "Geometry", "utils", "cli"]
4
+ __all__ = ["mesh", "fibers", "geometry", "Geometry", "utils", "cli", "aha"]
@@ -0,0 +1,240 @@
1
+ import dolfinx
2
+ import numpy as np
3
+
4
+
5
+ def focal(r_long_endo: float | np.ndarray, r_short_endo: float | np.ndarray) -> float | np.ndarray:
6
+ """Calculate the focal distance for a prolate ellipsoid
7
+
8
+ Parameters
9
+ ----------
10
+ r_long_endo : float | np.ndarray
11
+ Radius of the long axis of the endocardium
12
+ r_short_endo : float | np.ndarray
13
+ Radius of the short axis of the endocardium"
14
+
15
+ Returns
16
+ -------
17
+ float | np.ndarray
18
+ The focal distance for the prolate ellipsoid
19
+ """
20
+ return np.sqrt(r_long_endo**2 - r_short_endo**2)
21
+
22
+
23
+ def full_arctangent(x, y) -> float | np.ndarray:
24
+ """Compute the full arctangent in [0, 2pi]
25
+
26
+ Parameters
27
+ ----------
28
+ x : float | np.ndarray
29
+ y : float | np.ndarray
30
+
31
+ Returns
32
+ -------
33
+ float | np.ndarray
34
+ The angle in [0, 2pi]
35
+ """
36
+ t = np.arctan2(x, y)
37
+ return np.where(t < 0, t + 2 * np.pi, t)
38
+
39
+
40
+ def cartesian_to_prolate_ellipsoidal(
41
+ x: float | np.ndarray,
42
+ y: float | np.ndarray,
43
+ z: float | np.ndarray,
44
+ a: float | np.ndarray,
45
+ ) -> tuple[float | np.ndarray, float | np.ndarray, float | np.ndarray]:
46
+ """Convert Cartesian coordinates to prolate ellipsoidal coordinates
47
+
48
+ Parameters
49
+ ----------
50
+ x : float | np.ndarray
51
+ y : float | np.ndarray
52
+ z : float | np.ndarray
53
+ a : float | np.ndarray
54
+
55
+ Returns
56
+ -------
57
+ tuple[float | np.ndarray, float | np.ndarray, float | np.ndarray]
58
+ The prolate ellipsoidal coordinates (nu, mu, phi)
59
+ """
60
+ b1 = np.sqrt((x + a) ** 2 + y**2 + z**2)
61
+ b2 = np.sqrt((x - a) ** 2 + y**2 + z**2)
62
+
63
+ sigma = 1 / (2.0 * a) * (b1 + b2)
64
+ tau = 1 / (2.0 * a) * (b1 - b2)
65
+ phi = full_arctangent(z, y)
66
+ nu = np.arccosh(sigma)
67
+ mu = np.arccos(tau)
68
+ return nu, mu, phi
69
+
70
+
71
+ def get_level(region: int, mu: np.ndarray | float, mu_base: float, dmu: float) -> np.ndarray | bool:
72
+ """Get the level set for a given AHA region.
73
+
74
+ Notes
75
+ -----
76
+ The regions are defined as follows:
77
+ - Regions 1-6: Basal (mu_base <= mu <= mu_base + dmu)
78
+ - Regions 7-12: Midventricular (mu_base + dmu < mu <= mu_base + 2 * dmu)
79
+ - Regions 13-16: Apical (mu_base + 2 * dmu < mu <= mu_base + 3 * dmu)
80
+ - Region 17: Apex (mu > mu_base + 3 * dmu)
81
+ This function returns a boolean array indicating whether
82
+ each mu value belongs to the specified region.
83
+
84
+ Parameters
85
+ ----------
86
+ region : int
87
+ The AHA region (1-17)
88
+ mu : np.ndarray | float
89
+ The mu coordinate(s)
90
+ mu_base : float
91
+ The base value of mu for segmentation
92
+ dmu : float
93
+ The segmentation width
94
+ Returns
95
+ -------
96
+ np.ndarray | bool
97
+ The level set for the given AHA region
98
+ """
99
+ if 1 <= region <= 6:
100
+ return np.logical_and(mu_base <= mu, mu <= mu_base + dmu)
101
+ elif 7 <= region <= 12:
102
+ return np.logical_and(mu_base + dmu < mu, mu <= mu_base + 2 * dmu)
103
+ elif 13 <= region <= 16:
104
+ return np.logical_and(mu_base + 2 * dmu < mu, mu <= mu_base + 3 * dmu)
105
+ else:
106
+ return mu > mu_base + 3 * dmu
107
+
108
+
109
+ def get_sector(region: int, phi: np.ndarray | float) -> np.ndarray | bool:
110
+ if region in (1, 7):
111
+ return np.logical_and(0 < phi, phi <= np.pi / 3)
112
+ elif region in (2, 8):
113
+ return np.logical_and(np.pi / 3 < phi, phi <= 2 * np.pi / 3)
114
+ elif region in (3, 9):
115
+ return np.logical_and(2 * np.pi / 3 < phi, phi <= np.pi)
116
+ elif region in (4, 10):
117
+ return np.logical_and(np.pi < phi, phi <= 4 * np.pi / 3)
118
+ elif region in (5, 11):
119
+ return np.logical_and(4 * np.pi / 3 < phi, phi <= 5 * np.pi / 3)
120
+ elif region in (6, 12):
121
+ return np.logical_and(5 * np.pi / 3 < phi, phi <= 2 * np.pi)
122
+ elif region == 13:
123
+ return np.logical_and(0 < phi, phi <= np.pi / 2)
124
+ elif region == 14:
125
+ return np.logical_and(np.pi / 2 < phi, phi <= np.pi)
126
+ elif region == 15:
127
+ return np.logical_and(np.pi < phi, phi <= 3 * np.pi / 2)
128
+ elif region == 16:
129
+ return np.logical_and(3 * np.pi / 2 < phi, phi <= 2 * np.pi)
130
+ else:
131
+ # 17 APEX
132
+ return np.ones_like(phi, dtype=bool)
133
+
134
+
135
+ def find_region_entities(
136
+ mu: np.ndarray, phi: np.ndarray, mu_base: float, region: int, dmu: float
137
+ ) -> np.ndarray:
138
+ """Find the entities belonging to a given AHA region.
139
+
140
+ Parameters
141
+ ----------
142
+ mu : np.ndarray
143
+ The mu coordinate(s)
144
+ phi : np.ndarray
145
+ The phi coordinate(s)
146
+ mu_base : float
147
+ The base value of mu for segmentation
148
+ region : int
149
+ The AHA region (1-17)
150
+ dmu : float
151
+ The segmentation width
152
+
153
+ Returns
154
+ -------
155
+ np.ndarray
156
+ The indices of entities belonging to the specified AHA region
157
+ """
158
+ level = get_level(region=region, mu=mu, mu_base=mu_base, dmu=dmu)
159
+ sector = get_sector(region=region, phi=phi)
160
+ return np.where(np.logical_and(level, sector))[0]
161
+
162
+
163
+ def lv_aha(
164
+ mesh: dolfinx.mesh.Mesh,
165
+ r_long_endo: float,
166
+ r_short_endo: float,
167
+ mu_base: float,
168
+ dmu_factor: float = 1 / 4,
169
+ ) -> tuple[dolfinx.mesh.MeshTags, dict[str, tuple[int, int]]]:
170
+ """Generate AHA segments for idealized LV ellipsoid
171
+
172
+ Parameters
173
+ ----------
174
+ mesh : dolfinx.mesh.Mesh
175
+ The LV ellipsoidal mesh
176
+ r_long_endo : float
177
+ Radius of the long axis of the endocardium
178
+ r_short_endo : float
179
+ Radius of the short axis of the endocardium
180
+ mu_base : float
181
+ Base value of mu for segmentation
182
+ dmu_factor : float, optional
183
+ Factor to determine the segmentation width, by default 1/4
184
+
185
+ Returns
186
+ -------
187
+ tuple[dolfinx.mesh.MeshTags, dict[str, tuple[int, int]]]
188
+ A tuple with the MeshTags object containing the cell tags
189
+ for the AHA segments and a dictionary with the marker
190
+ names and corresponding (tag, dim) values
191
+ """
192
+ assert mesh.topology.dim == 3, "AHA segmentation only implemented for 3D geometries"
193
+ foc = focal(r_long_endo=r_long_endo, r_short_endo=r_short_endo)
194
+ mu_base = abs(mu_base)
195
+ x, y, z = dolfinx.mesh.compute_midpoints(
196
+ mesh,
197
+ 3,
198
+ entities=np.arange(mesh.topology.index_map(mesh.topology.dim).size_local, dtype=np.int32),
199
+ ).T
200
+
201
+ dmu = (np.pi - mu_base) * dmu_factor
202
+ _, mu, phi = cartesian_to_prolate_ellipsoidal(x, y, z, a=foc)
203
+ entities = [
204
+ find_region_entities(mu=mu, phi=phi, mu_base=mu_base, region=region, dmu=dmu)
205
+ for region in range(1, 18)
206
+ ]
207
+
208
+ values = [np.full(len(e), i + 1, dtype=np.int32) for i, e in enumerate(entities)]
209
+
210
+ cell_tags = dolfinx.mesh.meshtags(
211
+ mesh,
212
+ 3,
213
+ np.hstack(entities),
214
+ np.hstack(values),
215
+ )
216
+
217
+ markers = {}
218
+ i = 1
219
+
220
+ for level in ["BASAL", "MID", "APICAL"]:
221
+ for sector in [
222
+ "ANTERIOR",
223
+ "ANTEROSEPTAL",
224
+ "SEPTAL",
225
+ "INFERIOR",
226
+ "POSTERIOR",
227
+ "LATERAL",
228
+ ]:
229
+ if level == "APICAL" and sector in ("ANTEROSEPTAL", "POSTERIOR"):
230
+ continue
231
+
232
+ markers["-".join((level, sector))] = (i, 3)
233
+ i += 1
234
+
235
+ markers["APEX"] = (17, 3)
236
+
237
+ return cell_tags, markers
238
+
239
+
240
+ def biv_aha(): ...