# Copyright 2023, 2024 Konstantin Butenko, Jan Philipp Payonk
# Copyright 2023, 2024 Johannes Reding, Julius Zimmermann
# SPDX-License-Identifier: GPL-3.0-or-later
import os
from copy import deepcopy
from typing import ClassVar
from .materials import MATERIALS
[docs]class Settings:
"""Default settings of OSS-DBS."""
CUSTOM_SETTING: ClassVar[dict] = {
"BrainRegion": {
"Center": {"x[mm]": 0, "y[mm]": 0, "z[mm]": 0},
"Dimension": {"x[mm]": 40, "y[mm]": 40, "z[mm]": 40},
"Shape": "Ellipsoid",
}
}
ELECTRODE_SETTING: ClassVar[dict] = {
"Name": "BostonScientificVerciseDirected",
"Rotation[Degrees]": 0.0,
"Direction": {"x[mm]": 0.0, "y[mm]": 0.0, "z[mm]": 1.0},
"TipPosition": {"x[mm]": 0.0, "y[mm]": 0.0, "z[mm]": 0.0},
"Contacts": [],
"EncapsulationLayer": {},
}
CONTACT_SETTING: ClassVar[dict] = {
"Contact_ID": 0,
"Active": False,
"Current[A]": 0.0,
"Voltage[V]": 0.0,
"Floating": False,
"SurfaceImpedance": {"Model": None, "Parameters": {}},
"MaxMeshSize": 1e6,
"MaxMeshSizeEdge": 1e6,
}
ENCAPSULATION_SETTING: ClassVar[dict] = {
"Thickness[mm]": 0.0,
"Material": "Gray matter",
"DielectricModel": "ColeCole4",
"DielectricParameters": None,
"MaxMeshSize": 1e6,
}
SETTING: ClassVar[dict] = {
"Electrodes": [],
"Surfaces": [],
"MaterialDistribution": {
"MRIPath": "",
"MRIMapping": MATERIALS,
"DiffusionTensorActive": False,
"DTIPath": "",
"WMMasking": True,
},
"DielectricModel": {"Type": "ColeCole4", "CustomParameters": None},
"Mesh": {
"LoadMesh": False,
"LoadPath": "",
"MeshingHypothesis": {
"Type": "Default",
"MaxMeshSize": 1e6,
"MeshSizeFilename": "",
},
"HPRefinement": {
"Active": False,
"Levels": 2,
"Factor": 0.125,
},
"AdaptiveMeshRefinement": {
"Active": False,
"MaxIterations": 10,
"ErrorTolerance": 0.1,
},
"MaterialRefinementSteps": 0,
"MeshSize": {"Edges": {}, "Faces": {}, "Volumes": {}},
"SaveMesh": False,
"SavePath": "mesh",
},
"EQSMode": False,
"FEMOrder": 2,
"StimulationSignal": {
"Type": "Rectangle",
"ListOfFrequencies": [130.0],
"Frequency[Hz]": 130.0,
"PulseWidth[us]": 60.0,
"PulseTopWidth[us]": 0.0,
"CounterPulseWidth[us]": 0.0,
"InterPulseWidth[us]": 0.0,
"SpectrumMode": "FullSpectrum",
"CounterAmplitude": 1.0, # relative to amplitude given by contact
"CutoffFrequency": 1e6,
"CurrentControlled": False,
},
"Solver": {
"Type": "CG",
"Preconditioner": "bddc",
"PreconditionerKwargs": {},
"MaximumSteps": 10000,
"Precision": 1e-12,
},
"PointModel": {
"Pathway": {"Active": False, "FileName": "", "ExportField": False},
"Lattice": {
"Active": False,
"Center": {"x[mm]": 0, "y[mm]": 0, "z[mm]": 0},
"Shape": {"x": 10, "y": 10, "z": 10},
"Direction": {"x[mm]": 0, "y[mm]": 0, "z[mm]": 1},
"PointDistance[mm]": 0.1,
"CollapseVTA": False,
"ExportField": True,
},
"VoxelLattice": {
"Active": False,
"Shape": {"x": 10, "y": 10, "z": 10},
"TimeDomain": False,
"ExportField": True,
},
},
"OutputPath": "Results",
"ComputeImpedance": False,
"ImpedanceAnalysis": {
"Enabled": False,
"Frequencies": None,
"IncludeFloating": True,
},
"ComputeCurrents": False,
"ExportVTK": False,
"ExportVTKSubdivision": 0,
"ExportFrequency": None,
"ExportElectrode": False,
"ModelSide": 0,
"CalcAxonActivation": False,
"DielectricAccuracy": 0.01,
"ActivationThresholdVTA[V-per-m]": None,
"FailFlag": "oss",
"OutOfCore": False,
"PathwayFile": None,
"StimSets": {
"Active": False,
"StimSetsFile": None,
},
"StimulationFolder": os.getcwd(),
"TruncateAfterActivePartRatio": None,
}
def __init__(self, partial_settings: dict) -> None:
self._partial_settings = partial_settings
[docs] def complete_settings(self) -> dict:
"""Complete dictionary provided by user with default settings."""
settings = deepcopy(self.CUSTOM_SETTING)
settings.update(deepcopy(self.SETTING))
self._update(settings, self._partial_settings)
self._update_electrodes(settings)
return settings
def _update(self, target: dict, settings: dict) -> None:
for key in [key for key in target.keys() if key in settings.keys()]:
is_dict = False
if isinstance(target[key], dict):
# empty dicts yield False
is_dict = bool(target[key])
if is_dict:
self._update(target[key], settings[key])
else:
target[key] = settings[key]
def _update_electrodes(self, settings: dict) -> None:
for index, electrode in enumerate(settings["Electrodes"]):
for key, value in self.ELECTRODE_SETTING.items():
if key not in electrode:
settings["Electrodes"][index][key] = value
if key == "Contacts":
contacts = settings["Electrodes"][index][key]
self._update_contacts(contacts)
settings["Electrodes"][index][key] = contacts
for key, value in self.ENCAPSULATION_SETTING.items():
if key not in settings["Electrodes"][index]["EncapsulationLayer"]:
settings["Electrodes"][index]["EncapsulationLayer"][key] = value
def _update_contacts(self, contacts: list) -> None:
for index, contact in enumerate(contacts):
for key, value in self.CONTACT_SETTING.items():
if key not in contact:
contacts[index][key] = value