Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix edge refinement #49

Merged
merged 136 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
136 commits
Select commit Hold shift + click to select a range
7733612
make edge names to be refined clearer
j-zimmermann Jul 10, 2024
172da99
idea for edge markers
j-zimmermann Jul 16, 2024
b90db58
refine directed contacts, add tests scripts for mesh
j-zimmermann Jul 17, 2024
ac50843
update mesh example
Jul 23, 2024
ba37d63
change refinement of directed contacts (abbott_stjude & medtronic)
kenichi-maeda Jul 24, 2024
43b3487
only label outer edges
Jul 25, 2024
8824626
clean edge refinement
Jul 25, 2024
0a29d64
unoriented base contact
kenichi-maeda Jul 30, 2024
798b447
work on directed contacts
j-zimmermann Aug 7, 2024
cca1741
comments on rotation
j-zimmermann Aug 8, 2024
5ce00cb
reproduce bug
kenichi-maeda Aug 8, 2024
ee27831
remove one rotation
j-zimmermann Aug 8, 2024
58cfe45
adjust rotation of electrode
j-zimmermann Aug 12, 2024
7a77ad5
use non-standard direction in example
j-zimmermann Aug 12, 2024
e612670
updated version of rotation in comment, agrees without?
j-zimmermann Aug 12, 2024
e417c68
add default in comments
j-zimmermann Aug 12, 2024
12c4d1c
WIP: angle not correct
j-zimmermann Aug 13, 2024
6227d49
fix rotation / spin issue
j-zimmermann Aug 13, 2024
1ad10c7
test examples that don't work
j-zimmermann Aug 14, 2024
41fc438
a better check for the orientation
j-zimmermann Aug 14, 2024
b0efd6e
enable log to file
j-zimmermann Aug 14, 2024
2c3e3aa
example with custom electrode
j-zimmermann Aug 18, 2024
95acb13
more flexibility in interface
j-zimmermann Aug 19, 2024
ac44073
set default output path
j-zimmermann Aug 19, 2024
77c050f
write StimSets mesh to results directory (no cleanup yet)
j-zimmermann Aug 19, 2024
880ec90
merge updated tests
j-zimmermann Aug 21, 2024
f646d92
update output
j-zimmermann Aug 21, 2024
1d0a543
GMRES did not converge properly
j-zimmermann Aug 22, 2024
946a285
new benchmark
j-zimmermann Aug 22, 2024
72db5b7
fix geometry issue
j-zimmermann Aug 25, 2024
8b323e2
new test for electrode edges
j-zimmermann Aug 25, 2024
6135b18
test for multiple electrodes
j-zimmermann Aug 25, 2024
a5cb005
scripts to debug meshes / geo issues
j-zimmermann Aug 25, 2024
bedb1f2
ignore more files
j-zimmermann Aug 25, 2024
e6590eb
handle output path correctly
j-zimmermann Aug 26, 2024
19743d6
permit free choice of ground contact
j-zimmermann Aug 26, 2024
b2b30c9
update ci to only use ruff
j-zimmermann Aug 27, 2024
370fa88
export field too if needed
j-zimmermann Aug 27, 2024
1f74f9c
FIX: active grounding check and VTA threshold
Kinway25 Aug 28, 2024
0188cbb
export vtk from input
j-zimmermann Aug 28, 2024
f4dd2d3
adjust spin angle of all directed electrodes
j-zimmermann Aug 28, 2024
7830f1f
adjust spin angle of cartesia electrodes, needs to be checked
j-zimmermann Aug 28, 2024
93cb806
remove superfluous rotations
j-zimmermann Aug 28, 2024
2a988f2
add tests for spin direction of directed electrodes
j-zimmermann Aug 29, 2024
12fd83d
export also electrode mesh
j-zimmermann Aug 29, 2024
79ba4cd
fix face naming for microelectrode
Aug 30, 2024
549dac7
Merge branch 'main' into fix_edge_refinement
j-zimmermann Aug 30, 2024
e971683
expose voxel size
j-zimmermann Aug 30, 2024
111e709
all electrode contacts need to be in brain
j-zimmermann Aug 30, 2024
80db23a
update OOC results
j-zimmermann Aug 31, 2024
ed93fbe
add informative output to AMR
j-zimmermann Aug 31, 2024
c65f4ba
fix medtronic contacts
j-zimmermann Sep 2, 2024
3a7e3b8
fix abbott electrode
j-zimmermann Sep 2, 2024
855be31
fix vercise electrode
j-zimmermann Sep 2, 2024
09f0125
update dixi electrodes
j-zimmermann Sep 2, 2024
eca4611
fix neuropace
j-zimmermann Sep 2, 2024
ed368e3
fixed pins medical
j-zimmermann Sep 2, 2024
9e72b70
define lead diameter for all electrodes, make export more robust
j-zimmermann Sep 2, 2024
daf066b
clearer error message for wrong input parameter
j-zimmermann Sep 2, 2024
a42dbf6
catch zero-thickness of encap layer better
j-zimmermann Sep 2, 2024
ba39743
fix MicroProbes
j-zimmermann Sep 2, 2024
c9bf592
update tests
j-zimmermann Sep 2, 2024
ad3813e
fix microelectrode
j-zimmermann Sep 2, 2024
ee17f82
UPD: higher precision for grounded current
Kinway25 Sep 5, 2024
0189a51
add utilities for VTA
j-zimmermann Sep 6, 2024
bcf35e4
update output message
j-zimmermann Sep 7, 2024
ffd76d9
base setting for convergence study
j-zimmermann Sep 7, 2024
7664654
rename activation threshold to make unit clear
j-zimmermann Sep 7, 2024
d77ce36
add VTA volume computation and export
j-zimmermann Sep 7, 2024
1272d18
update test case for new lattice resolution
j-zimmermann Sep 7, 2024
122d3a3
update input file parameter name
j-zimmermann Sep 7, 2024
5fbc486
catch case when no activation threshold is provided
j-zimmermann Sep 7, 2024
f84e241
catch points outside lattice
j-zimmermann Sep 8, 2024
770cbb8
catch points outside lattice
j-zimmermann Sep 8, 2024
7a3029c
update AMR impedance handling
j-zimmermann Sep 8, 2024
28c4d89
add base parameters for PAM
j-zimmermann Sep 8, 2024
2b286aa
expose electrode parameters
j-zimmermann Sep 8, 2024
733aaee
structure for convergence study
j-zimmermann Sep 9, 2024
48339b0
initialise point models when final mesh is ready
j-zimmermann Sep 9, 2024
74d303d
no large files in release
j-zimmermann Sep 9, 2024
00e45ea
add material mesh refinement
j-zimmermann Sep 9, 2024
ee82754
update AMR routine with more output and better control
j-zimmermann Sep 10, 2024
0e1cb35
update file handler and convergence routine
j-zimmermann Sep 10, 2024
d781ec1
catch NEURON error (WIP)
j-zimmermann Sep 10, 2024
7335a13
use gzipped file
j-zimmermann Sep 11, 2024
3474927
remove line
j-zimmermann Sep 11, 2024
81b3987
change deprecated numpy function
j-zimmermann Sep 11, 2024
06b3f28
overwrite impedances only when computed
j-zimmermann Sep 11, 2024
3a7f9c2
add metrics for PAM
j-zimmermann Sep 14, 2024
2dba40f
add VCM report
j-zimmermann Sep 14, 2024
27462ed
PAM files
j-zimmermann Sep 14, 2024
068c5ec
make sure directory exists
j-zimmermann Sep 14, 2024
72e6325
full layout for convergence study
j-zimmermann Sep 14, 2024
40a8a0a
full layout for convergence study vercise
j-zimmermann Sep 14, 2024
bf17774
update case grounding
j-zimmermann Sep 14, 2024
9af3463
update case grounding
j-zimmermann Sep 14, 2024
bbd76a5
fix typo
j-zimmermann Sep 14, 2024
1787834
add PAM layout
j-zimmermann Sep 14, 2024
75053a4
update currents
j-zimmermann Sep 14, 2024
4da91fe
add PAM layout
j-zimmermann Sep 14, 2024
62ffa29
adjust lattice shape
j-zimmermann Sep 14, 2024
adf167d
adjust lattice shape
j-zimmermann Sep 14, 2024
30d56d9
updated PAM files
j-zimmermann Sep 15, 2024
ceff225
added Vercise PAM files
j-zimmermann Sep 15, 2024
dee980b
typo
j-zimmermann Sep 15, 2024
9c512de
Revert "added Vercise PAM files"
j-zimmermann Sep 15, 2024
737c0f0
added Vercise PAM files
j-zimmermann Sep 15, 2024
10fdfd8
add monopolar case
j-zimmermann Sep 16, 2024
1c8229b
add point refinement
j-zimmermann Sep 17, 2024
6d3af2d
update PAM convergence study
j-zimmermann Sep 28, 2024
9ecfdf5
run for new PAM model
j-zimmermann Sep 28, 2024
6f0c4c2
final VTA layout
j-zimmermann Oct 7, 2024
512ffe8
add comparison in time domain for not-activated neurons
j-zimmermann Oct 7, 2024
dc3201a
just Jacobi is more stable
j-zimmermann Oct 7, 2024
ded60f3
update preconditioner
j-zimmermann Oct 10, 2024
edf55e9
update preconditioner
j-zimmermann Oct 10, 2024
d244467
update preconditioner
j-zimmermann Oct 10, 2024
765c22b
add new pam studies
j-zimmermann Oct 10, 2024
4199a98
preconditioner is set by string not set
j-zimmermann Oct 11, 2024
97a1ace
update source code to catch bug
j-zimmermann Oct 13, 2024
7c4c451
deprecate Python 3.8 because of EOL
j-zimmermann Oct 14, 2024
0129f0b
truncate time-domain output vector
j-zimmermann Oct 15, 2024
a7d3ead
fix indexing error
j-zimmermann Oct 16, 2024
1a0893f
run truncation test
j-zimmermann Oct 19, 2024
4de6d21
split export function
j-zimmermann Oct 22, 2024
a16263a
FIX: correct position of the first contact
Kinway25 Oct 24, 2024
7766bcf
Merge branch 'fix_edge_refinement' of github.com:SFB-ELAINE/OSS-DBSv2…
j-zimmermann Oct 24, 2024
744a849
add mesh scripts
j-zimmermann Oct 24, 2024
a6d81f7
stimsets overview
j-zimmermann Oct 24, 2024
b4f5fe4
update reference meshes
j-zimmermann Oct 24, 2024
3604211
add issue template
j-zimmermann Oct 29, 2024
c4a1cb1
update wire for microprobes electrode (#58)
JPPayonk Nov 3, 2024
71f10ab
update convergence results
j-zimmermann Nov 3, 2024
b630536
Merge branch 'fix_edge_refinement' of github.com:SFB-ELAINE/OSS-DBSv2…
j-zimmermann Nov 3, 2024
f3cc6de
update preconditioner
j-zimmermann Nov 3, 2024
32610c5
update default Lead-DBS
j-zimmermann Nov 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions examples/MeshAPI/VerciseMeshDirected_SetMeshSizes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"""
Example of a full model geometry
with one Vercise electrode without
encapsulation layer.
The mesh is generated and
local mesh refinements are taken
into account during the meshing
process.
"""
import ngsolve

import ossdbs
from ossdbs.utils.vtk_export import FieldSolution


def save(cf, filename: str) -> None:
"""Save solution in VTK format."""
names = [f"{cf.label}_real"]
if cf.is_complex:
names.append(f"{cf.label}_imag")

coefficients = [cf.solution.real]
if cf.is_complex:
coefficients.append(cf.solution.imag)

vtk = ngsolve.VTKOutput(
ma=cf.mesh,
coefs=coefficients,
names=names,
filename=filename,
subdivision=0,
)
vtk.Do(vb=ngsolve.BND)


settings = {
"Electrodes": [
{
"Name": "BostonScientificVerciseDirected",
"Rotation[Degrees]": 0,
"Direction": {"x[mm]": 0, "y[mm]": 0, "z[mm]": 1},
"TipPosition": {"x[mm]": 0, "y[mm]": 15, "z[mm]": -3},
"EncapsulationLayer": {
"Thickness[mm]": 0.0, # indicates that no encapsulation is modelled
},
"Contacts": [
{
"Contact_ID": 1,
"MaxMeshSizeEdge": 0.05,
},
{
"Contact_ID": 2,
"MaxMeshSizeEdge": 0.01,
},
{
"Contact_ID": 3,
"MaxMeshSizeEdge": 0.05,
},
{
"Contact_ID": 4,
"MaxMeshSizeEdge": 0.05,
},
{
"Contact_ID": 5,
"MaxMeshSizeEdge": 0.05,
},
{
"Contact_ID": 6,
"MaxMeshSizeEdge": 0.05,
},
{
"Contact_ID": 7,
"MaxMeshSizeEdge": 0.05,
},
{
"Contact_ID": 8,
"MaxMeshSizeEdge": 0.05,
},
],
}
],
"BrainRegion": {
"Center": {"x[mm]": 5, "y[mm]": 14, "z[mm]": -4.5},
"Dimension": {"x[mm]": 50.0, "y[mm]": 50.0, "z[mm]": 50.0},
"Shape": "Ellipsoid",
},
"Mesh": {"LoadMesh": False, "SaveMesh": False},
"ExportElectrode": False,
}

# generate mesh with brain
mesh = ossdbs.generate_mesh(settings)

print(mesh.boundaries)
print(mesh.materials)

bnd_dict = {}
for idx in range(1, 9):
bnd_dict[f"E1C{idx}"] = idx

ngsolve.Draw(mesh.boundary_coefficients(bnd_dict), mesh.ngsolvemesh, "bnd")

cf = FieldSolution(
solution=mesh.boundary_coefficients(bnd_dict),
label="bnd",
mesh=mesh.ngsolvemesh,
is_complex=False,
)
save(cf, "electrode_in_brain")
76 changes: 76 additions & 0 deletions examples/MeshAPI/VerciseMeshDirected_SetMeshSizes_RefineOne.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"""
Example of a full model geometry
with one Vercise electrode without
encapsulation layer.
The mesh is generated and
local mesh refinements are taken
into account during the meshing
process.
"""
import ossdbs

settings = {
"Electrodes": [
{
"Name": "BostonScientificVerciseDirected",
"Rotation[Degrees]": 0,
"Direction": {"x[mm]": 0, "y[mm]": 0, "z[mm]": 1},
"TipPosition": {"x[mm]": 0, "y[mm]": 15, "z[mm]": -3},
"EncapsulationLayer": {
"Thickness[mm]": 0.0, # indicates that no encapsulation is modelled
},
"Contacts": [
{
"Contact_ID": 1,
"Active": True,
"Current[A]": 0.0,
"Voltage[V]": 1.0,
"Floating": False,
"SurfaceImpedance[Ohmm]": {"real": 0.0, "imag": 0.0},
"MaxMeshSizeEdge": 0.05,
},
{
"Contact_ID": 3,
"Active": True,
"Current[A]": 0.0,
"Voltage[V]": 0.0,
"Floating": False,
"SurfaceImpedance[Ohmm]": {"real": 0.0, "imag": 0.0},
"MaxMeshSizeEdge": 0.05,
},
{
"Contact_ID": 5,
"Active": False,
"Current[A]": 0.0,
"Voltage[V]": 0.0,
"Floating": False,
"SurfaceImpedance[Ohmm]": {"real": 0.0, "imag": 0.0},
"MaxMeshSizeEdge": 0.05,
},
],
}
],
"BrainRegion": {
"Center": {"x[mm]": 5, "y[mm]": 14, "z[mm]": -4.5},
"Dimension": {"x[mm]": 50.0, "y[mm]": 50.0, "z[mm]": 50.0},
"Shape": "Ellipsoid",
},
"ExportElectrode": False,
}

electrodes = ossdbs.generate_electrodes(settings)
vercise = electrodes[0]
electrode_settings = settings["Electrodes"][0]
# set edge mesh sizes
for edge in vercise.geometry.edges:
if edge.name is not None:
contact_number = int(edge.name.replace("Contact_", ""))
for contact_setting in electrode_settings["Contacts"]:
if contact_setting["Contact_ID"] == contact_number:
if "MaxMeshSizeEdge" in contact_setting:
edge.maxh = contact_setting["MaxMeshSizeEdge"]
continue

vercise.export_electrode(
output_path=".", brain_dict=settings["BrainRegion"], n_electrode=0
)
48 changes: 17 additions & 31 deletions ossdbs/electrodes/abbott_stjude.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ def _contacts(self) -> netgen.libngpy._NgOCC.TopoDS_Shape:
else:
# Label all the named contacts appropriately
for edge in contact.edges:
if edge.name is not None:
if edge.name == "Rename":
edge.name = name
return netgen.occ.Fuse(contacts)

Expand All @@ -253,36 +253,22 @@ def _contact_directed(self) -> netgen.libngpy._NgOCC.TopoDS_Shape:
# Centering contact to label edges
contact = contact.Rotate(axis, angle)

# TODO refactor / wrap in function
# Find max z, min z, max x, and max y values and label min x and min y edge
max_z_val = max_y_val = max_x_val = float("-inf")
min_z_val = float("inf")
# Label all outer edges
for edge in contact.edges:
if edge.center.z > max_z_val:
max_z_val = edge.center.z
if edge.center.z < min_z_val:
min_z_val = edge.center.z
if edge.center.x > max_x_val:
max_x_val = edge.center.x
max_x_edge = edge
if edge.center.y > max_y_val:
max_y_val = edge.center.y
max_y_edge = edge
max_x_edge.name = "max x"
max_y_edge.name = "max y"
# Label only the outer edges of the contact with min z and max z values
for edge in contact.edges:
if np.isclose(edge.center.z, max_z_val) and not (
np.isclose(edge.center.x, radius / 2)
or np.isclose(edge.center.y, radius / 2)
):
edge.name = "max z"
elif np.isclose(edge.center.z, min_z_val) and not (
np.isclose(edge.center.x, radius / 2)
or np.isclose(edge.center.y, radius / 2)
):
edge.name = "min z"
edge_center = np.array([edge.center.x, edge.center.y, edge.center.z])

# Skip center edge
if np.allclose(np.cross(edge_center, self._direction), 0):
continue

new_center = np.dot(edge_center, self._direction) * np.array(
self._direction
)

# Mark only outer edges
if not np.isclose(np.linalg.norm(edge_center - new_center), radius / 2):
edge.name = "Rename"

contact = contact.Rotate(axis, -angle)
# TODO check that the starting axis of the contacts
# are correct according to the documentation

return contact
10 changes: 5 additions & 5 deletions ossdbs/electrodes/boston_scientific_cartesia.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def _contacts(self) -> netgen.libngpy._NgOCC.TopoDS_Shape:
else:
# Label all the named contacts appropriately
for edge in contact.edges:
if edge.name is not None:
if edge.name == "Rename":
edge.name = name
return netgen.occ.Fuse(contacts)

Expand Down Expand Up @@ -202,20 +202,20 @@ def _contact_directed(self) -> netgen.libngpy._NgOCC.TopoDS_Shape:
if edge.center.y > max_y_val:
max_y_val = edge.center.y
max_y_edge = edge
max_x_edge.name = "max x"
max_y_edge.name = "max y"
max_x_edge.name = "Rename"
max_y_edge.name = "Rename"
# Label only the outer edges of the contact with min z and max z values
for edge in contact.edges:
if np.isclose(edge.center.z, max_z_val) and not (
np.isclose(edge.center.x, radius / 2)
or np.isclose(edge.center.y, radius / 2)
):
edge.name = "max z"
edge.name = "Rename"
elif np.isclose(edge.center.z, min_z_val) and not (
np.isclose(edge.center.x, radius / 2)
or np.isclose(edge.center.y, radius / 2)
):
edge.name = "min z"
edge.name = "Rename"

# TODO check that the starting axis of the contacts
# are correct according to the documentation
Expand Down
50 changes: 18 additions & 32 deletions ossdbs/electrodes/boston_scientific_vercise.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,11 @@ def _contacts(self) -> netgen.libngpy._NgOCC.TopoDS_Shape:
else:
# Label all the named contacts appropriately
for edge in contact.edges:
if edge.name is not None:
if edge.name == "Rename":
edge.name = name
return netgen.occ.Fuse(contacts)

# ruff: noqa: C901
def _contact_directed(self) -> netgen.libngpy._NgOCC.TopoDS_Shape:
point = (0, 0, 0)
radius = self._parameters.lead_diameter * 0.5
Expand All @@ -164,38 +165,23 @@ def _contact_directed(self) -> netgen.libngpy._NgOCC.TopoDS_Shape:
contact = body - eraser.Rotate(axis, angle) - eraser.Rotate(axis, -angle)
# Centering contact to label edges
contact = contact.Rotate(axis, angle)
# TODO refactor / wrap in function
# Find max z, min z, max x, and max y values and label min x and min y edge
max_z_val = max_y_val = max_x_val = float("-inf")
min_z_val = float("inf")
for edge in contact.edges:
if edge.center.z > max_z_val:
max_z_val = edge.center.z
if edge.center.z < min_z_val:
min_z_val = edge.center.z
if edge.center.x > max_x_val:
max_x_val = edge.center.x
max_x_edge = edge
if edge.center.y > max_y_val:
max_y_val = edge.center.y
max_y_edge = edge
max_x_edge.name = "max x"
max_y_edge.name = "max y"
# Label only the outer edges of the contact with min z and max z values

# Label all outer edges
for edge in contact.edges:
if np.isclose(edge.center.z, max_z_val) and not (
np.isclose(edge.center.x, radius / 2)
or np.isclose(edge.center.y, radius / 2)
):
edge.name = "max z"
elif np.isclose(edge.center.z, min_z_val) and not (
np.isclose(edge.center.x, radius / 2)
or np.isclose(edge.center.y, radius / 2)
):
edge.name = "min z"

# TODO check that the starting axis of the contacts
# are correct according to the documentation
edge_center = np.array([edge.center.x, edge.center.y, edge.center.z])

# Skip center edge
if np.allclose(np.cross(edge_center, self._direction), 0):
continue

new_center = np.dot(edge_center, self._direction) * np.array(
self._direction
)

# Mark only outer edges
if not np.isclose(np.linalg.norm(edge_center - new_center), radius / 2):
edge.name = "Rename"

contact = contact.Rotate(axis, -angle)

return contact
Expand Down
Loading
Loading