cardiac-geometriesx 0.0.1__tar.gz → 0.0.4__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.
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/PKG-INFO +1 -3
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/pyproject.toml +26 -6
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/src/cardiac_geometries/__init__.py +1 -5
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/src/cardiac_geometries/cli.py +0 -1
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/src/cardiac_geometries/fibers/__init__.py +1 -3
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/src/cardiac_geometries/geometry.py +6 -10
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/src/cardiac_geometries/mesh.py +37 -10
- cardiac_geometriesx-0.0.4/src/cardiac_geometries/utils.py +122 -0
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/src/cardiac_geometriesx.egg-info/PKG-INFO +1 -3
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/src/cardiac_geometriesx.egg-info/requires.txt +0 -2
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/tests/test_cli.py +4 -3
- cardiac_geometriesx-0.0.1/src/cardiac_geometries/utils.py +0 -173
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/README.md +0 -0
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/setup.cfg +0 -0
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/src/cardiac_geometries/fibers/lv_ellipsoid.py +2 -2
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/src/cardiac_geometries/fibers/slab.py +1 -1
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/src/cardiac_geometries/fibers/utils.py +4 -4
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/src/cardiac_geometriesx.egg-info/SOURCES.txt +0 -0
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/src/cardiac_geometriesx.egg-info/dependency_links.txt +0 -0
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/src/cardiac_geometriesx.egg-info/entry_points.txt +0 -0
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/src/cardiac_geometriesx.egg-info/not-zip-safe +0 -0
- {cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/src/cardiac_geometriesx.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cardiac-geometriesx
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: A python library for cardiac geometries
|
|
5
5
|
Author-email: Henrik Finsberg <henriknf@simula.no>
|
|
6
6
|
License: MIT
|
|
@@ -12,8 +12,6 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
12
12
|
Requires-Python: >=3.8
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
Requires-Dist: fenics-dolfinx>=0.8.0
|
|
15
|
-
Requires-Dist: meshio
|
|
16
|
-
Requires-Dist: h5py
|
|
17
15
|
Requires-Dist: structlog
|
|
18
16
|
Requires-Dist: cardiac-geometries-core
|
|
19
17
|
Requires-Dist: rich-click
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cardiac-geometriesx"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.4"
|
|
8
8
|
description = "A python library for cardiac geometries"
|
|
9
9
|
authors = [{name = "Henrik Finsberg", email = "henriknf@simula.no"}]
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -18,8 +18,6 @@ urls = {Homepage = "https://github.com/finsberg/cardiac-geometriesx" }
|
|
|
18
18
|
requires-python = ">=3.8"
|
|
19
19
|
dependencies = [
|
|
20
20
|
"fenics-dolfinx>=0.8.0",
|
|
21
|
-
"meshio",
|
|
22
|
-
"h5py",
|
|
23
21
|
"structlog",
|
|
24
22
|
"cardiac-geometries-core",
|
|
25
23
|
"rich-click",
|
|
@@ -112,11 +110,11 @@ target-version = "py310"
|
|
|
112
110
|
|
|
113
111
|
[tool.ruff.lint]
|
|
114
112
|
# Enable pycodestyle (`E`) and Pyflakes (`F`) codes by default.
|
|
115
|
-
select = ["E", "F"]
|
|
113
|
+
select = ["E", "F", "I"]
|
|
116
114
|
ignore = ["E402", "E741", "E743", "E731"]
|
|
117
115
|
|
|
118
116
|
# Allow autofix for all enabled rules (when `--fix`) is provided.
|
|
119
|
-
fixable = ["A", "B", "C", "D", "E", "F"]
|
|
117
|
+
fixable = ["A", "B", "C", "D", "E", "F", "I"]
|
|
120
118
|
unfixable = []
|
|
121
119
|
|
|
122
120
|
# Allow unused variables when underscore-prefixed.
|
|
@@ -127,6 +125,28 @@ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|
|
127
125
|
# Unlike Flake8, default to a complexity level of 10.
|
|
128
126
|
max-complexity = 10
|
|
129
127
|
|
|
128
|
+
[tool.ruff.lint.isort]
|
|
129
|
+
known-first-party = ["cardiac_geometries"]
|
|
130
|
+
known-third-party = [
|
|
131
|
+
"basix",
|
|
132
|
+
"dolfinx",
|
|
133
|
+
"ffcx",
|
|
134
|
+
"ufl",
|
|
135
|
+
"gmsh",
|
|
136
|
+
"numpy",
|
|
137
|
+
"pytest",
|
|
138
|
+
]
|
|
139
|
+
section-order = [
|
|
140
|
+
"future",
|
|
141
|
+
"standard-library",
|
|
142
|
+
"mpi",
|
|
143
|
+
"third-party",
|
|
144
|
+
"first-party",
|
|
145
|
+
"local-folder",
|
|
146
|
+
]
|
|
147
|
+
[tool.ruff.lint.isort.sections]
|
|
148
|
+
"mpi" = ["mpi4py", "petsc4py"]
|
|
149
|
+
|
|
130
150
|
|
|
131
151
|
[tool.bumpversion]
|
|
132
152
|
allow_dirty = false
|
|
@@ -136,7 +156,7 @@ tag = true
|
|
|
136
156
|
sign_tags = false
|
|
137
157
|
tag_name = "v{new_version}"
|
|
138
158
|
tag_message = "Bump version: {current_version} → {new_version}"
|
|
139
|
-
current_version = "0.0.
|
|
159
|
+
current_version = "0.0.4"
|
|
140
160
|
|
|
141
161
|
|
|
142
162
|
[[tool.bumpversion.files]]
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
from . import fibers
|
|
2
|
-
from . import geometry
|
|
1
|
+
from . import cli, fibers, geometry, mesh, utils
|
|
3
2
|
from .geometry import Geometry
|
|
4
|
-
from . import mesh
|
|
5
|
-
from . import utils
|
|
6
|
-
from . import cli
|
|
7
3
|
|
|
8
4
|
__all__ = ["mesh", "fibers", "geometry", "Geometry", "utils", "cli"]
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
from dataclasses import dataclass, field
|
|
2
1
|
import json
|
|
2
|
+
import shutil
|
|
3
|
+
from dataclasses import dataclass, field
|
|
3
4
|
from pathlib import Path
|
|
4
|
-
|
|
5
|
+
|
|
5
6
|
from mpi4py import MPI
|
|
6
|
-
|
|
7
|
+
|
|
7
8
|
import adios4dolfinx
|
|
9
|
+
import dolfinx
|
|
8
10
|
import numpy as np
|
|
9
11
|
|
|
10
12
|
from . import utils
|
|
@@ -85,16 +87,10 @@ class Geometry:
|
|
|
85
87
|
|
|
86
88
|
# Read mesh
|
|
87
89
|
if (folder / "mesh.xdmf").exists():
|
|
88
|
-
mesh, cfun = utils.read_mesh(comm=comm, filename=folder / "mesh.xdmf")
|
|
90
|
+
mesh, cfun, ffun = utils.read_mesh(comm=comm, filename=folder / "mesh.xdmf")
|
|
89
91
|
else:
|
|
90
92
|
raise ValueError("No mesh file found")
|
|
91
93
|
|
|
92
|
-
# Read meshtags
|
|
93
|
-
if (folder / "triangle_mesh.xdmf").exists():
|
|
94
|
-
ffun = utils.read_ffun(mesh=mesh, filename=folder / "triangle_mesh.xdmf")
|
|
95
|
-
else:
|
|
96
|
-
ffun = None
|
|
97
|
-
|
|
98
94
|
# Read markers
|
|
99
95
|
if (folder / "markers.json").exists():
|
|
100
96
|
if comm.rank == 0:
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
import math
|
|
3
|
-
import json
|
|
4
1
|
import datetime
|
|
2
|
+
import json
|
|
3
|
+
import math
|
|
5
4
|
from importlib.metadata import metadata
|
|
6
|
-
from
|
|
5
|
+
from pathlib import Path
|
|
7
6
|
|
|
8
7
|
from mpi4py import MPI
|
|
8
|
+
|
|
9
9
|
import cardiac_geometries_core as cgc
|
|
10
|
+
from structlog import get_logger
|
|
10
11
|
|
|
11
12
|
from . import utils
|
|
12
13
|
from .geometry import Geometry
|
|
@@ -42,6 +43,7 @@ def biv_ellipsoid(
|
|
|
42
43
|
fiber_angle_endo: float = -60,
|
|
43
44
|
fiber_angle_epi: float = +60,
|
|
44
45
|
fiber_space: str = "P_1",
|
|
46
|
+
verbose: bool = False,
|
|
45
47
|
) -> Geometry:
|
|
46
48
|
"""Create BiV ellipsoidal geometry
|
|
47
49
|
|
|
@@ -95,6 +97,8 @@ def biv_ellipsoid(
|
|
|
95
97
|
Angle for the epicardium, by default +60
|
|
96
98
|
fiber_space : str, optional
|
|
97
99
|
Function space for fibers of the form family_degree, by default "P_1"
|
|
100
|
+
verbose : bool, optional
|
|
101
|
+
If True print information from gmsh, by default False
|
|
98
102
|
|
|
99
103
|
Returns
|
|
100
104
|
-------
|
|
@@ -164,9 +168,10 @@ def biv_ellipsoid(
|
|
|
164
168
|
a_epi_rv=a_epi_rv,
|
|
165
169
|
b_epi_rv=b_epi_rv,
|
|
166
170
|
c_epi_rv=c_epi_rv,
|
|
171
|
+
verbose=verbose,
|
|
167
172
|
)
|
|
168
173
|
comm.barrier()
|
|
169
|
-
geometry = utils.gmsh2dolfin(comm=comm, msh_file=mesh_name
|
|
174
|
+
geometry = utils.gmsh2dolfin(comm=comm, msh_file=mesh_name)
|
|
170
175
|
|
|
171
176
|
if comm.rank == 0:
|
|
172
177
|
with open(outdir / "markers.json", "w") as f:
|
|
@@ -220,6 +225,7 @@ def biv_ellipsoid_torso(
|
|
|
220
225
|
fiber_angle_endo: float = -60,
|
|
221
226
|
fiber_angle_epi: float = +60,
|
|
222
227
|
fiber_space: str = "P_1",
|
|
228
|
+
verbose: bool = False,
|
|
223
229
|
) -> Geometry:
|
|
224
230
|
"""Create BiV ellipsoidal geometry
|
|
225
231
|
|
|
@@ -285,6 +291,8 @@ def biv_ellipsoid_torso(
|
|
|
285
291
|
Angle for the epicardium, by default +60
|
|
286
292
|
fiber_space : str, optional
|
|
287
293
|
Function space for fibers of the form family_degree, by default "P_1"
|
|
294
|
+
verbose : bool, optional
|
|
295
|
+
If True print information from gmsh, by default False
|
|
288
296
|
|
|
289
297
|
Returns
|
|
290
298
|
-------
|
|
@@ -364,10 +372,11 @@ def biv_ellipsoid_torso(
|
|
|
364
372
|
a_epi_rv=a_epi_rv,
|
|
365
373
|
b_epi_rv=b_epi_rv,
|
|
366
374
|
c_epi_rv=c_epi_rv,
|
|
375
|
+
verbose=verbose,
|
|
367
376
|
)
|
|
368
377
|
comm.barrier()
|
|
369
378
|
|
|
370
|
-
geometry = utils.gmsh2dolfin(comm=comm, msh_file=mesh_name
|
|
379
|
+
geometry = utils.gmsh2dolfin(comm=comm, msh_file=mesh_name)
|
|
371
380
|
|
|
372
381
|
if comm.rank == 0:
|
|
373
382
|
with open(outdir / "markers.json", "w") as f:
|
|
@@ -412,6 +421,7 @@ def lv_ellipsoid(
|
|
|
412
421
|
fiber_angle_epi: float = +60,
|
|
413
422
|
fiber_space: str = "P_1",
|
|
414
423
|
aha: bool = True,
|
|
424
|
+
verbose: bool = False,
|
|
415
425
|
) -> Geometry:
|
|
416
426
|
"""Create an LV ellipsoidal geometry
|
|
417
427
|
|
|
@@ -447,6 +457,8 @@ def lv_ellipsoid(
|
|
|
447
457
|
Function space for fibers of the form family_degree, by default "P_1"
|
|
448
458
|
aha : bool, optional
|
|
449
459
|
If True create 17-segment AHA regions
|
|
460
|
+
verbose : bool, optional
|
|
461
|
+
If True print information from gmsh, by default False
|
|
450
462
|
|
|
451
463
|
Returns
|
|
452
464
|
-------
|
|
@@ -497,10 +509,11 @@ def lv_ellipsoid(
|
|
|
497
509
|
mu_apex_endo=mu_apex_endo,
|
|
498
510
|
mu_apex_epi=mu_apex_epi,
|
|
499
511
|
psize_ref=psize_ref,
|
|
512
|
+
verbose=verbose,
|
|
500
513
|
)
|
|
501
514
|
comm.barrier()
|
|
502
515
|
|
|
503
|
-
geometry = utils.gmsh2dolfin(comm=comm, msh_file=mesh_name
|
|
516
|
+
geometry = utils.gmsh2dolfin(comm=comm, msh_file=mesh_name)
|
|
504
517
|
|
|
505
518
|
# if aha:
|
|
506
519
|
# from .aha import lv_aha
|
|
@@ -559,6 +572,7 @@ def slab(
|
|
|
559
572
|
fiber_angle_endo: float = -60,
|
|
560
573
|
fiber_angle_epi: float = +60,
|
|
561
574
|
fiber_space: str = "P_1",
|
|
575
|
+
verbose: bool = False,
|
|
562
576
|
) -> Geometry:
|
|
563
577
|
"""Create slab geometry
|
|
564
578
|
|
|
@@ -582,6 +596,8 @@ def slab(
|
|
|
582
596
|
Angle for the epicardium, by default +60
|
|
583
597
|
fiber_space : str, optional
|
|
584
598
|
Function space for fibers of the form family_degree, by default "P_1"
|
|
599
|
+
verbose : bool, optional
|
|
600
|
+
If True print information from gmsh, by default False
|
|
585
601
|
|
|
586
602
|
Returns
|
|
587
603
|
-------
|
|
@@ -615,10 +631,17 @@ def slab(
|
|
|
615
631
|
default=utils.json_serial,
|
|
616
632
|
)
|
|
617
633
|
|
|
618
|
-
cgc.slab(
|
|
634
|
+
cgc.slab(
|
|
635
|
+
mesh_name=mesh_name.as_posix(),
|
|
636
|
+
lx=lx,
|
|
637
|
+
ly=ly,
|
|
638
|
+
lz=lz,
|
|
639
|
+
dx=dx,
|
|
640
|
+
verbose=verbose,
|
|
641
|
+
)
|
|
619
642
|
comm.barrier()
|
|
620
643
|
|
|
621
|
-
geometry = utils.gmsh2dolfin(comm=comm, msh_file=mesh_name
|
|
644
|
+
geometry = utils.gmsh2dolfin(comm=comm, msh_file=mesh_name)
|
|
622
645
|
|
|
623
646
|
if comm.rank == 0:
|
|
624
647
|
with open(outdir / "markers.json", "w") as f:
|
|
@@ -651,6 +674,7 @@ def slab_in_bath(
|
|
|
651
674
|
by: float = 0.0,
|
|
652
675
|
bz: float = 0.1,
|
|
653
676
|
dx: float = 0.001,
|
|
677
|
+
verbose: bool = False,
|
|
654
678
|
) -> Geometry:
|
|
655
679
|
"""Create slab geometry
|
|
656
680
|
|
|
@@ -672,6 +696,8 @@ def slab_in_bath(
|
|
|
672
696
|
Thickness of bath the z-direction, by default 0.1
|
|
673
697
|
dx : float, optional
|
|
674
698
|
Element size, by default 0.001
|
|
699
|
+
verbose : bool, optional
|
|
700
|
+
If True print information from gmsh, by default False
|
|
675
701
|
|
|
676
702
|
Returns
|
|
677
703
|
-------
|
|
@@ -714,9 +740,10 @@ def slab_in_bath(
|
|
|
714
740
|
by=by,
|
|
715
741
|
bz=bz,
|
|
716
742
|
dx=dx,
|
|
743
|
+
verbose=verbose,
|
|
717
744
|
)
|
|
718
745
|
|
|
719
|
-
geometry = utils.gmsh2dolfin(comm=comm, msh_file=mesh_name
|
|
746
|
+
geometry = utils.gmsh2dolfin(comm=comm, msh_file=mesh_name)
|
|
720
747
|
|
|
721
748
|
if comm.rank == 0:
|
|
722
749
|
with open(outdir / "markers.json", "w") as f:
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import tempfile
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Iterable, NamedTuple
|
|
5
|
+
|
|
6
|
+
from mpi4py import MPI
|
|
7
|
+
|
|
8
|
+
import basix
|
|
9
|
+
import dolfinx
|
|
10
|
+
import numpy as np
|
|
11
|
+
from structlog import get_logger
|
|
12
|
+
|
|
13
|
+
logger = get_logger()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def element2array(el: basix.finite_element.FiniteElement) -> np.ndarray:
|
|
17
|
+
return np.array(
|
|
18
|
+
[int(el.family), int(el.cell_type), int(el.degree), int(el.discontinuous)],
|
|
19
|
+
dtype=np.uint8,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def number2Enum(num: int, enum: Iterable) -> Enum:
|
|
24
|
+
for e in enum:
|
|
25
|
+
if int(e) == num:
|
|
26
|
+
return e
|
|
27
|
+
raise ValueError(f"Invalid value {num} for enum {enum}")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def array2element(arr: np.ndarray) -> basix.finite_element.FiniteElement:
|
|
31
|
+
family = number2Enum(arr[0], basix.ElementFamily)
|
|
32
|
+
cell_type = number2Enum(arr[1], basix.CellType)
|
|
33
|
+
degree = int(arr[2])
|
|
34
|
+
discontinuous = bool(arr[3])
|
|
35
|
+
# TODO: Shape is hardcoded to (3,) for now, but this should also be stored
|
|
36
|
+
return basix.ufl.element(
|
|
37
|
+
family=family,
|
|
38
|
+
cell=cell_type,
|
|
39
|
+
degree=degree,
|
|
40
|
+
discontinuous=discontinuous,
|
|
41
|
+
shape=(3,),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def handle_mesh_name(mesh_name: str = "") -> Path:
|
|
46
|
+
if mesh_name == "":
|
|
47
|
+
fd, mesh_name = tempfile.mkstemp(suffix=".msh")
|
|
48
|
+
return Path(mesh_name).with_suffix(".msh")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def json_serial(obj):
|
|
52
|
+
if isinstance(obj, (np.ndarray)):
|
|
53
|
+
return obj.tolist()
|
|
54
|
+
else:
|
|
55
|
+
try:
|
|
56
|
+
return str(obj)
|
|
57
|
+
except Exception:
|
|
58
|
+
raise TypeError("Type %s not serializable" % type(obj))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class GMshGeometry(NamedTuple):
|
|
62
|
+
mesh: dolfinx.mesh.Mesh
|
|
63
|
+
cfun: dolfinx.mesh.MeshTags | None
|
|
64
|
+
ffun: dolfinx.mesh.MeshTags | None
|
|
65
|
+
efun: dolfinx.mesh.MeshTags | None
|
|
66
|
+
vfun: dolfinx.mesh.MeshTags | None
|
|
67
|
+
markers: dict[str, tuple[int, int]]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def read_mesh(
|
|
71
|
+
comm, filename: str | Path
|
|
72
|
+
) -> tuple[dolfinx.mesh.Mesh, dolfinx.mesh.MeshTags | None, dolfinx.mesh.MeshTags | None]:
|
|
73
|
+
with dolfinx.io.XDMFFile(comm, filename, "r") as xdmf:
|
|
74
|
+
mesh = xdmf.read_mesh(name="Mesh")
|
|
75
|
+
cfun = xdmf.read_meshtags(mesh, name="Cell tags")
|
|
76
|
+
mesh.topology.create_connectivity(2, 3)
|
|
77
|
+
ffun = xdmf.read_meshtags(mesh, name="Facet tags")
|
|
78
|
+
return mesh, cfun, ffun
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def gmsh2dolfin(comm: MPI.Intracomm, msh_file, rank: int = 0) -> GMshGeometry:
|
|
82
|
+
logger.debug(f"Convert file {msh_file} to dolfin")
|
|
83
|
+
outdir = Path(msh_file).parent
|
|
84
|
+
|
|
85
|
+
import gmsh
|
|
86
|
+
|
|
87
|
+
# We could make this work in parallel in the future
|
|
88
|
+
|
|
89
|
+
if comm.rank == rank:
|
|
90
|
+
gmsh.initialize()
|
|
91
|
+
gmsh.model.add("Mesh from file")
|
|
92
|
+
gmsh.merge(str(msh_file))
|
|
93
|
+
mesh, ct, ft = dolfinx.io.gmshio.model_to_mesh(gmsh.model, comm, 0)
|
|
94
|
+
markers = {
|
|
95
|
+
gmsh.model.getPhysicalName(*v): tuple(reversed(v))
|
|
96
|
+
for v in gmsh.model.getPhysicalGroups()
|
|
97
|
+
}
|
|
98
|
+
gmsh.finalize()
|
|
99
|
+
else:
|
|
100
|
+
mesh, ct, ft = dolfinx.io.gmshio.model_to_mesh(gmsh.model, comm, 0)
|
|
101
|
+
markers = {}
|
|
102
|
+
mesh.name = "Mesh"
|
|
103
|
+
ct.name = "Cell tags"
|
|
104
|
+
ft.name = "Facet tags"
|
|
105
|
+
|
|
106
|
+
markers = comm.bcast(markers, root=rank)
|
|
107
|
+
|
|
108
|
+
# Save tags to xdmf
|
|
109
|
+
with dolfinx.io.XDMFFile(comm, outdir / "mesh.xdmf", "w") as xdmf:
|
|
110
|
+
xdmf.write_mesh(mesh)
|
|
111
|
+
mesh.topology.create_connectivity(2, 3)
|
|
112
|
+
xdmf.write_meshtags(
|
|
113
|
+
ct, mesh.geometry, geometry_xpath=f"/Xdmf/Domain/Grid[@Name='{mesh.name}']/Geometry"
|
|
114
|
+
)
|
|
115
|
+
xdmf.write_meshtags(
|
|
116
|
+
ft, mesh.geometry, geometry_xpath=f"/Xdmf/Domain/Grid[@Name='{mesh.name}']/Geometry"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
vfun = None
|
|
120
|
+
efun = None
|
|
121
|
+
|
|
122
|
+
return GMshGeometry(mesh, ct, ft, efun, vfun, markers)
|
{cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/src/cardiac_geometriesx.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cardiac-geometriesx
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: A python library for cardiac geometries
|
|
5
5
|
Author-email: Henrik Finsberg <henriknf@simula.no>
|
|
6
6
|
License: MIT
|
|
@@ -12,8 +12,6 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
12
12
|
Requires-Python: >=3.8
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
Requires-Dist: fenics-dolfinx>=0.8.0
|
|
15
|
-
Requires-Dist: meshio
|
|
16
|
-
Requires-Dist: h5py
|
|
17
15
|
Requires-Dist: structlog
|
|
18
16
|
Requires-Dist: cardiac-geometries-core
|
|
19
17
|
Requires-Dist: rich-click
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
from mpi4py import MPI
|
|
4
|
-
from cardiac_geometries import cli
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
import pytest
|
|
7
6
|
from click.testing import CliRunner
|
|
8
7
|
|
|
8
|
+
from cardiac_geometries import Geometry, cli
|
|
9
|
+
|
|
9
10
|
|
|
10
11
|
@pytest.mark.parametrize(
|
|
11
12
|
"script",
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
import tempfile
|
|
3
|
-
from typing import NamedTuple, Iterable
|
|
4
|
-
from enum import Enum
|
|
5
|
-
|
|
6
|
-
import numpy as np
|
|
7
|
-
from mpi4py import MPI
|
|
8
|
-
from structlog import get_logger
|
|
9
|
-
import basix
|
|
10
|
-
import dolfinx
|
|
11
|
-
import meshio
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
logger = get_logger()
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def element2array(el: basix.finite_element.FiniteElement) -> np.ndarray:
|
|
18
|
-
return np.array(
|
|
19
|
-
[int(el.family), int(el.cell_type), int(el.degree), int(el.discontinuous)],
|
|
20
|
-
dtype=np.uint8,
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def number2Enum(num: int, enum: Iterable) -> Enum:
|
|
25
|
-
for e in enum:
|
|
26
|
-
if int(e) == num:
|
|
27
|
-
return e
|
|
28
|
-
raise ValueError(f"Invalid value {num} for enum {enum}")
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def array2element(arr: np.ndarray) -> basix.finite_element.FiniteElement:
|
|
32
|
-
family = number2Enum(arr[0], basix.ElementFamily)
|
|
33
|
-
cell_type = number2Enum(arr[1], basix.CellType)
|
|
34
|
-
degree = int(arr[2])
|
|
35
|
-
discontinuous = bool(arr[3])
|
|
36
|
-
# TODO: Shape is hardcoded to (3,) for now, but this should also be stored
|
|
37
|
-
return basix.ufl.element(
|
|
38
|
-
family=family,
|
|
39
|
-
cell=cell_type,
|
|
40
|
-
degree=degree,
|
|
41
|
-
discontinuous=discontinuous,
|
|
42
|
-
shape=(3,),
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def handle_mesh_name(mesh_name: str = "") -> Path:
|
|
47
|
-
if mesh_name == "":
|
|
48
|
-
fd, mesh_name = tempfile.mkstemp(suffix=".msh")
|
|
49
|
-
return Path(mesh_name).with_suffix(".msh")
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def json_serial(obj):
|
|
53
|
-
if isinstance(obj, (np.ndarray)):
|
|
54
|
-
return obj.tolist()
|
|
55
|
-
else:
|
|
56
|
-
try:
|
|
57
|
-
return str(obj)
|
|
58
|
-
except Exception:
|
|
59
|
-
raise TypeError("Type %s not serializable" % type(obj))
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
class GMshGeometry(NamedTuple):
|
|
63
|
-
mesh: dolfinx.mesh.Mesh
|
|
64
|
-
cfun: dolfinx.mesh.MeshTags | None
|
|
65
|
-
ffun: dolfinx.mesh.MeshTags | None
|
|
66
|
-
efun: dolfinx.mesh.MeshTags | None
|
|
67
|
-
vfun: dolfinx.mesh.MeshTags | None
|
|
68
|
-
markers: dict[str, tuple[int, int]]
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def create_mesh(mesh, cell_type):
|
|
72
|
-
# From http://jsdokken.com/converted_files/tutorial_pygmsh.html
|
|
73
|
-
cells = mesh.get_cells_type(cell_type)
|
|
74
|
-
if cells.size == 0:
|
|
75
|
-
return None
|
|
76
|
-
|
|
77
|
-
cells = mesh.get_cells_type(cell_type)
|
|
78
|
-
cell_data = mesh.get_cell_data("gmsh:physical", cell_type)
|
|
79
|
-
out_mesh = meshio.Mesh(
|
|
80
|
-
points=mesh.points,
|
|
81
|
-
cells={cell_type: cells},
|
|
82
|
-
cell_data={"name_to_read": [cell_data]},
|
|
83
|
-
)
|
|
84
|
-
return out_mesh
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def read_ffun(mesh, filename: str | Path) -> dolfinx.mesh.MeshTags | None:
|
|
88
|
-
mesh.topology.create_connectivity(mesh.topology.dim - 1, mesh.topology.dim)
|
|
89
|
-
with dolfinx.io.XDMFFile(mesh.comm, filename, "r") as xdmf:
|
|
90
|
-
ffun = xdmf.read_meshtags(mesh, name="Grid")
|
|
91
|
-
ffun.name = "Facet tags"
|
|
92
|
-
return ffun
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def read_mesh(comm, filename: str | Path) -> tuple[dolfinx.mesh.Mesh, dolfinx.mesh.MeshTags | None]:
|
|
96
|
-
with dolfinx.io.XDMFFile(comm, filename, "r") as xdmf:
|
|
97
|
-
mesh = xdmf.read_mesh(name="Grid")
|
|
98
|
-
cfun = xdmf.read_meshtags(mesh, name="Grid")
|
|
99
|
-
return mesh, cfun
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def gmsh2dolfin(comm: MPI.Intracomm, msh_file, unlink: bool = False) -> GMshGeometry:
|
|
103
|
-
logger.debug(f"Convert file {msh_file} to dolfin")
|
|
104
|
-
outdir = Path(msh_file).parent
|
|
105
|
-
|
|
106
|
-
vertex_mesh_name = outdir / "vertex_mesh.xdmf"
|
|
107
|
-
line_mesh_name = outdir / "line_mesh.xdmf"
|
|
108
|
-
triangle_mesh_name = outdir / "triangle_mesh.xdmf"
|
|
109
|
-
tetra_mesh_name = outdir / "mesh.xdmf"
|
|
110
|
-
|
|
111
|
-
if comm.rank == 0:
|
|
112
|
-
msh = meshio.gmsh.read(msh_file)
|
|
113
|
-
vertex_mesh = create_mesh(msh, "vertex")
|
|
114
|
-
line_mesh = create_mesh(msh, "line")
|
|
115
|
-
triangle_mesh = create_mesh(msh, "triangle")
|
|
116
|
-
tetra_mesh = create_mesh(msh, "tetra")
|
|
117
|
-
|
|
118
|
-
if vertex_mesh is not None:
|
|
119
|
-
meshio.write(vertex_mesh_name, vertex_mesh)
|
|
120
|
-
|
|
121
|
-
if line_mesh is not None:
|
|
122
|
-
meshio.write(line_mesh_name, line_mesh)
|
|
123
|
-
|
|
124
|
-
if triangle_mesh is not None:
|
|
125
|
-
meshio.write(triangle_mesh_name, triangle_mesh)
|
|
126
|
-
|
|
127
|
-
if tetra_mesh is not None:
|
|
128
|
-
meshio.write(
|
|
129
|
-
tetra_mesh_name,
|
|
130
|
-
tetra_mesh,
|
|
131
|
-
)
|
|
132
|
-
markers = msh.field_data
|
|
133
|
-
else:
|
|
134
|
-
markers = {}
|
|
135
|
-
# Broadcast markers
|
|
136
|
-
markers = comm.bcast(markers, root=0)
|
|
137
|
-
comm.barrier()
|
|
138
|
-
|
|
139
|
-
mesh, cfun = read_mesh(comm, tetra_mesh_name)
|
|
140
|
-
|
|
141
|
-
if triangle_mesh_name.is_file():
|
|
142
|
-
ffun = read_ffun(mesh, triangle_mesh_name)
|
|
143
|
-
else:
|
|
144
|
-
ffun = None
|
|
145
|
-
|
|
146
|
-
if line_mesh_name.is_file():
|
|
147
|
-
mesh.topology.create_connectivity(mesh.topology.dim - 2, mesh.topology.dim)
|
|
148
|
-
with dolfinx.io.XDMFFile(comm, line_mesh_name, "r") as xdmf:
|
|
149
|
-
efun = xdmf.read_meshtags(mesh, name="Grid")
|
|
150
|
-
else:
|
|
151
|
-
efun = None
|
|
152
|
-
|
|
153
|
-
if vertex_mesh_name.is_file():
|
|
154
|
-
mesh.topology.create_connectivity(mesh.topology.dim - 3, mesh.topology.dim)
|
|
155
|
-
with dolfinx.io.XDMFFile(comm, vertex_mesh_name, "r") as xdmf:
|
|
156
|
-
vfun = xdmf.read_meshtags(mesh, name="Grid")
|
|
157
|
-
else:
|
|
158
|
-
vfun = None
|
|
159
|
-
|
|
160
|
-
if unlink:
|
|
161
|
-
# Wait for all processes to finish reading
|
|
162
|
-
comm.barrier()
|
|
163
|
-
if comm.rank == 0:
|
|
164
|
-
vertex_mesh_name.unlink(missing_ok=True)
|
|
165
|
-
line_mesh_name.unlink(missing_ok=True)
|
|
166
|
-
triangle_mesh_name.unlink(missing_ok=True)
|
|
167
|
-
tetra_mesh_name.unlink(missing_ok=True)
|
|
168
|
-
vertex_mesh_name.with_suffix(".h5").unlink(missing_ok=True)
|
|
169
|
-
line_mesh_name.with_suffix(".h5").unlink(missing_ok=True)
|
|
170
|
-
triangle_mesh_name.with_suffix(".h5").unlink(missing_ok=True)
|
|
171
|
-
tetra_mesh_name.with_suffix(".h5").unlink(missing_ok=True)
|
|
172
|
-
|
|
173
|
-
return GMshGeometry(mesh, cfun, ffun, efun, vfun, markers)
|
|
File without changes
|
|
File without changes
|
{cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/src/cardiac_geometries/fibers/utils.py
RENAMED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
from typing import NamedTuple
|
|
2
1
|
from pathlib import Path
|
|
2
|
+
from typing import NamedTuple
|
|
3
3
|
|
|
4
|
+
import adios4dolfinx
|
|
4
5
|
import dolfinx
|
|
5
|
-
from dolfinx.fem.petsc import LinearProblem
|
|
6
|
-
import ufl
|
|
7
6
|
import numpy as np
|
|
8
|
-
import
|
|
7
|
+
import ufl
|
|
8
|
+
from dolfinx.fem.petsc import LinearProblem
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class Microstructure(NamedTuple):
|
{cardiac_geometriesx-0.0.1 → cardiac_geometriesx-0.0.4}/src/cardiac_geometriesx.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|