cardiac-geometriesx 0.4.4__tar.gz → 0.4.6__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.

Potentially problematic release.


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

Files changed (24) hide show
  1. {cardiac_geometriesx-0.4.4/src/cardiac_geometriesx.egg-info → cardiac_geometriesx-0.4.6}/PKG-INFO +5 -2
  2. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/pyproject.toml +5 -2
  3. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/src/cardiac_geometries/cli.py +100 -1
  4. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/src/cardiac_geometries/fibers/utils.py +9 -1
  5. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/src/cardiac_geometries/geometry.py +8 -1
  6. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/src/cardiac_geometries/mesh.py +133 -2
  7. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/src/cardiac_geometries/utils.py +67 -25
  8. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6/src/cardiac_geometriesx.egg-info}/PKG-INFO +5 -2
  9. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/src/cardiac_geometriesx.egg-info/requires.txt +3 -0
  10. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/tests/test_cli.py +28 -0
  11. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/LICENSE +0 -0
  12. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/README.md +0 -0
  13. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/setup.cfg +0 -0
  14. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/src/cardiac_geometries/__init__.py +0 -0
  15. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/src/cardiac_geometries/fibers/__init__.py +0 -0
  16. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/src/cardiac_geometries/fibers/lv_ellipsoid.py +0 -0
  17. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/src/cardiac_geometries/fibers/slab.py +0 -0
  18. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/src/cardiac_geometries/gui.py +0 -0
  19. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/src/cardiac_geometriesx.egg-info/SOURCES.txt +0 -0
  20. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/src/cardiac_geometriesx.egg-info/dependency_links.txt +0 -0
  21. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/src/cardiac_geometriesx.egg-info/entry_points.txt +0 -0
  22. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/src/cardiac_geometriesx.egg-info/not-zip-safe +0 -0
  23. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/src/cardiac_geometriesx.egg-info/top_level.txt +0 -0
  24. {cardiac_geometriesx-0.4.4 → cardiac_geometriesx-0.4.6}/tests/test_save_load.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: cardiac-geometriesx
3
- Version: 0.4.4
3
+ Version: 0.4.6
4
4
  Summary: A python library for cardiac geometries
5
5
  Author-email: Henrik Finsberg <henriknf@simula.no>
6
6
  License: MIT
@@ -33,10 +33,13 @@ Requires-Dist: pyvista[all]>=0.43.0; extra == "docs"
33
33
  Requires-Dist: trame-vuetify; extra == "docs"
34
34
  Requires-Dist: ipywidgets; extra == "docs"
35
35
  Requires-Dist: fenicsx-ldrb; extra == "docs"
36
+ Requires-Dist: ukb-atlas; extra == "docs"
36
37
  Provides-Extra: test
37
38
  Requires-Dist: pre-commit; extra == "test"
38
39
  Requires-Dist: pytest; extra == "test"
39
40
  Requires-Dist: pytest-cov; extra == "test"
41
+ Requires-Dist: ukb-atlas; extra == "test"
42
+ Requires-Dist: fenicsx-ldrb; extra == "test"
40
43
  Provides-Extra: gui
41
44
  Requires-Dist: streamlit; extra == "gui"
42
45
  Requires-Dist: stpyvista; extra == "gui"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cardiac-geometriesx"
7
- version = "0.4.4"
7
+ version = "0.4.6"
8
8
  description = "A python library for cardiac geometries"
9
9
  authors = [{name = "Henrik Finsberg", email = "henriknf@simula.no"}]
10
10
  license = {text = "MIT"}
@@ -46,11 +46,14 @@ docs = [
46
46
  "trame-vuetify",
47
47
  "ipywidgets",
48
48
  "fenicsx-ldrb",
49
+ "ukb-atlas",
49
50
  ]
50
51
  test = [
51
52
  "pre-commit",
52
53
  "pytest",
53
54
  "pytest-cov",
55
+ "ukb-atlas",
56
+ "fenicsx-ldrb",
54
57
  ]
