cardiac-geometriesx 0.1.2__py3-none-any.whl → 0.2.0__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
@@ -8,8 +8,10 @@ from . import mesh
8
8
 
9
9
  meta = metadata("cardiac-geometriesx")
10
10
  __version__ = meta["Version"]
11
- __author__ = meta["Author"]
11
+ __author__ = meta["Author-email"]
12
12
  __license__ = meta["License"]
13
+ __email__ = meta["Author-email"]
14
+ __program_name__ = meta["Name"]
13
15
 
14
16
 
15
17
  @click.group()
@@ -867,8 +869,20 @@ def slab_in_bath(
867
869
  geo.save(outdir / "slab_in_bath.bp")
868
870
 
869
871
 
872
+ @click.command("gui")
873
+ def gui():
874
+ # Make sure we can import the required packages
875
+ from . import gui # noqa: F401
876
+
877
+ gui_path = Path(__file__).parent.joinpath("gui.py")
878
+ import subprocess as sp
879
+
880
+ sp.run(["streamlit", "run", gui_path.as_posix()])
881
+
882
+
870
883
  app.add_command(lv_ellipsoid)
871
884
  app.add_command(biv_ellipsoid)
872
885
  app.add_command(biv_ellipsoid_torso)
873
886
  app.add_command(slab)
874
887
  app.add_command(slab_in_bath)
888
+ app.add_command(gui)
@@ -1,9 +1,9 @@
1
1
  from pathlib import Path
2
2
 
3
- import basix
4
3
  import dolfinx
5
4
  import numpy as np
6
5
 
6
+ from ..utils import space_from_string
7
7
  from . import utils
8
8
 
9
9
 
@@ -82,14 +82,9 @@ def compute_system(
82
82
  s0 = np.cross(f0, n0, axis=0)
83
83
  s0 = utils.normalize(s0)
84
84
 
85
- el = basix.ufl.element(
86
- element.family_name,
87
- mesh.ufl_cell().cellname(),
88
- degree=element.degree,
89
- discontinuous=element.discontinuous,
90
- shape=(mesh.geometry.dim,),
85
+ Vv = space_from_string(
86
+ space_string=f"{element.family_name}_{element.degree}", mesh=mesh, dim=mesh.geometry.dim
91
87
  )
92
- Vv = dolfinx.fem.functionspace(mesh, el)
93
88
 
94
89
  fiber = dolfinx.fem.Function(Vv)
95
90
  norm_f = np.linalg.norm(f0, axis=0)
@@ -131,10 +126,13 @@ def create_microstructure(
131
126
  )
132
127
 
133
128
  if outdir is not None:
134
- with dolfinx.io.VTXWriter(
135
- mesh.comm, Path(outdir) / "laplace.bp", [t], engine="BP4"
136
- ) as file:
137
- file.write(0.0)
129
+ try:
130
+ with dolfinx.io.VTXWriter(
131
+ mesh.comm, Path(outdir) / "laplace.bp", [t], engine="BP4"
132
+ ) as file:
133
+ file.write(0.0)
134
+ except RuntimeError:
135
+ pass
138
136
 
139
137
  system = compute_system(
140
138
  t,
@@ -1,9 +1,9 @@
1
1
  from pathlib import Path
2
2
 
3
- import basix
4
3
  import dolfinx
5
4
  import numpy as np
6
5
 
6
+ from ..utils import space_from_string
7
7
  from . import utils
8
8
 
9
9
 
@@ -59,14 +59,9 @@ def compute_system(
59
59
  n0 = np.cross(f0, s0, axis=0)
60
60
  n0 = utils.normalize(n0)
61
61
 
62
- el = basix.ufl.element(
63
- element.family_name,
64
- mesh.ufl_cell().cellname(),
65
- degree=element.degree,
66
- discontinuous=element.discontinuous,
67
- shape=(mesh.geometry.dim,),
62
+ Vv = space_from_string(
63
+ space_string=f"{element.family_name}_{element.degree}", mesh=mesh, dim=mesh.geometry.dim
68
64
  )
69
- Vv = dolfinx.fem.functionspace(mesh, el)
70
65
 
71
66
  fiber = dolfinx.fem.Function(Vv)
72
67
  norm_f = np.linalg.norm(f0, axis=0)
@@ -134,10 +129,13 @@ def create_microstructure(
134
129
  function_space=function_space,
135
130
  )
136
131
  if outdir is not None:
137
- with dolfinx.io.VTXWriter(
138
- mesh.comm, Path(outdir) / "laplace.bp", [t], engine="BP4"
139
- ) as file:
140
- file.write(0.0)
132
+ try:
133
+ with dolfinx.io.VTXWriter(
134
+ mesh.comm, Path(outdir) / "laplace.bp", [t], engine="BP4"
135
+ ) as file:
136
+ file.write(0.0)
137
+ except RuntimeError:
138
+ pass
141
139
 
142
140
  system = compute_system(
143
141
  t,
@@ -7,6 +7,8 @@ import numpy as np
7
7
  import ufl
8
8
  from dolfinx.fem.petsc import LinearProblem
9
9
 
10
+ from ..utils import space_from_string
11
+
10
12
 
11
13
  class Microstructure(NamedTuple):
12
14
  f0: dolfinx.fem.Function
@@ -20,17 +22,20 @@ def save_microstructure(
20
22
  from ..utils import element2array
21
23
 
22
24
  # Save for paraview visualization
23
- with dolfinx.io.VTXWriter(
24
- mesh.comm, Path(outdir) / "microstructure-viz.bp", functions, engine="BP4"
25
- ) as file:
26
- file.write(0.0)
25
+ try:
26
+ with dolfinx.io.VTXWriter(
27
+ mesh.comm, Path(outdir) / "microstructure-viz.bp", functions, engine="BP4"
28
+ ) as file:
29
+ file.write(0.0)
30
+ except RuntimeError:
31
+ pass
27
32
 
28
33
  # Save with proper function space
29
34
  filename = Path(outdir) / "microstructure.bp"
30
35
  for function in functions:
31
36
  adios4dolfinx.write_function(u=function, filename=filename)
32
37
 
33
- attributes = {f.name: element2array(f.ufl_element().basix_element) for f in functions}
38
+ attributes = {f.name: element2array(f.ufl_element()) for f in functions}
34
39
  adios4dolfinx.write_attributes(
35
40
  comm=mesh.comm,
36
41
  filename=filename,
@@ -73,10 +78,10 @@ def laplace(
73
78
  uh = problem.solve()
74
79
 
75
80
  if function_space != "P_1":
76
- family, degree = function_space.split("_")
77
- W = dolfinx.fem.functionspace(mesh, (family, int(degree)))
81
+ W = space_from_string(function_space, mesh, dim=1)
78
82
  t = dolfinx.fem.Function(W)
79
- t.interpolate(uh)
83
+ expr = dolfinx.fem.Expression(uh, W.element.interpolation_points())
84
+ t.interpolate(expr)
80
85
  else:
81
86
  t = uh
82
87
 
@@ -15,9 +15,11 @@ from . import utils
15
15
  @dataclass # (frozen=True, slots=True)
16
16
  class Geometry:
17
17
  mesh: dolfinx.mesh.Mesh
18
- markers: dict[str, tuple[str, str]] = field(default_factory=dict)
19
- ffun: dolfinx.mesh.MeshTags | None = None
18
+ markers: dict[str, tuple[int, int]] = field(default_factory=dict)
20
19
  cfun: dolfinx.mesh.MeshTags | None = None
20
+ ffun: dolfinx.mesh.MeshTags | None = None
21
+ efun: dolfinx.mesh.MeshTags | None = None
22
+ vfun: dolfinx.mesh.MeshTags | None = None
21
23
  f0: dolfinx.fem.Function | None = None
22
24
  s0: dolfinx.fem.Function | None = None
23
25
  n0: dolfinx.fem.Function | None = None
@@ -25,8 +27,7 @@ class Geometry:
25
27
  def save(self, path: str | Path) -> None:
26
28
  path = Path(path)
27
29
 
28
- if path.exists() and self.mesh.comm.rank == 0:
29
- shutil.rmtree(path)
30
+ shutil.rmtree(path, ignore_errors=True)
30
31
  self.mesh.comm.barrier()
31
32
  adios4dolfinx.write_mesh(mesh=self.mesh, filename=path)
32
33
 
@@ -37,15 +38,37 @@ class Geometry:
37
38
  attributes={k: np.array(v, dtype=np.uint8) for k, v in self.markers.items()},
38
39
  )
39
40
 
41
+ if self.cfun is not None:
42
+ adios4dolfinx.write_meshtags(
43
+ meshtags=self.cfun,
44
+ mesh=self.mesh,
45
+ filename=path,
46
+ meshtag_name="Cell tags",
47
+ )
40
48
  if self.ffun is not None:
41
49
  adios4dolfinx.write_meshtags(
42
50
  meshtags=self.ffun,
43
51
  mesh=self.mesh,
44
52
  filename=path,
53
+ meshtag_name="Facet tags",
54
+ )
55
+ if self.efun is not None:
56
+ adios4dolfinx.write_meshtags(
57
+ meshtags=self.efun,
58
+ mesh=self.mesh,
59
+ filename=path,
60
+ meshtag_name="Edge tags",
61
+ )
62
+ if self.vfun is not None:
63
+ adios4dolfinx.write_meshtags(
64
+ meshtags=self.vfun,
65
+ mesh=self.mesh,
66
+ filename=path,
67
+ meshtag_name="Vertex tags",
45
68
  )
46
69
 
47
70
  if self.f0 is not None:
48
- el = self.f0.ufl_element().basix_element
71
+ el = self.f0.ufl_element()
49
72
  arr = utils.element2array(el)
50
73
 
51
74
  adios4dolfinx.write_attributes(
@@ -60,26 +83,49 @@ class Geometry:
60
83
  if self.n0 is not None:
61
84
  adios4dolfinx.write_function(u=self.n0, filename=path, name="n0")
62
85
 
86
+ self.mesh.comm.barrier()
87
+
63
88
  @classmethod
64
89
  def from_file(cls, comm: MPI.Intracomm, path: str | Path) -> "Geometry":
65
90
  path = Path(path)
91
+
66
92
  mesh = adios4dolfinx.read_mesh(comm=comm, filename=path)
67
93
  markers = adios4dolfinx.read_attributes(comm=comm, filename=path, name="markers")
68
- ffun = adios4dolfinx.read_meshtags(mesh=mesh, meshtag_name="Facet tags", filename=path)
94
+ tags = {}
95
+ for name, meshtag_name in (
96
+ ("cfun", "Cell tags"),
97
+ ("ffun", "Facet tags"),
98
+ ("efun", "Edge tags"),
99
+ ("vfun", "Vertex tags"),
100
+ ):
101
+ try:
102
+ tags[name] = adios4dolfinx.read_meshtags(
103
+ mesh=mesh, meshtag_name=meshtag_name, filename=path
104
+ )
105
+ except KeyError:
106
+ tags[name] = None
107
+
108
+ functions = {}
69
109
  function_space = adios4dolfinx.read_attributes(
70
110
  comm=comm, filename=path, name="function_space"
71
111
  )
72
- element = utils.array2element(function_space["f0"])
73
- # Assume same function space for all functions
74
- V = dolfinx.fem.functionspace(mesh, element)
75
- f0 = dolfinx.fem.Function(V, name="f0")
76
- s0 = dolfinx.fem.Function(V, name="s0")
77
- n0 = dolfinx.fem.Function(V, name="n0")
78
-
79
- adios4dolfinx.read_function(u=f0, filename=path, name="f0")
80
- adios4dolfinx.read_function(u=s0, filename=path, name="s0")
81
- adios4dolfinx.read_function(u=n0, filename=path, name="n0")
82
- return cls(mesh=mesh, markers=markers, ffun=ffun, f0=f0, s0=s0, n0=n0)
112
+ for name, el in function_space.items():
113
+ element = utils.array2element(el)
114
+ V = dolfinx.fem.functionspace(mesh, element)
115
+ f = dolfinx.fem.Function(V, name=name)
116
+ try:
117
+ adios4dolfinx.read_function(u=f, filename=path, name=name)
118
+ except KeyError:
119
+ continue
120
+ else:
121
+ functions[name] = f
122
+
123
+ return cls(
124
+ mesh=mesh,
125
+ markers=markers,
126
+ **functions,
127
+ **tags,
128
+ )
83
129
 
84
130
  @classmethod
85
131
  def from_folder(cls, comm: MPI.Intracomm, folder: str | Path) -> "Geometry":
@@ -87,7 +133,7 @@ class Geometry:
87
133
 
88
134
  # Read mesh
89
135
  if (folder / "mesh.xdmf").exists():
90
- mesh, cfun, ffun = utils.read_mesh(comm=comm, filename=folder / "mesh.xdmf")
136
+ mesh, tags = utils.read_mesh(comm=comm, filename=folder / "mesh.xdmf")
91
137
  else:
92
138
  raise ValueError("No mesh file found")
93
139
 
@@ -101,22 +147,26 @@ class Geometry:
101
147
  else:
102
148
  markers = {}
103
149
 
150
+ functions = {}
104
151
  microstructure_path = folder / "microstructure.bp"
105
152
  if microstructure_path.exists():
106
153
  function_space = adios4dolfinx.read_attributes(
107
154
  comm=MPI.COMM_WORLD, filename=microstructure_path, name="function_space"
108
155
  )
109
- # Assume same function space for all functions
110
- element = utils.array2element(function_space["f0"])
111
- V = dolfinx.fem.functionspace(mesh, element)
112
- f0 = dolfinx.fem.Function(V, name="f0")
113
- s0 = dolfinx.fem.Function(V, name="s0")
114
- n0 = dolfinx.fem.Function(V, name="n0")
115
-
116
- adios4dolfinx.read_function(u=f0, filename=microstructure_path, name="f0")
117
- adios4dolfinx.read_function(u=s0, filename=microstructure_path, name="s0")
118
- adios4dolfinx.read_function(u=n0, filename=microstructure_path, name="n0")
119
- else:
120
- f0 = s0 = n0 = None
121
-
122
- return cls(mesh=mesh, ffun=ffun, cfun=cfun, markers=markers, f0=f0, s0=s0, n0=n0)
156
+ for name, el in function_space.items():
157
+ element = utils.array2element(el)
158
+ V = dolfinx.fem.functionspace(mesh, element)
159
+ f = dolfinx.fem.Function(V, name=name)
160
+ try:
161
+ adios4dolfinx.read_function(u=f, filename=microstructure_path, name=name)
162
+ except KeyError:
163
+ continue
164
+ else:
165
+ functions[name] = f
166
+
167
+ return cls(
168
+ mesh=mesh,
169
+ markers=markers,
170
+ **functions,
171
+ **tags,
172
+ )
@@ -0,0 +1,272 @@
1
+ try:
2
+ import streamlit as st
3
+ except ImportError:
4
+ print("Please install streamlit - python3 -m pip install streamlit")
5
+ exit(1)
6
+
7
+ try:
8
+ import pyvista as pv
9
+ except ImportError:
10
+ msg = (
11
+ "Please install pyvista - python3 -m pip install pyvista. "
12
+ "Note if you using ARM Mac, then check out the following link "
13
+ "on how to install vtk: https://github.com/KitwareMedical/VTKPythonPackage/issues/42"
14
+ )
15
+ print(msg)
16
+ exit(1)
17
+
18
+ try:
19
+ from stpyvista import stpyvista
20
+ except ImportError:
21
+ print("Please install stpyvista - python3 -m pip install stpyvista")
22
+ exit(1)
23
+
24
+ import os
25
+
26
+ os.environ["GMSH_INTERRUPTIBLE"] = "0"
27
+
28
+ import math
29
+ from pathlib import Path
30
+
31
+ import mpi4py
32
+
33
+ import dolfinx
34
+
35
+ import cardiac_geometries
36
+
37
+
38
+ def return_none(*args, **kwargs):
39
+ return None
40
+
41
+
42
+ def load_geometry(folder: str):
43
+ comm = mpi4py.MPI.COMM_WORLD
44
+ try:
45
+ return cardiac_geometries.geometry.Geometry.from_folder(comm, folder)
46
+ except Exception as e:
47
+ st.error(f"Error loading geometry: {e}")
48
+ return None
49
+
50
+
51
+ def plot_geometry(geo):
52
+ V = dolfinx.fem.functionspace(geo.mesh, ("Lagrange", 1))
53
+
54
+ pv.start_xvfb()
55
+
56
+ # Plot the mesh with cell tags
57
+ mesh_plotter = pv.Plotter()
58
+ mesh_plotter.background_color = "white"
59
+ mesh_plotter.window_size = [600, 400]
60
+
61
+ topology, cell_types, geometry = dolfinx.plot.vtk_mesh(V)
62
+
63
+ grid = pv.UnstructuredGrid(topology, cell_types, geometry)
64
+ if geo.cfun is not None:
65
+ grid.cell_data["Cell tags"] = geo.cfun.values
66
+ grid.set_active_scalars("Cell tags")
67
+ mesh_plotter.add_mesh(grid, show_edges=True)
68
+
69
+ mesh_plotter.view_isometric()
70
+ st.header("Mesh and cell tags")
71
+ stpyvista(mesh_plotter)
72
+
73
+ if geo.ffun is not None:
74
+ vtk_bmesh = dolfinx.plot.vtk_mesh(geo.mesh, geo.ffun.dim, geo.ffun.indices)
75
+ bgrid = pv.UnstructuredGrid(*vtk_bmesh)
76
+ bgrid.cell_data["Facet tags"] = geo.ffun.values
77
+ bgrid.set_active_scalars("Facet tags")
78
+ facet_plotter = pv.Plotter()
79
+ facet_plotter.background_color = "white"
80
+ facet_plotter.window_size = [600, 400]
81
+ facet_plotter.add_mesh(bgrid, show_edges=True)
82
+ facet_plotter.view_isometric()
83
+ st.header("Facet tags")
84
+ stpyvista(facet_plotter)
85
+
86
+ if geo.f0 is not None:
87
+ st.header("Fibers")
88
+ size_arrows_fibers = st.slider(
89
+ "Arrow size fibers", min_value=0.1, max_value=10.0, value=2.0
90
+ )
91
+ topology, cell_types, geometry = dolfinx.plot.vtk_mesh(geo.f0.function_space)
92
+ values = geo.f0.x.array.real.reshape((geometry.shape[0], len(geo.f0)))
93
+ function_grid = pv.UnstructuredGrid(topology, cell_types, geometry)
94
+ function_grid["u"] = values
95
+ glyphs = function_grid.glyph(orient="u", factor=size_arrows_fibers)
96
+ fiber_plotter = pv.Plotter()
97
+ fiber_plotter.background_color = "white"
98
+ fiber_plotter.window_size = [600, 400]
99
+ fiber_plotter.add_mesh(glyphs, show_edges=True)
100
+ fiber_plotter.view_isometric()
101
+ stpyvista(fiber_plotter)
102
+
103
+ if geo.s0 is not None:
104
+ st.header("Sheets")
105
+ size_arrows_sheets = st.slider(
106
+ "Arrow size sheets", min_value=0.1, max_value=10.0, value=2.0
107
+ )
108
+ topology, cell_types, geometry = dolfinx.plot.vtk_mesh(geo.s0.function_space)
109
+ values = geo.s0.x.array.real.reshape((geometry.shape[0], len(geo.s0)))
110
+ function_grid = pv.UnstructuredGrid(topology, cell_types, geometry)
111
+ function_grid["u"] = values
112
+ glyphs = function_grid.glyph(orient="u", factor=size_arrows_sheets)
113
+ sheet_plotter = pv.Plotter()
114
+ sheet_plotter.background_color = "white"
115
+ sheet_plotter.window_size = [600, 400]
116
+ sheet_plotter.add_mesh(glyphs, show_edges=True)
117
+ sheet_plotter.view_isometric()
118
+ stpyvista(sheet_plotter)
119
+
120
+ if geo.n0 is not None:
121
+ st.header("Sheet Normal")
122
+ size_arrows_normal = st.slider(
123
+ "Arrow size sheet normal", min_value=0.1, max_value=10.0, value=2.0
124
+ )
125
+ topology, cell_types, geometry = dolfinx.plot.vtk_mesh(geo.n0.function_space)
126
+ values = geo.n0.x.array.real.reshape((geometry.shape[0], len(geo.n0)))
127
+ function_grid = pv.UnstructuredGrid(topology, cell_types, geometry)
128
+ function_grid["u"] = values
129
+ glyphs = function_grid.glyph(orient="u", factor=size_arrows_normal)
130
+ normal_plotter = pv.Plotter()
131
+ normal_plotter.background_color = "white"
132
+ normal_plotter.window_size = [600, 400]
133
+ normal_plotter.add_mesh(glyphs, show_edges=True)
134
+ normal_plotter.view_isometric()
135
+ stpyvista(normal_plotter)
136
+
137
+
138
+ def load():
139
+ st.title("Load existing geometries")
140
+
141
+ cwd = Path.cwd()
142
+ folders = [f.name for f in cwd.iterdir() if f.is_dir()]
143
+ # Select a folder
144
+
145
+ folder = st.selectbox("Select a folder", folders)
146
+ geo = load_geometry(folder)
147
+
148
+ if geo is not None:
149
+ plot_geometry(geo)
150
+
151
+ return
152
+
153
+
154
+ def create_lv():
155
+ st.title("Create lv geometry")
156
+
157
+ outdir = st.text_input("Output directory", value="lv_ellipsoid")
158
+ col1, col2, col3 = st.columns(3)
159
+
160
+ with col1:
161
+ st.header("Radius")
162
+ r_short_endo = st.number_input("r_short_endo", value=7.0)
163
+ r_short_epi = st.number_input("r_short_epi", value=10.0)
164
+ r_long_endo = st.number_input("r_long_endo", value=17.0)
165
+ r_long_epi = st.number_input("r_long_epi", value=20.0)
166
+ p_size_ref = st.number_input("p_size_ref", value=3.0)
167
+
168
+ with col2:
169
+ st.header("Angles")
170
+ mu_apex_endo = st.number_input("mu_apex_endo", value=-math.pi)
171
+ mu_base_endo = st.number_input("mu_base_endo", value=-math.acos(5 / 17))
172
+ mu_apex_epi = st.number_input("mu_apex_epi", value=-math.pi)
173
+ mu_base_epi = st.number_input("mu_base_epi", value=-math.acos(5 / 20))
174
+
175
+ with col3:
176
+ st.header("Fibers")
177
+ create_fibers = st.checkbox("Create fibers", value=True)
178
+ if create_fibers:
179
+ fiber_space = st.selectbox("Fiber space", ["P_1", "P_2"])
180
+ fiber_angle_endo = st.number_input("fiber_angle_endo", value=60)
181
+ fiber_angle_epi = st.number_input("fiber_angle_epi", value=-60)
182
+
183
+ if st.button("Create"):
184
+ args = [
185
+ "geox",
186
+ "lv-ellipsoid",
187
+ "--r-short-endo",
188
+ str(r_short_endo),
189
+ "--r-short-epi",
190
+ str(r_short_epi),
191
+ "--r-long-endo",
192
+ str(r_long_endo),
193
+ "--r-long-epi",
194
+ str(r_long_epi),
195
+ "--mu-apex-endo",
196
+ str(mu_apex_endo),
197
+ "--mu-base-endo",
198
+ str(mu_base_endo),
199
+ "--mu-apex-epi",
200
+ str(mu_apex_epi),
201
+ "--mu-base-epi",
202
+ str(mu_base_epi),
203
+ "--psize-ref",
204
+ str(p_size_ref),
205
+ ]
206
+
207
+ if create_fibers:
208
+ args.extend(
209
+ [
210
+ "--create-fibers",
211
+ "--fiber-space",
212
+ fiber_space,
213
+ "--fiber-angle-endo",
214
+ str(fiber_angle_endo),
215
+ "--fiber-angle-epi",
216
+ str(fiber_angle_epi),
217
+ ]
218
+ )
219
+
220
+ args.append(str(outdir))
221
+ st.markdown(f"```{' '.join(args)}```")
222
+
223
+ if Path(outdir).exists():
224
+ st.warning(f"Folder {outdir} already exists. Overwriting...")
225
+ import shutil
226
+
227
+ shutil.rmtree(outdir)
228
+
229
+ import subprocess as sp
230
+
231
+ ret = sp.run(
232
+ args,
233
+ capture_output=True,
234
+ )
235
+
236
+ st.markdown(f"```{ret.stdout.decode()}```")
237
+
238
+ if st.button("Visualize folder"):
239
+ geo = load_geometry(outdir)
240
+
241
+ if geo is not None:
242
+ plot_geometry(geo)
243
+
244
+ return
245
+
246
+
247
+ # Page settings
248
+ st.set_page_config(page_title="simcardems")
249
+
250
+ # Sidebar settings
251
+ pages = {
252
+ "Load": load,
253
+ "Create LV geometry": create_lv,
254
+ }
255
+
256
+ st.sidebar.title("Cardiac geometries")
257
+
258
+ # Radio buttons to select desired option
259
+ page = st.sidebar.radio("Pages", tuple(pages.keys()))
260
+
261
+ pages[page]()
262
+
263
+ # About
264
+ st.sidebar.markdown(
265
+ """
266
+ - [Source code](https://github.com/ComputationalPhysiology/cardiac-geometriesx)
267
+ - [Documentation](http://computationalphysiology.github.io/cardiac-geometriesx)
268
+
269
+
270
+ Copyright © 2024 Henrik Finsberg @ Simula Research Laboratory
271
+ """,
272
+ )