# Copyright 2023, 2024 Konstantin Butenko, Johannes Reding, Julius Zimmermann
# SPDX-License-Identifier: GPL-3.0-or-later
from typing import ClassVar
[docs]class TypeChecker:
"""Check types of input dictionary."""
TYPES: ClassVar[dict] = {
"DielectricModel": {
"Type": str,
},
"Electrodes": list,
"FailFlag": str,
"StimulationFolder": str,
"TruncateAfterActivePartRatio": (type(None), float),
"ModelSide": int,
"CalcAxonActivation": bool,
"OutOfCore": bool,
"StimSets": {
"Active": bool,
"StimSetsFile": (type(None), str),
},
"AdaptiveMeshRefinement": bool,
# TODO add more tests
"PathwayFile": (type(None), str),
"DielectricAccuracy": float,
"ActivationThresholdVTA[V-per-m]": (type(None), float),
"Contacts": {"MaxMeshSize": float, "MaxMeshSizeEdge": float},
"EncapsulationLayer": {
"Thickness[mm]": (int, float),
"Material": str,
"MaxMeshSize": (int, float),
},
"EQSMode": bool,
"FEMOrder": int,
"MaterialDistribution": {
"MRIPath": str,
"DiffusionTensorActive": bool,
"DTIPath": str,
"WMMasking": bool,
},
"Mesh": {
"LoadMesh": bool,
"LoadPath": str,
"MeshElementOrder": int,
"MeshingHypothesis": {
"Type": str,
"MaxMeshSize": (int, float),
"MeshSizeFilename": str,
},
"SaveMesh": bool,
"SavePath": str,
},
"OutputPath": str,
"BrainRegion": {
"Center": {
"x[mm]": (int, float),
"y[mm]": (int, float),
"z[mm]": (int, float),
},
"Dimension": {
"x[mm]": (int, float),
"y[mm]": (int, float),
"z[mm]": (int, float),
},
"Shape": str,
},
"Solver": {
"Type": str,
"Preconditioner": str,
"PreconditionerKwargs": dict,
"MaximumSteps": int,
"Precision": (int, float),
},
"StimulationSignal": {
"Type": str,
"Frequency[Hz]": (int, float),
"PulseWidth[us]": (int, float),
"PulseTopWidth[us]": (int, float),
"CounterPulseWidth[us]": (int, float),
"InterPulseWidth[us]": (int, float),
"SpectrumMode": str,
"CounterAmplitude": float,
"CutoffFrequency": float,
},
"PointModel": {
"Pathway": {"Active": bool, "FileName": str, "ExportField": bool},
"Lattice": {
"Center": {
"x[mm]": (int, float),
"y[mm]": (int, float),
"z[mm]": (int, float),
},
"Direction": {
"x[mm]": (int, float),
"y[mm]": (int, float),
"z[mm]": (int, float),
},
"CollapseVTA": bool,
"PointDistance[mm]": (int, float),
"Shape": {"x": int, "y": int, "z": int},
"ExportField": bool,
},
"VoxelLattice": {
"Active": bool,
"Shape": {"x": int, "y": int, "z": int},
"TimeDomain": bool,
"ExportField": bool,
},
},
}
ELECTRODE_SETTING: ClassVar[dict] = {
"Name": str,
"Rotation[Degrees]": (int, float),
"Direction": {
"x[mm]": (int, float),
"y[mm]": (int, float),
"z[mm]": (int, float),
},
"TipPosition": {
"x[mm]": (int, float),
"y[mm]": (int, float),
"z[mm]": (int, float),
},
"Contacts": list,
}
CONTACT_SETTING: ClassVar[dict] = {
"Contact_ID": int,
"Active": bool,
"Current[A]": (int, float),
"Voltage[V]": (int, float),
"Floating": bool,
"SurfaceImpedance": {"Model": (type(None), str), "Parameters": dict},
}
[docs] @classmethod
def check(cls, settings: dict) -> None:
"""Check types in settings dictionary."""
cls.__check(cls.TYPES, settings)
cls.__check_electrodes(settings)
@classmethod
def __check(cls, target: dict, settings: dict) -> dict:
for key in [key for key in target.keys() if key in settings.keys()]:
if isinstance(target[key], dict):
try:
cls.__check(target[key], settings[key])
except TypeError as e:
message = f"['{key}']" + str(e)
raise TypeError(message) from None
else:
if not isinstance(settings[key], target[key]):
message = f"['{key}'] is not of instance {target[key]}"
raise TypeError(message)
@classmethod
def __check_contacts(cls, contacts: list) -> None:
for index, contact in enumerate(contacts):
try:
cls.__check(cls.CONTACT_SETTING, contact)
except TypeError as e:
message = f"['Contacts'][{index}]"
raise TypeError(message + str(e)) from None
@classmethod
def __check_electrodes(cls, settings: dict) -> None:
for index, electrode in enumerate(settings["Electrodes"]):
try:
cls.__check(cls.ELECTRODE_SETTING, electrode)
cls.__check_contacts(electrode["Contacts"])
except TypeError as e:
message = f"['Electrodes'][{index}]"
raise TypeError(message + str(e)) from None