cardiac-geometriesx 0.4.5__py3-none-any.whl → 0.4.7__py3-none-any.whl

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.

Potentially problematic release.


This version of cardiac-geometriesx might be problematic. Click here for more details.

cardiac_geometries/cli.py CHANGED
@@ -24,6 +24,112 @@ def app():
24
24
  pass
25
25
 
26
26
 
27
+ @click.command(help="Create UK Biobank geometry")
28
+ @click.argument(
29
+ "outdir",
30
+ required=True,
31
+ type=click.Path(
32
+ file_okay=False,
33
+ dir_okay=True,
34
+ writable=True,
35
+ readable=True,
36
+ resolve_path=True,
37
+ ),
38
+ )
39
+ @click.option(
40
+ "--mode",
41
+ default=-1,
42
+ type=int,
43
+ help="Mode of the geometry",
44
+ show_default=True,
45
+ )
46
+ @click.option(
47
+ "--std",
48
+ default=1.5,
49
+ type=float,
50
+ help="Standard deviation of the geometry",
51
+ show_default=True,
52
+ )
53
+ @click.option(
54
+ "--case",
55
+ default="ED",
56
+ type=str,
57
+ help="Case of the geometry",
58
+ show_default=True,
59
+ )
60
+ @click.option(
61
+ "--char-length-max",
62
+ default=2.0,
63
+ type=float,
64
+ help="Maximum characteristic length of the mesh",
65
+ show_default=True,
66
+ )
67
+ @click.option(
68
+ "--char-length-min",
69
+ default=2.0,
70
+ type=float,
71
+ help="Minimum characteristic length of the mesh",
72
+ show_default=True,
73
+ )
74
+ @click.option(
75
+ "--fiber-angle-endo",
76
+ default=60,
77
+ type=float,
78
+ help="Angle for the endocardium",
79
+ show_default=True,
80
+ )
81
+ @click.option(
82
+ "--fiber-angle-epi",
83
+ default=-60,
84
+ type=float,
85
+ help="Angle for the epicardium",
86
+ show_default=True,
87
+ )
88
+ @click.option(
89
+ "--fiber-space",
90
+ default="P_1",
91
+ type=str,
92
+ help="Function space for fibers of the form family_degree",
93
+ show_default=True,
94
+ )
95
+ @click.option(
96
+ "--clipped/--no-clipped",
97
+ default=False,
98
+ is_flag=True,
99
+ type=bool,
100
+ help="If True create clip away the outflow tracts",
101
+ show_default=True,
102
+ )
103
+ def ukb(
104
+ outdir: Path | str,
105
+ mode: int = -1,
106
+ std: float = 1.5,
107
+ case: str = "ED",
108
+ char_length_max: float = 2.0,
109
+ char_length_min: float = 2.0,
110
+ fiber_angle_endo: float = 60,
111
+ fiber_angle_epi: float = -60,
112
+ fiber_space: str = "P_1",
113
+ clipped: bool = False,
114
+ ):
115
+ outdir = Path(outdir)
116
+ outdir.mkdir(exist_ok=True)
117
+
118
+ geo = mesh.ukb(
119
+ outdir=outdir,
120
+ mode=mode,
121
+ std=std,
122
+ case=case,
123
+ char_length_max=char_length_max,
124
+ char_length_min=char_length_min,
125
+ fiber_angle_endo=fiber_angle_endo,
126
+ fiber_angle_epi=fiber_angle_epi,
127
+ fiber_space=fiber_space,
128
+ clipped=clipped,
129
+ )
130
+ geo.save(outdir / "ukb.bp")
131
+
132
+
27
133
  @click.command(help="Create LV ellipsoidal geometry")