55
58
  gui = [
56
59
  "streamlit",
@@ -174,7 +177,7 @@ tag = true
174
177
  sign_tags = false
175
178
  tag_name = "v{new_version}"
176
179
  tag_message = "Bump version: {current_version} → {new_version}"
177
- current_version = "0.4.4"
180
+ current_version = "0.4.6"
178
181
 
179
182
 
180
183
  [[tool.bumpversion.files]]
@@ -24,6 +24,102 @@ 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
+ def ukb(
96
+ outdir: Path | str,
97
+ mode: int = -1,
98
+ std: float = 1.5,
99
+ case: str = "ED",
100
+ char_length_max: float = 2.0,
101
+ char_length_min: float = 2.0,
102
+ fiber_angle_endo: float = 60,
103
+ fiber_angle_epi: float = -60,
104
+ fiber_space: str = "P_1",
105
+ ):
106
+ outdir = Path(outdir)
107
+ outdir.mkdir(exist_ok=True)
108
+
109
+ geo = mesh.ukb(
110
+ outdir=outdir,
111
+ mode=mode,
112
+ std=std,
113
+ case=case,
114
+ char_length_max=char_length_max,
115
+ char_length_min=char_length_min,
116
+ fiber_angle_endo=fiber_angle_endo,
117
+ fiber_angle_epi=fiber_angle_epi,
118
+ fiber_space=fiber_space,
119
+ )
120
+ geo.save(outdir / "ukb.bp")
121
+
122
+
27
123
  @click.command(help="Create LV ellipsoidal geometry")
28
124
  @click.argument(
29
125
  "outdir",
@@ -382,6 +478,7 @@ def biv_ellipsoid(
382
478
  geo = mesh.biv_ellipsoid(
383
479
  outdir=outdir,
384
480
  char_length=char_length,
481
+ center_lv_x=center_lv_x,
385
482
  center_lv_y=center_lv_y,
386
483
  center_lv_z=center_lv_z,
387
484
  a_endo_lv=a_endo_lv,
@@ -390,6 +487,7 @@ def biv_ellipsoid(
390
487
  a_epi_lv=a_epi_lv,
391
488
  b_epi_lv=b_epi_lv,
392
489
  c_epi_lv=c_epi_lv,
490
+ center_rv_x=center_rv_x,
393
491
  center_rv_y=center_rv_y,
394
492
  center_rv_z=center_rv_z,
395
493
  a_endo_rv=a_endo_rv,
@@ -457,7 +555,7 @@ def biv_ellipsoid(
457
555
  default=math.pi / 6,
458
556
  type=float,
459
557
  help=(
460
- "Angle to rotate the torso in order to object realistic" " position of the heart in a torso"
558
+ "Angle to rotate the torso in order to object realistic position of the heart in a torso"
461
559
  ),
462
560
  show_default=True,
463
561
  )
@@ -886,3 +984,4 @@ app.add_command(biv_ellipsoid_torso)
886
984
  app.add_command(slab)
887
985
  app.add_command(slab_in_bath)
888
986
  app.add_command(gui)
987
+ app.add_command(ukb)
@@ -7,9 +7,12 @@ import dolfinx
7
7
  import numpy as np
8
8
  import ufl
9
9
  from dolfinx.fem.petsc import LinearProblem
10
+ from packaging.version import Version
10
11
 
11
12
  from ..utils import space_from_string
12
13
 
14
+ _dolfinx_version = Version(dolfinx.__version__)
15
+
13
16
 
14
17
  class Microstructure(NamedTuple):
15
18
  f0: dolfinx.fem.Function
@@ -101,7 +104,12 @@ def laplace(
101
104
  if function_space != "P_1":
102
105
  W = space_from_string(function_space, mesh, dim=1)
103
106
  t = dolfinx.fem.Function(W)
104
- expr = dolfinx.fem.Expression(uh, W.element.interpolation_points())
107
+ if _dolfinx_version >= Version("0.10"):
108
+ points = W.element.interpolation_points
109
+ else:
110
+ points = W.element.interpolation_points()
111
+
112
+ expr = dolfinx.fem.Expression(uh, points)
105
113
  t.interpolate(expr)
106
114
  else:
107
115
  t = uh
@@ -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,132 @@ __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
+ comm: MPI.Comm = MPI.COMM_WORLD,
36
+ ) -> Geometry:
37
+ try:
38
+ import ukb.cli
39
+ except ImportError as e:
40
+ msg = (
41
+ "To create the UKB mesh you need to install the ukb package "
42
+ "which you can install with pip install ukb-atlas"
43
+ )
44
+ raise ImportError(msg) from e
45
+
46
+ if comm.rank == 0:
47
+ print(comm.rank)
48
+ ukb.cli.main(
49
+ [
50
+ str(outdir),
51
+ "--mode",
52
+ str(mode),
53
+ "--std",
54
+ str(std),
55
+ "--case",
56
+ case,
57
+ "--mesh",
58
+ "--char_length_max",
59
+ str(char_length_max),
60
+ "--char_length_min",
61
+ str(char_length_min),
62
+ ]
63
+ )
64
+ comm.barrier()
65
+ outdir = Path(outdir)
66
+ mesh_name = outdir / f"{case}.msh"
67
+
68
+ geometry = utils.gmsh2dolfin(comm=comm, msh_file=mesh_name)
69
+
70
+ if comm.rank == 0:
71
+ (outdir / "markers.json").write_text(
72
+ json.dumps(geometry.markers, default=utils.json_serial)
73
+ )
74
+ (outdir / "info.json").write_text(
75
+ json.dumps(
76
+ {
77
+ "mode": mode,
78
+ "std": std,
79
+ "case": case,
80
+ "char_length_max": char_length_max,
81
+ "char_length_min": char_length_min,
82
+ "fiber_angle_endo": fiber_angle_endo,
83
+ "fiber_angle_epi": fiber_angle_epi,
84
+ "fiber_space": fiber_space,
85
+ "cardiac_geometry_version": __version__,
86
+ "type": "ukb",
87
+ "timestamp": datetime.datetime.now().isoformat(),
88
+ }
89
+ )
90
+ )
91
+
92
+ try:
93
+ import ldrb
94
+ except ImportError:
95
+ msg = (
96
+ "To create fibers you need to install the ldrb package "
97
+ "which you can install with pip install fenicsx-ldrb"
98
+ )
99
+ raise ImportError(msg)
100
+
101
+ # base_marker = 3
102
+ # indices = []
103
+ # for k in ["PV", "TV", "AV", "MV"]:
104
+ # indices.append(geometry.ffun.find(geometry.markers[k][0]))
105
+ # indices = np.hstack(comm.allreduce(indices, op=MPI.SUM))
106
+ # values = np.full(len(indices), base_marker, dtype=np.int32)
107
+
108
+ markers = {
109
+ "lv": [geometry.markers["LV"][0]],
110
+ "rv": [geometry.markers["RV"][0]],
111
+ "epi": [geometry.markers["EPI"][0]],
112
+ "base": [
113
+ geometry.markers["PV"][0],
114
+ geometry.markers["TV"][0],
115
+ geometry.markers["AV"][0],
116
+ geometry.markers["MV"][0],
117
+ ],
118
+ }
119
+ system = ldrb.dolfinx_ldrb(
120
+ mesh=geometry.mesh,
121
+ ffun=geometry.ffun,
122
+ markers=markers,
123
+ alpha_endo_lv=fiber_angle_endo,
124
+ alpha_epi_lv=fiber_angle_epi,
125
+ beta_endo_lv=0,
126
+ beta_epi_lv=0,
127
+ fiber_space=fiber_space,
128
+ )
129
+
130
+ save_microstructure(
131
+ mesh=geometry.mesh,
132
+ functions=(system.f0, system.s0, system.n0),
133
+ outdir=outdir,
134
+ )
135
+
136
+ for k, v in system._asdict().items():
137
+ if v is None:
138
+ continue
139
+ if fiber_space.startswith("Q"):
140
+ # Cannot visualize Quadrature spaces yet
141
+ continue
142
+
143
+ logger.debug(f"Write {k}: {v}")
144
+ with dolfinx.io.VTXWriter(comm, outdir / f"{k}-viz.bp", [v], engine="BP4") as vtx:
145
+ vtx.write(0.0)
146
+
147
+ geo = Geometry.from_folder(comm=comm, folder=outdir)
148
+ return geo
149
+
150
+
21
151
  def biv_ellipsoid(
22
152
  outdir: str | Path,
23
153
  char_length: float = 0.5,
@@ -199,10 +329,11 @@ def biv_ellipsoid(
199
329
  beta_epi_lv=0,
200
330
  fiber_space=fiber_space,
201
331
  )
202
- from .fibers.utils import save_microstructure
203
332
 
204
333
  save_microstructure(
205
- mesh=geometry.mesh, functions=(system.f0, system.s0, system.n0), outdir=outdir
334
+ mesh=geometry.mesh,
335
+ functions=(system.f0, system.s0, system.n0),
336
+ outdir=outdir,
206
337
  )
207
338
 
208
339
  geo = Geometry.from_folder(comm=comm, folder=outdir)
@@ -10,6 +10,7 @@ from mpi4py import MPI
10
10
  import basix
11
11
  import dolfinx
12
12
  import numpy as np
13
+ from dolfinx.graph import adjacencylist
13
14
  from packaging.version import Version
14
15
  from structlog import get_logger
15
16
 
@@ -184,7 +185,8 @@ def model_to_mesh(
184
185
  )
185
186
 
186
187
  mesh.topology.create_connectivity(mesh.topology.dim, 0)
187
- adj = dolfinx.cpp.graph.AdjacencyList_int32(local_entities)
188
+ # adj = dolfinx.cpp.graph.AdjacencyList_int32(local_entities)
189
+ adj = adjacencylist(local_entities)
188
190
  ct = dolfinx.mesh.meshtags_from_entities(
189
191
  mesh, mesh.topology.dim, adj, local_values.astype(np.int32, copy=False)
190
192
  )
@@ -213,7 +215,7 @@ def model_to_mesh(
213
215
  )
214
216
 
215
217
  mesh.topology.create_connectivity(topology.dim - 1, tdim)
216
- adj = dolfinx.cpp.graph.AdjacencyList_int32(local_entities)
218
+ adj = adjacencylist(local_entities)
217
219
 
218
220
  ft = dolfinx.io.gmshio.meshtags_from_entities(
219
221
  mesh, tdim - 1, adj, local_values.astype(np.int32, copy=False)
@@ -236,7 +238,7 @@ def model_to_mesh(
236
238
  mesh, tdim - 2, marked_edges, edge_values
237
239
  )
238
240
  mesh.topology.create_connectivity(topology.dim - 2, tdim)
239
- adj = dolfinx.cpp.graph.AdjacencyList_int32(local_entities)
241
+ adj = adjacencylist(local_entities)
240
242
  et = dolfinx.io.gmshio.meshtags_from_entities(
241
243
  mesh, tdim - 2, adj, local_values.astype(np.int32, copy=False)
242
244
  )
@@ -258,7 +260,7 @@ def model_to_mesh(
258
260
  mesh, tdim - 3, marked_vertices, vertex_values
259
261
  )
260
262
  mesh.topology.create_connectivity(topology.dim - 3, tdim)
261
- adj = dolfinx.cpp.graph.AdjacencyList_int32(local_entities)
263
+ adj = adjacencylist(local_entities)
262
264
  vt = dolfinx.io.gmshio.meshtags_from_entities(
263
265
  mesh, tdim - 3, adj, local_values.astype(np.int32, copy=False)
264
266
  )
@@ -420,49 +422,89 @@ def read_mesh(
420
422
  def gmsh2dolfin(comm: MPI.Intracomm, msh_file, rank: int = 0) -> GMshGeometry:
421
423
  logger.debug(f"Convert file {msh_file} to dolfin")
422
424
  outdir = Path(msh_file).parent
425
+ outdir.mkdir(parents=True, exist_ok=True)
426
+
427
+ if Version(dolfinx.__version__) >= Version("0.10.0"):
428
+ mesh_data = dolfinx.io.gmshio.read_from_msh(comm=comm, filename=msh_file)
429
+ mesh = mesh_data.mesh
430
+ markers_ = mesh_data.physical_groups
431
+ ct = mesh_data.cell_tags
432
+ tdim = mesh.topology.dim
433
+ if ct is None:
434
+ ct = dolfinx.mesh.meshtags(
435
+ mesh, tdim, np.empty(0, dtype=np.int32), np.empty(0, dtype=np.int32)
436
+ )
437
+
438
+ ft = mesh_data.facet_tags
439
+ if ft is None:
440
+ ft = dolfinx.mesh.meshtags(
441
+ mesh, tdim - 1, np.empty(0, dtype=np.int32), np.empty(0, dtype=np.int32)
442
+ )
443
+ et = mesh_data.edge_tags
444
+ if et is None:
445
+ et = dolfinx.mesh.meshtags(
446
+ mesh, tdim - 2, np.empty(0, dtype=np.int32), np.empty(0, dtype=np.int32)
447
+ )
448
+ vt = mesh_data.vertex_tags
449
+ if vt is None:
450
+ vt = dolfinx.mesh.meshtags(
451
+ mesh, tdim - 3, np.empty(0, dtype=np.int32), np.empty(0, dtype=np.int32)
452
+ )
453
+
454
+ markers = {k: tuple(reversed(v)) for k, v in markers_.items()}
423
455
 
424
- import gmsh
456
+ else:
457
+ import gmsh
458
+
459
+ # We could make this work in parallel in the future
460
+
461
+ if comm.rank == rank:
462
+ gmsh.initialize()
463
+ gmsh.model.add("Mesh from file")
464
+ gmsh.merge(str(msh_file))
465
+ mesh, ct, ft, et, vt = model_to_mesh(gmsh.model, comm, 0)
466
+ markers = {
467
+ gmsh.model.getPhysicalName(*v): tuple(reversed(v))
468
+ for v in gmsh.model.getPhysicalGroups()
469
+ }
470
+ gmsh.finalize()
471
+ else:
472
+ mesh, ct, ft, et, vt = model_to_mesh(gmsh.model, comm, 0)
473
+ markers = {}
425
474
 
426
- # We could make this work in parallel in the future
475
+ markers = comm.bcast(markers, root=rank)
427
476
 
428
- if comm.rank == rank:
429
- gmsh.initialize()
430
- gmsh.model.add("Mesh from file")
431
- gmsh.merge(str(msh_file))
432
- mesh, ct, ft, et, vt = model_to_mesh(gmsh.model, comm, 0)
433
- markers = {
434
- gmsh.model.getPhysicalName(*v): tuple(reversed(v))
435
- for v in gmsh.model.getPhysicalGroups()
436
- }
437
- gmsh.finalize()
438
- else:
439
- mesh, ct, ft, et, vt = model_to_mesh(gmsh.model, comm, 0)
440
- markers = {}
441
477
  mesh.name = "Mesh"
442
478
  ct.name = "Cell tags"
443
479
  ft.name = "Facet tags"
444
480
  et.name = "Edge tags"
445
481
  vt.name = "Vertex tags"
446
482
 
447
- markers = comm.bcast(markers, root=rank)
448
-
449
483
  # Save tags to xdmf
450
484
  with dolfinx.io.XDMFFile(comm, outdir / "mesh.xdmf", "w") as xdmf:
451
485
  xdmf.write_mesh(mesh)
452
486
  xdmf.write_meshtags(
453
- 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",
454
490
  )
455
491
  mesh.topology.create_connectivity(2, 3)
456
492
  xdmf.write_meshtags(
457
- 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",
458
496
  )
459
497
  mesh.topology.create_connectivity(1, 3)
460
498
  xdmf.write_meshtags(
461
- 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",
462
502
  )
463
503
  mesh.topology.create_connectivity(0, 3)
464
504
  xdmf.write_meshtags(
465
- 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",
466
508
  )
467
509
 
468
510
  return GMshGeometry(mesh, ct, ft, et, vt, markers)
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: cardiac-geometriesx
3
- Version: 0.4.4
3
+ Version: 0.4.6
4
4
  Summary: A python library for cardiac geometries
5
5
  Author-email: Henrik Finsberg <henriknf@simula.no>
6
6
  License: MIT
@@ -33,10 +33,13 @@ Requires-Dist: pyvista[all]>=0.43.0; extra == "docs"
33
33
  Requires-Dist: trame-vuetify; extra == "docs"
34
34
  Requires-Dist: ipywidgets; extra == "docs"
35
35
  Requires-Dist: fenicsx-ldrb; extra == "docs"
36
+ Requires-Dist: ukb-atlas; extra == "docs"
36
37
  Provides-Extra: test
37
38
  Requires-Dist: pre-commit; extra == "test"
38
39
  Requires-Dist: pytest; extra == "test"
39
40
  Requires-Dist: pytest-cov; extra == "test"
41
+ Requires-Dist: ukb-atlas; extra == "test"
42
+ Requires-Dist: fenicsx-ldrb; extra == "test"
40
43
  Provides-Extra: gui
41
44
  Requires-Dist: streamlit; extra == "gui"
42
45
  Requires-Dist: stpyvista; extra == "gui"
@@ -21,6 +21,7 @@ pyvista[all]>=0.43.0
21
21
  trame-vuetify
22
22
  ipywidgets
23
23
  fenicsx-ldrb
24
+ ukb-atlas
24
25
 
25
26
  [gui]
26
27
  streamlit
@@ -34,3 +35,5 @@ fenicsx-ldrb
34
35
  pre-commit
35
36
  pytest
36
37
  pytest-cov
38
+ ukb-atlas
39
+ fenicsx-ldrb
@@ -58,3 +58,31 @@ def test_script_no_fibers(script, tmp_path: Path):
58
58
 
59
59
  geo = Geometry.from_folder(comm=comm, folder=path)
60
60
  assert geo.mesh.geometry.dim == 3
61
+
62
+
63
+ @pytest.mark.parametrize("case", ["ED", "ES"])
64
+ def test_ukb(tmp_path: Path, case: str):
65
+ runner = CliRunner()
66
+
67
+ comm = MPI.COMM_WORLD
68
+ path = comm.bcast(tmp_path, root=0)
69
+
70
+ res = runner.invoke(
71
+ cli.ukb,
72
+ [
73
+ path.as_posix(),
74
+ "--case",
75
+ case,
76
+ "--char-length-max",
77
+ "10.0",
78
+ "--char-length-min",
79
+ "10.0",
80
+ ],
81
+ )
82
+ assert res.exit_code == 0
83
+ assert path.is_dir()
84
+
85
+ assert (path / "mesh.xdmf").exists()
86
+ assert (path / f"{case}.msh").exists()
87
+ geo = Geometry.from_folder(comm=comm, folder=path)
88
+ assert geo.mesh.geometry.dim == 3