Skip to content

Commit

Permalink
restructure and add SEEG electrodes
Browse files Browse the repository at this point in the history
  • Loading branch information
JPPayonk committed Jan 29, 2024
1 parent e35221c commit 781aebd
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 158 deletions.
35 changes: 23 additions & 12 deletions ossdbs/electrodes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@
AbbottStJudeDirected6173,
BostonScientificVercise,
BostonScientificVerciseDirected,
DixiSEEG5,
DixiSEEG8,
DixiSEEG10,
DixiSEEG12,
DixiSEEG15,
DixiSEEG18,
Medtronic3387,
Medtronic3389,
Medtronic3391,
Expand All @@ -35,10 +39,8 @@
default_electrode_parameters,
)
from .dixi_microtechniques import (
DixiSEEG10Model,
DixiSEEG10Parameters,
DixiSEEG15Model,
DixiSEEG15Parameters,
DixiSEEGModel,
DixiSEEGParameters,
)
from .electrode_model_template import ElectrodeModel
from .medtronic import (
Expand All @@ -63,8 +65,12 @@
"AbbottStJudeDirected6173": AbbottStJudeDirected6173,
"BostonScientificVercise": BostonScientificVercise,
"BostonScientificVerciseDirected": BostonScientificVerciseDirected,
"DixiSEEG5": DixiSEEG5,
"DixiSEEG8": DixiSEEG8,
"DixiSEEG10": DixiSEEG10,
"DixiSEEG12": DixiSEEG12,
"DixiSEEG15": DixiSEEG15,
"DixiSEEG18": DixiSEEG18,
"Medtronic3387": Medtronic3387,
"Medtronic3389": Medtronic3389,
"Medtronic3391": Medtronic3391,
Expand All @@ -87,8 +93,12 @@
"AbbottStJudeDirected6173Custom": AbbottStJudeDirectedModel,
"BostonScientificVerciseCustom": BostonScientificVerciseModel,
"BostonScientificVerciseDirectedCustom": BostonScientificVerciseDirectedModel,
"DixiSEEG10Custom": DixiSEEG10Model,
"DixiSEEG15Custom": DixiSEEG15Model,
"DixiSEEG5Custom": DixiSEEGModel,
"DixiSEEG8Custom": DixiSEEGModel,
"DixiSEEG10Custom": DixiSEEGModel,
"DixiSEEG12Custom": DixiSEEGModel,
"DixiSEEG15Custom": DixiSEEGModel,
"DixiSEEG158ustom": DixiSEEGModel,
"Medtronic3387Custom": MedtronicModel,
"Medtronic3389Custom": MedtronicModel,
"Medtronic3391Custom": MedtronicModel,
Expand All @@ -110,8 +120,7 @@
"AbbottStJudeDirectedModel": AbbottStJudeParameters,
"BostonScientificVerciseModel": BostonScientificVerciseParameters,
"BostonScientificVerciseDirectedModel": BostonScientificVerciseDirectedParameters,
"DixiSEEG10Model": DixiSEEG10Parameters,
"DixiSEEG15Model": DixiSEEG15Parameters,
"DixiSEEGModel": DixiSEEGParameters,
"MedtronicModel": MedtronicParameters,
"MedtronicSenSightModel": MedtronicParameters,
"MicroElectrodeModel": MicroElectrodeParameters,
Expand All @@ -137,12 +146,14 @@
"BostonScientificVerciseDirectedModel",
"BostonScientificVerciseParameters",
"BostonScientificVerciseDirectedParameters",
"DixiSEEG5",
"DixiSEEG8",
"DixiSEEG10",
"DixiSEEG12",
"DixiSEEG15",
"DixiSEEG10Model",
"DixiSEEG15Model",
"DixiSEEG10Parameters",
"DixiSEEG15Parameters",
"DixiSEEG18",
"DixiSEEGModel",
"DixiSEEGParameters",
"Medtronic3387",
"Medtronic3389",
"Medtronic3391",
Expand Down
76 changes: 68 additions & 8 deletions ossdbs/electrodes/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@
BostonScientificVerciseParameters,
)
from .dixi_microtechniques import (
DixiSEEG10Model,
DixiSEEG10Parameters,
DixiSEEG15Model,
DixiSEEG15Parameters,
DixiSEEGModel,
DixiSEEGParameters,
)
from .medtronic import (
MedtronicModel,
Expand Down Expand Up @@ -177,19 +175,53 @@
lead_diameter=1.3,
total_length=400.0,
),
"DixiSEEG10": DixiSEEG10Parameters(
"DixiSEEG5": DixiSEEGParameters(
tip_length=0.8,
contact_length=2.0,
contact_spacing=1.5,
lead_diameter=0.8,
total_length=400.0,
n_contacts=5,
),
"DixiSEEG15": DixiSEEG15Parameters(
"DixiSEEG8": DixiSEEGParameters(
tip_length=0.8,
contact_length=2.0,
contact_spacing=1.5,
lead_diameter=0.8,
total_length=400.0,
n_contacts=8,
),
"DixiSEEG10": DixiSEEGParameters(
tip_length=0.8,
contact_length=2.0,
contact_spacing=1.5,
lead_diameter=0.8,
total_length=400.0,
n_contacts=10,
),
"DixiSEEG12": DixiSEEGParameters(
tip_length=0.8,
contact_length=2.0,
contact_spacing=1.5,
lead_diameter=0.8,
total_length=400.0,
n_contacts=12,
),
"DixiSEEG15": DixiSEEGParameters(
tip_length=0.8,
contact_length=2.0,
contact_spacing=1.5,
lead_diameter=0.8,
total_length=400.0,
n_contacts=15,
),
"DixiSEEG18": DixiSEEGParameters(
tip_length=0.8,
contact_length=2.0,
contact_spacing=1.5,
lead_diameter=0.8,
total_length=400.0,
n_contacts=18,
),
}

Expand Down Expand Up @@ -329,15 +361,43 @@ def PINSMedicalL303(
return PINSMedicalModel(parameters, rotation, direction, position)


def DixiSEEG5(
rotation: float = 0, direction: tuple = (0, 0, 1), position: tuple = (0, 0, 0)
):
parameters = default_electrode_parameters["DixiSEEG5"]
return DixiSEEGModel(parameters, rotation, direction, position)


def DixiSEEG8(
rotation: float = 0, direction: tuple = (0, 0, 1), position: tuple = (0, 0, 0)
):
parameters = default_electrode_parameters["DixiSEEG8"]
return DixiSEEGModel(parameters, rotation, direction, position)


def DixiSEEG10(
rotation: float = 0, direction: tuple = (0, 0, 1), position: tuple = (0, 0, 0)
):
parameters = default_electrode_parameters["DixiSEEG10"]
return DixiSEEG10Model(parameters, rotation, direction, position)
return DixiSEEGModel(parameters, rotation, direction, position)


def DixiSEEG12(
rotation: float = 0, direction: tuple = (0, 0, 1), position: tuple = (0, 0, 0)
):
parameters = default_electrode_parameters["DixiSEEG12"]
return DixiSEEGModel(parameters, rotation, direction, position)


def DixiSEEG15(
rotation: float = 0, direction: tuple = (0, 0, 1), position: tuple = (0, 0, 0)
):
parameters = default_electrode_parameters["DixiSEEG15"]
return DixiSEEG15Model(parameters, rotation, direction, position)
return DixiSEEGModel(parameters, rotation, direction, position)


def DixiSEEG18(
rotation: float = 0, direction: tuple = (0, 0, 1), position: tuple = (0, 0, 0)
):
parameters = default_electrode_parameters["DixiSEEG18"]
return DixiSEEGModel(parameters, rotation, direction, position)
147 changes: 9 additions & 138 deletions ossdbs/electrodes/dixi_microtechniques.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,26 @@


@dataclass
class DixiSEEG10Parameters:
class DixiSEEGParameters:
# dimensions [mm]
tip_length: float
contact_length: float
contact_spacing: float
lead_diameter: float
total_length: float
n_contacts: int

def get_center_first_contact(self) -> float:
"""Returns distance between electrode tip and center of first contact."""
return 0.5 * self.contact_length

def get_distance_l1_l4(self) -> float:
"""Returns distance between first level contact and fourth level contact."""
return 9 * (self.contact_length + self.contact_spacing)
return 3 * (self.contact_length + self.contact_spacing)


class DixiSEEG10Model(ElectrodeModel):
"""Dixi Microtechniques SEEG10 electrode.
class DixiSEEGModel(ElectrodeModel):
"""Dixi Microtechniques SEEG electrode.
Attributes
----------
Expand All @@ -44,7 +45,9 @@ class DixiSEEG10Model(ElectrodeModel):
Position vector (x,y,z) of electrode tip.
"""

_n_contacts = 10
_n_contacts = 18 # TODO set to actual value from each dataclass
# def __init__(self, parameters: DixiSEEGParameters, rotation: float, direction: tuple, position: tuple):
# self._n_contacts = parameters.n_contacts

def parameter_check(self):
# Check to ensure that all parameters are at least 0
Expand Down Expand Up @@ -108,139 +111,7 @@ def __contacts(self) -> netgen.libngpy._NgOCC.TopoDS_Shape:
contact_cyl = occ.Cylinder(p=(0, 0, 0), d=direction, r=radius, h=height)

contacts = []
for count in range(self._n_contacts):
name = self._boundaries[f"Contact_{count + 1}"]
contact.bc(name)
min_edge_z_val = float("inf")
max_edge_z_val = float("-inf")
for edge in contact.edges:
if edge.center.z < min_edge_z_val:
min_edge_z_val = edge.center.z
min_edge = edge
if edge.center.z > max_edge_z_val:
max_edge_z_val = edge.center.z
max_edge = edge
# Only name edge with the max z value for contact_1
max_edge.name = name
# first contact is different from others
if count == 0:
distance = (
self._parameters.contact_length + self._parameters.contact_spacing
)
contacts.append(contact)
contact = contact_cyl
else:
min_edge.name = name
vector = tuple(np.array(self._direction) * distance)
contacts.append(contact.Move(vector))
distance += (
self._parameters.contact_length + self._parameters.contact_spacing
)

return occ.Glue(contacts)


@dataclass
class DixiSEEG15Parameters:
# dimensions [mm]
tip_length: float
contact_length: float
contact_spacing: float
lead_diameter: float
total_length: float

def get_center_first_contact(self) -> float:
"""Returns distance between electrode tip and center of first contact."""
return 0.5 * self.contact_length

def get_distance_l1_l4(self) -> float:
"""Returns distance between first level contact and fourth level contact."""
return 14 * (self.contact_length + self.contact_spacing)


class DixiSEEG15Model(ElectrodeModel):
"""Dixi Microtechniques SEEG10 electrode.
Attributes
----------
parameters : DixiSEEGParameters
Parameters for Dixi Microtechniques SEEG electrode geometry.
rotation : float
Rotation angle in degree of electrode.
direction : tuple
Direction vector (x,y,z) of electrode.
position : tuple
Position vector (x,y,z) of electrode tip.
"""

_n_contacts = 15

def parameter_check(self):
# Check to ensure that all parameters are at least 0
for param in asdict(self._parameters).values():
if param < 0:
raise ValueError("Parameter values cannot be less than zero")

def _construct_encapsulation_geometry(
self, thickness: float
) -> netgen.libngpy._NgOCC.TopoDS_Shape:
"""Generate geometry of encapsulation layer around electrode.
Parameters
----------
thickness : float
Thickness of encapsulation layer.
Returns
-------
netgen.libngpy._NgOCC.TopoDS_Shape
"""
center = tuple(np.array(self._direction) * self._parameters.lead_diameter * 0.5)
radius = self._parameters.lead_diameter * 0.5 + thickness
height = self._parameters.total_length - self._parameters.tip_length
tip = occ.Sphere(c=center, r=radius)
lead = occ.Cylinder(p=center, d=self._direction, r=radius, h=height)
encapsulation = tip + lead
encapsulation.bc("EncapsulationLayerSurface")
encapsulation.mat("EncapsulationLayer")
return encapsulation.Move(v=self._position) - self.geometry

def _construct_geometry(self) -> netgen.libngpy._NgOCC.TopoDS_Shape:
contacts = self.__contacts()
# TODO check
electrode = netgen.occ.Glue([self.__body() - contacts, contacts])
return electrode.Move(v=self._position)

def __body(self) -> netgen.libngpy._NgOCC.TopoDS_Shape:
radius = self._parameters.lead_diameter * 0.5
center = tuple(np.array(self._direction) * radius)
tip = occ.Sphere(c=center, r=radius)
height = self._parameters.total_length - self._parameters.tip_length
lead = occ.Cylinder(p=center, d=self._direction, r=radius, h=height)
body = tip + lead
body.bc(self._boundaries["Body"])
return body

def __contacts(self) -> netgen.libngpy._NgOCC.TopoDS_Shape:
radius = self._parameters.lead_diameter * 0.5
direction = self._direction

center = tuple(np.array(direction) * radius)
# define half space at tip_center to use to construct a hemsiphere as part of the contact tip
half_space = netgen.occ.HalfSpace(p=center, n=direction)
contact_tip = occ.Sphere(c=center, r=radius) * half_space
h_pt2 = self._parameters.contact_length - radius
contact_pt2 = occ.Cylinder(p=center, d=direction, r=radius, h=h_pt2)
# defining first contact
contact = contact_tip + contact_pt2
height = self._parameters.contact_length
contact_cyl = occ.Cylinder(p=(0, 0, 0), d=direction, r=radius, h=height)

contacts = []
for count in range(self._n_contacts):
for count in range(self._parameters.n_contacts):
name = self._boundaries[f"Contact_{count + 1}"]
contact.bc(name)
min_edge_z_val = float("inf")
Expand Down

0 comments on commit 781aebd

Please sign in to comment.