28
134
  @click.argument(
29
135
  "outdir",
@@ -382,6 +488,7 @@ def biv_ellipsoid(
382
488
  geo = mesh.biv_ellipsoid(
383
489
  outdir=outdir,
384
490
  char_length=char_length,
491
+ center_lv_x=center_lv_x,
385
492
  center_lv_y=center_lv_y,
386
493
  center_lv_z=center_lv_z,
387
494
  a_endo_lv=a_endo_lv,
@@ -390,6 +497,7 @@ def biv_ellipsoid(
390
497
  a_epi_lv=a_epi_lv,
391
498
  b_epi_lv=b_epi_lv,
392
499
  c_epi_lv=c_epi_lv,
500
+ center_rv_x=center_rv_x,
393
501
  center_rv_y=center_rv_y,
394
502
  center_rv_z=center_rv_z,
395
503
  a_endo_rv=a_endo_rv,
@@ -457,7 +565,7 @@ def biv_ellipsoid(
457
565
  default=math.pi / 6,
458
566
  type=float,
459
567
  help=(
460
- "Angle to rotate the torso in order to object realistic" " position of the heart in a torso"
568
+ "Angle to rotate the torso in order to object realistic position of the heart in a torso"
461
569
  ),
462
570
  show_default=True,
463
571
  )
@@ -886,3 +994,4 @@ app.add_command(biv_ellipsoid_torso)
886
994
  app.add_command(slab)
887
995
  app.add_command(slab_in_bath)
888
996
  app.add_command(gui)
997
+ app.add_command(ukb)
@@ -1,4 +1,5 @@
1
1
  import json
2
+ import logging
2
3
  import shutil
3
4
  from dataclasses import dataclass, field
4
5
  from pathlib import Path
@@ -13,6 +14,8 @@ from packaging.version import Version
13
14
 
14
15
  from . import utils
15
16
 
17
+ logger = logging.getLogger(__name__)
18
+
16
19
 
17
20
  @dataclass # (frozen=True, slots=True)
18
21
  class Geometry:
@@ -139,15 +142,17 @@ class Geometry:
139
142
  @classmethod
140
143
  def from_folder(cls, comm: MPI.Intracomm, folder: str | Path) -> "Geometry":
141
144
  folder = Path(folder)
142
-
145
+ logger.info(f"Reading geometry from {folder}")
143
146
  # Read mesh
144
147
  if (folder / "mesh.xdmf").exists():
148
+ logger.debug("Reading mesh")
145
149
  mesh, tags = utils.read_mesh(comm=comm, filename=folder / "mesh.xdmf")
146
150
  else:
147
151
  raise ValueError("No mesh file found")
148
152
 
149
153
  # Read markers
150
154
  if (folder / "markers.json").exists():
155
+ logger.debug("Reading markers")
151
156
  if comm.rank == 0:
152
157
  markers = json.loads((folder / "markers.json").read_text())
153
158
  else:
@@ -168,10 +173,12 @@ class Geometry:
168
173
  functions = {}
169
174
  microstructure_path = folder / "microstructure.bp"
170
175
  if microstructure_path.exists():
176
+ logger.debug("Reading microstructure")
171
177
  # function_space = adios4dolfinx.read_attributes(
172
178
  # comm=MPI.COMM_WORLD, filename=microstructure_path, name="function_space"
173
179
  # )
174
180
  for name, el in microstructure.items():
181
+ logger.debug(f"Reading {name}")
175
182
  element = utils.array2element(el)
176
183
  V = dolfinx.fem.functionspace(mesh, element)
177
184
  f = dolfinx.fem.Function(V, name=name)
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import datetime
2
4
  import json
3
5
  import math
@@ -7,9 +9,11 @@ from pathlib import Path
7
9
  from mpi4py import MPI
8
10
 
9
11
  import cardiac_geometries_core as cgc
12
+ import dolfinx
10
13
  from structlog import get_logger
11
14
 
12
15
  from . import utils
16
+ from .fibers.utils import save_microstructure
13
17
  from .geometry import Geometry
14
18
 
15
19
  meta = metadata("cardiac-geometriesx")
@@ -18,6 +22,137 @@ __version__ = meta["Version"]
18
22
  logger = get_logger()
19
23
 
20
24
 
25
+ def ukb(
26
+ outdir: str | Path,
27
+ mode: int = -1,
28
+ std: float = 1.5,
29
+ case: str = "ED",
30
+ char_length_max: float = 2.0,
31
+ char_length_min: float = 2.0,
32
+ fiber_angle_endo: float = 60,
33
+ fiber_angle_epi: float = -60,
34
+ fiber_space: str = "P_1",
35
+ clipped: bool = False,
36
+ comm: MPI.Comm = MPI.COMM_WORLD,
37
+ ) -> Geometry:
38
+ try:
39
+ import ukb.cli
40
+ except ImportError as e:
41
+ msg = (
42
+ "To create the UKB mesh you need to install the ukb package "
43
+ "which you can install with pip install ukb-atlas"
44
+ )
45
+ raise ImportError(msg) from e
46
+
47
+ if comm.rank == 0:
48
+ ukb.cli.main(["surf", str(outdir), "--mode", str(mode), "--std", str(std), "--case", case])
49
+ mesh_args = [
50
+ "mesh",
51
+ str(outdir),
52
+ "--case",
53
+ case,
54
+ "--char_length_max",
55
+ str(char_length_max),
56
+ "--char_length_min",
57
+ str(char_length_min),
58
+ ]
59
+ if clipped:
60
+ ukb.cli.main(["clip", str(outdir), "--case", case, "--smooth"])
61
+ mesh_args.append("--clipped")
62
+ print(comm.rank)
63
+
64
+ ukb.cli.main(mesh_args)
65
+ comm.barrier()
66
+ outdir = Path(outdir)
67
+ if clipped:
68
+ mesh_name = outdir / f"{case}_clipped.msh"
69
+ else:
70
+ mesh_name = outdir / f"{case}.msh"
71
+
72
+ geometry = utils.gmsh2dolfin(comm=comm, msh_file=mesh_name)
73
+
74
+ if comm.rank == 0:
75
+ (outdir / "markers.json").write_text(
76
+ json.dumps(geometry.markers, default=utils.json_serial)
77
+ )
78
+ (outdir / "info.json").write_text(
79
+ json.dumps(
80
+ {
81
+ "mode": mode,
82
+ "std": std,
83
+ "case": case,
84
+ "char_length_max": char_length_max,
85
+ "char_length_min": char_length_min,
86
+ "fiber_angle_endo": fiber_angle_endo,
87
+ "fiber_angle_epi": fiber_angle_epi,
88
+ "fiber_space": fiber_space,
89
+ "cardiac_geometry_version": __version__,
90
+ "type": "ukb",
91
+ "timestamp": datetime.datetime.now().isoformat(),
92
+ }
93
+ )
94
+ )
95
+
96
+ try:
97
+ import ldrb
98
+ except ImportError:
99
+ msg = (
100
+ "To create fibers you need to install the ldrb package "
101
+ "which you can install with pip install fenicsx-ldrb"
102
+ )
103
+ raise ImportError(msg)
104
+
105
+ if clipped:
106
+ markers = {
107
+ "lv": [geometry.markers["LV"][0]],
108
+ "rv": [geometry.markers["RV"][0]],
109
+ "epi": [geometry.markers["EPI"][0]],
110
+ "base": [geometry.markers["BASE"][0]],
111
+ }
112
+ else:
113
+ markers = {
114
+ "lv": [geometry.markers["LV"][0]],
115
+ "rv": [geometry.markers["RV"][0]],
116
+ "epi": [geometry.markers["EPI"][0]],
117
+ "base": [
118
+ geometry.markers["PV"][0],
119
+ geometry.markers["TV"][0],
120
+ geometry.markers["AV"][0],
121
+ geometry.markers["MV"][0],
122
+ ],
123
+ }
124
+ system = ldrb.dolfinx_ldrb(
125
+ mesh=geometry.mesh,
126
+ ffun=geometry.ffun,
127
+ markers=markers,
128
+ alpha_endo_lv=fiber_angle_endo,
129
+ alpha_epi_lv=fiber_angle_epi,
130
+ beta_endo_lv=0,
131
+ beta_epi_lv=0,
132
+ fiber_space=fiber_space,
133
+ )
134
+
135
+ save_microstructure(
136
+ mesh=geometry.mesh,
137
+ functions=(system.f0, system.s0, system.n0),
138
+ outdir=outdir,
139
+ )
140
+
141
+ for k, v in system._asdict().items():
142
+ if v is None:
143
+ continue
144
+ if fiber_space.startswith("Q"):
145
+ # Cannot visualize Quadrature spaces yet
146
+ continue
147
+
148
+ logger.debug(f"Write {k}: {v}")
149
+ with dolfinx.io.VTXWriter(comm, outdir / f"{k}-viz.bp", [v], engine="BP4") as vtx:
150
+ vtx.write(0.0)
151
+
152
+ geo = Geometry.from_folder(comm=comm, folder=outdir)
153
+ return geo
154
+
155
+
21
156
  def biv_ellipsoid(
22
157
  outdir: str | Path,
23
158
  char_length: float = 0.5,
@@ -199,10 +334,11 @@ def biv_ellipsoid(
199
334
  beta_epi_lv=0,
200
335
  fiber_space=fiber_space,
201
336
  )
202
- from .fibers.utils import save_microstructure
203
337
 
204
338
  save_microstructure(
205
- mesh=geometry.mesh, functions=(system.f0, system.s0, system.n0), outdir=outdir
339
+ mesh=geometry.mesh,
340
+ functions=(system.f0, system.s0, system.n0),
341
+ outdir=outdir,
206
342
  )
207
343
 
208
344
  geo = Geometry.from_folder(comm=comm, folder=outdir)
@@ -422,6 +422,7 @@ def read_mesh(
422
422
  def gmsh2dolfin(comm: MPI.Intracomm, msh_file, rank: int = 0) -> GMshGeometry:
423
423
  logger.debug(f"Convert file {msh_file} to dolfin")
424
424
  outdir = Path(msh_file).parent
425
+ outdir.mkdir(parents=True, exist_ok=True)
425
426
 
426
427
  if Version(dolfinx.__version__) >= Version("0.10.0"):
427
428
  mesh_data = dolfinx.io.gmshio.read_from_msh(comm=comm, filename=msh_file)
@@ -454,6 +455,7 @@ def gmsh2dolfin(comm: MPI.Intracomm, msh_file, rank: int = 0) -> GMshGeometry:
454
455
 
455
456
  else:
456
457
  import gmsh
458
+
457
459
  # We could make this work in parallel in the future
458
460
 
459
461
  if comm.rank == rank:
@@ -482,19 +484,27 @@ def gmsh2dolfin(comm: MPI.Intracomm, msh_file, rank: int = 0) -> GMshGeometry:
482
484
  with dolfinx.io.XDMFFile(comm, outdir / "mesh.xdmf", "w") as xdmf:
483
485
  xdmf.write_mesh(mesh)
484
486
  xdmf.write_meshtags(
485
- ct, mesh.geometry, geometry_xpath=f"/Xdmf/Domain/Grid[@Name='{mesh.name}']/Geometry"
487
+ ct,
488
+ mesh.geometry,
489
+ geometry_xpath=f"/Xdmf/Domain/Grid[@Name='{mesh.name}']/Geometry",
486
490
  )
487
491
  mesh.topology.create_connectivity(2, 3)
488
492
  xdmf.write_meshtags(
489
- ft, mesh.geometry, geometry_xpath=f"/Xdmf/Domain/Grid[@Name='{mesh.name}']/Geometry"
493
+ ft,
494
+ mesh.geometry,
495
+ geometry_xpath=f"/Xdmf/Domain/Grid[@Name='{mesh.name}']/Geometry",
490
496
  )
491
497
  mesh.topology.create_connectivity(1, 3)
492
498
  xdmf.write_meshtags(
493
- et, mesh.geometry, geometry_xpath=f"/Xdmf/Domain/Grid[@Name='{mesh.name}']/Geometry"
499
+ et,
500
+ mesh.geometry,
501
+ geometry_xpath=f"/Xdmf/Domain/Grid[@Name='{mesh.name}']/Geometry",
494
502
  )
495
503
  mesh.topology.create_connectivity(0, 3)
496
504
  xdmf.write_meshtags(
497
- vt, mesh.geometry, geometry_xpath=f"/Xdmf/Domain/Grid[@Name='{mesh.name}']/Geometry"
505
+ vt,
506
+ mesh.geometry,
507
+ geometry_xpath=f"/Xdmf/Domain/Grid[@Name='{mesh.name}']/Geometry",
498
508
  )
499
509
 
500
510
  return GMshGeometry(mesh, ct, ft, et, vt, markers)
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: cardiac-geometriesx
3
- Version: 0.4.5
3
+ Version: 0.4.7
4
4
  Summary: A python library for cardiac geometries
5
5
  Author-email: Henrik Finsberg <henriknf@simula.no>
6
6
  License: MIT
@@ -38,6 +38,8 @@ Provides-Extra: test
38
38
  Requires-Dist: pre-commit; extra == "test"
39
39
  Requires-Dist: pytest; extra == "test"
40
40
  Requires-Dist: pytest-cov; extra == "test"
41
+ Requires-Dist: ukb-atlas[pyvista]; extra == "test"
42
+ Requires-Dist: fenicsx-ldrb; extra == "test"
41
43
  Provides-Extra: gui
42
44
  Requires-Dist: streamlit; extra == "gui"
43
45
  Requires-Dist: stpyvista; extra == "gui"
@@ -45,6 +47,7 @@ Requires-Dist: pyvista[all]>=0.43.0; extra == "gui"
45
47
  Requires-Dist: trame-vuetify; extra == "gui"
46
48
  Requires-Dist: ipywidgets; extra == "gui"
47
49
  Requires-Dist: fenicsx-ldrb; extra == "gui"
50
+ Dynamic: license-file
48
51
 
49
52
  ![_](docs/_static/logo.png)
50
53
 
@@ -0,0 +1,16 @@
1
+ cardiac_geometries/__init__.py,sha256=2W_ywAeLjyRk5MqSPAodHa4UN2lOnW1h8tmGLQ3gaJ0,150
2
+ cardiac_geometries/cli.py,sha256=Um8iYayrjinNiPkqq7PZC_8GRqzXc_eFswIcrRHOdeU,22007
3
+ cardiac_geometries/geometry.py,sha256=y-V4DyksNCqPwK7-NQIjcS6ldlvh1ub2p7lj9FZJHWw,6795
4
+ cardiac_geometries/gui.py,sha256=9WYR850wLrqsUrVUC37E2SaO0OWA_oagSe-YNrsxz3k,8376
5
+ cardiac_geometries/mesh.py,sha256=jLHTujz_38SQ5loN94nuN6pMG8IVPmlCdwOJhzuMxy4,30922
6
+ cardiac_geometries/utils.py,sha256=nJE-gyV7NIAmgl5JGlG5Vl7XsIcYS8Y20bVjYcd4R6c,21964
7
+ cardiac_geometries/fibers/__init__.py,sha256=WpRrn9Iakl-3m8IGtFkqP0LXGjw5EZHZ8Eg9JCnCdrg,137
8
+ cardiac_geometries/fibers/lv_ellipsoid.py,sha256=LZNhxzTsn-h88xXGP5bNfnWKhdvBROUjWjt8yrQFW48,6010
9
+ cardiac_geometries/fibers/slab.py,sha256=TYQhckJ8mwXz_08Cx3QsPQMtemkaxZ955SnSMrDfBPE,3898
10
+ cardiac_geometries/fibers/utils.py,sha256=pCoXkaUbidLGPY0Ty8uWCU1siDkHcv2qP8rZtdxMf0k,3475
11
+ cardiac_geometriesx-0.4.7.dist-info/licenses/LICENSE,sha256=lo5K2rJPZOSv6luutGHbzzi3IpXNaB9E2UWq60qvNx0,1111
12
+ cardiac_geometriesx-0.4.7.dist-info/METADATA,sha256=oJ34aXkOaMw3jfqhmYbsIFeyAf5SMjf_PP-SifEgUW4,4320
13
+ cardiac_geometriesx-0.4.7.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
14
+ cardiac_geometriesx-0.4.7.dist-info/entry_points.txt,sha256=xOBnlc6W-H9oCDYLNz3kpki26OmpfYSoFSrmi_4V-Ec,52
15
+ cardiac_geometriesx-0.4.7.dist-info/top_level.txt,sha256=J0gQxkWR2my5Vf7Qt8buDY8ZOjYdVfIweVunCGXWKNE,19
16
+ cardiac_geometriesx-0.4.7.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,16 +0,0 @@
1
- cardiac_geometries/__init__.py,sha256=2W_ywAeLjyRk5MqSPAodHa4UN2lOnW1h8tmGLQ3gaJ0,150
2
- cardiac_geometries/cli.py,sha256=GKcsMNhVFoxWDr-o3jiDCXmTvPgCSEB9Kws8AvYIK4Q,19678
3
- cardiac_geometries/geometry.py,sha256=JSsLKfXej4DSgzRbivC-_jvyjiY6zkXcRCRL93IIys8,6504
4
- cardiac_geometries/gui.py,sha256=9WYR850wLrqsUrVUC37E2SaO0OWA_oagSe-YNrsxz3k,8376
5
- cardiac_geometries/mesh.py,sha256=_vNNMpQWyLEZ3TmsHAUh0l3Z2TYuQBy6Os-6nB9_Z9A,26908
6
- cardiac_geometries/utils.py,sha256=tMn_XQRYbP1__c0QPHZeiCRJun3TZiH80OwJDESTPzY,21817
7
- cardiac_geometries/fibers/__init__.py,sha256=WpRrn9Iakl-3m8IGtFkqP0LXGjw5EZHZ8Eg9JCnCdrg,137
8
- cardiac_geometries/fibers/lv_ellipsoid.py,sha256=LZNhxzTsn-h88xXGP5bNfnWKhdvBROUjWjt8yrQFW48,6010
9
- cardiac_geometries/fibers/slab.py,sha256=TYQhckJ8mwXz_08Cx3QsPQMtemkaxZ955SnSMrDfBPE,3898
10
- cardiac_geometries/fibers/utils.py,sha256=pCoXkaUbidLGPY0Ty8uWCU1siDkHcv2qP8rZtdxMf0k,3475
11
- cardiac_geometriesx-0.4.5.dist-info/LICENSE,sha256=lo5K2rJPZOSv6luutGHbzzi3IpXNaB9E2UWq60qvNx0,1111
12
- cardiac_geometriesx-0.4.5.dist-info/METADATA,sha256=VcogZHS3ECHMoAQ-ei54PSdGCOi6-bviY8KjEYbPgaw,4202
13
- cardiac_geometriesx-0.4.5.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
14
- cardiac_geometriesx-0.4.5.dist-info/entry_points.txt,sha256=xOBnlc6W-H9oCDYLNz3kpki26OmpfYSoFSrmi_4V-Ec,52
15
- cardiac_geometriesx-0.4.5.dist-info/top_level.txt,sha256=J0gQxkWR2my5Vf7Qt8buDY8ZOjYdVfIweVunCGXWKNE,19
16
- cardiac_geometriesx-0.4.5.dist-info/RECORD,,