diff --git a/bluecellulab/cell/core.py b/bluecellulab/cell/core.py index 9a42d910..9d2ec32f 100644 --- a/bluecellulab/cell/core.py +++ b/bluecellulab/cell/core.py @@ -19,7 +19,7 @@ from pathlib import Path import queue -from typing import Iterable, List, Optional, Tuple +from typing import Callable, Iterable, List, Optional, Tuple from typing_extensions import deprecated import neuron @@ -162,6 +162,49 @@ def __init__(self, # Persistent objects, like clamps, that exist as long # as the object exists self.persistent: list[HocObjectType] = [] + self._local_to_global_matrix: Optional[np.ndarray] = None + + def set_local_to_global_matrix( + self, position: np.ndarray, quaternion: Optional[np.ndarray] = None + ) -> None: + """Set the local-to-global coordinate transform for this cell. + + The transform is a 3x4 matrix [R | t] built from a unit + quaternion (w, x, y, z) and a 3D translation vector. If + *quaternion* is None, only the translation is applied (identity + rotation). + """ + matrix = np.eye(3, 4, dtype=np.float64) + if quaternion is not None: + w, x, y, z = quaternion + # Normalised quaternion → 3×3 rotation matrix + xx, yy, zz = x * x, y * y, z * z + xy, xz, yz = x * y, x * z, y * z + wx, wy, wz = w * x, w * y, w * z + r = np.array( + [ + [1.0 - 2 * (yy + zz), 2 * (xy - wz), 2 * (xz + wy)], + [2 * (xy + wz), 1.0 - 2 * (xx + zz), 2 * (yz - wx)], + [2 * (xz - wy), 2 * (yz + wx), 1.0 - 2 * (xx + yy)], + ], + dtype=np.float64, + ) + matrix[:, :3] = r + matrix[:, 3] = position + self._local_to_global_matrix = matrix + + def local_to_global_coord_mapping(self, points: np.ndarray) -> np.ndarray: + """Apply the local-to-global coordinate transform to *points*. + + Args: + points: array of shape (N, 3) with local coordinates. + + Returns: + Array of shape (N, 3) with global coordinates. + """ + if self._local_to_global_matrix is None: + return points + return points @ self._local_to_global_matrix[:, :3].T + self._local_to_global_matrix[:, 3] def _init_psections(self) -> None: """Initialize the psections of the cell.""" @@ -483,7 +526,6 @@ def create_netcon_spikedetector(self, target: HocObjectType, location: str, thre - The position is out of bounds (e.g., negative or greater than 1.0). """ - if location == "soma": sec = public_hoc_cell(self.cell).soma[0] source = sec(1)._ref_v @@ -564,8 +606,6 @@ def add_replay_minis(self, sid = synapse_id[1] weight = syn_description[SynapseProperty.G_SYNX] - # numpy int to int - post_sec_id = int(syn_description[SynapseProperty.POST_SECTION_ID]) weight_scalar = connection_modifiers.get('Weight', 1.0) exc_mini_frequency, inh_mini_frequency = mini_frequencies \ @@ -787,7 +827,6 @@ def add_variable_recording( segx: Segment position between 0 and 1. dt: Optional recording time step. """ - if section is None: section = self.soma @@ -837,6 +876,173 @@ def n_segments(self) -> int: """Get the number of segments in the cell.""" return sum(section.nseg for section in self.sections.values()) + def compute_segment_local_coordinates(self) -> dict[str, np.ndarray]: + """Compute local 3D coordinates of segment endpoints for all sections. + + Extracts 3D point data from NEURON sections and interpolates segment + boundary positions along the section axis. Used for extracellular + stimulus displacement calculations. + + Returns: + Dictionary mapping section names to arrays of shape (nseg+1, 3) + containing [x, y, z] coordinates of segment endpoints in micrometers. + Sections without 3D point data return empty arrays. + """ + segment_coords = {} + + for section in self.sections.values(): + n_pts = int(neuron.h.n3d(sec=section)) + + if n_pts == 0: + logger.warning( + f"Section {section.name()} has no 3D points, " + "cannot compute segment coordinates" + ) + segment_coords[section.name()] = np.array([]) + continue + + arc_positions = np.array([neuron.h.arc3d(i, sec=section) for i in range(n_pts)]) + x_coords = np.array([neuron.h.x3d(i, sec=section) for i in range(n_pts)]) + y_coords = np.array([neuron.h.y3d(i, sec=section) for i in range(n_pts)]) + z_coords = np.array([neuron.h.z3d(i, sec=section) for i in range(n_pts)]) + + if section.L > 0: + arc_normalized = arc_positions / section.L + else: + arc_normalized = arc_positions + + seg_positions = np.linspace(0, 1, section.nseg + 1) + + x_interp = np.interp(seg_positions, arc_normalized, x_coords) + y_interp = np.interp(seg_positions, arc_normalized, y_coords) + z_interp = np.interp(seg_positions, arc_normalized, z_coords) + + segment_coords[section.name()] = np.column_stack((x_interp, y_interp, z_interp)) + + return segment_coords + + compute_segment_coordinates = compute_segment_local_coordinates + + def compute_segment_global_coordinates(self) -> dict[str, np.ndarray]: + """Compute global 3D coordinates of segment endpoints for all sections. + + Same as :meth:`compute_segment_local_coordinates` but with the + local-to-global transform applied (if set via + :meth:`set_local_to_global_matrix`). + + Returns: + Dictionary mapping section names to arrays of shape (nseg+1, 3) + containing [x, y, z] global coordinates in micrometers. + Sections without 3D point data return empty arrays. + """ + local_coords = self.compute_segment_local_coordinates() + global_coords = {} + for sec_name, coords in local_coords.items(): + if len(coords) == 0: + global_coords[sec_name] = coords + else: + global_coords[sec_name] = self.local_to_global_coord_mapping(coords) + return global_coords + + @staticmethod + def get_segment_position( + sec_seg_points: np.ndarray, + soma_local_position: np.ndarray, + section: NeuronSection, + x: float, + func_loc2glob: Optional[Callable[[np.ndarray], np.ndarray]] = None, + ) -> Optional[np.ndarray]: + """Get the global coordinates of the segment. + + For axon and myelin, interpolate along the y-axis of the local soma coordinates, and then + convert to global coordinates. + + Args: + sec_seg_points: segment global positions in the current section + soma_local_position: soma local position to interpolate axon and myelin position + section: hoc section + x: offset along the section, in [0, 1] + func_loc2glob: function to convert local coordinates to global ones for axon and myelin, + return the local coordinates if None + Returns: + global coordinates [x, y, z], type np.array + """ + if not section.n3d(): # Axonal segments don't have 3d points associated, so we guess + if "axon" in section.name(): + pattern = r"axon\[(\d+)\]$" + if match := re.search(pattern, section.name()): + axon_index = int(match.group(1)) + local_positions = Cell.interp_axon_positions( + x, axon_index, soma_local_position + ) + return func_loc2glob(local_positions) if func_loc2glob else local_positions + elif "myelin" in section.name(): + pattern = r"myelin\[(\d+)\]$" + if match := re.search(pattern, section.name()): + myelin_index = int(match.group(1)) + local_positions = Cell.interp_myelin_positions( + x, myelin_index, soma_local_position + ) + return func_loc2glob(local_positions) if func_loc2glob else local_positions + else: + raise ValueError(f"section {section.name()} has no 3d points defined") + else: + seg_index = int(np.floor((len(sec_seg_points) - 1) * x)) + return sec_seg_points[seg_index] + return None + + @staticmethod + def interp_axon_positions(x: float, axon_index: int, soma_position: np.ndarray) -> np.ndarray: + """Interpolate the coordinates of the axon segment for the given x, + because of no 3d point for the new axons. + + Assume that the axon is oriented along the y-axis from soma, 30 + um displaced for 1st axon, 60 um for 2nd axon, the same x- and + z-coordinates as soma. x=0 is soma, and x=1 is the end of the + axon section. + """ + if axon_index > 1: + raise ValueError("More than 2 axon sections exist!") + xpos = [soma_position[0], soma_position[0]] + ypos = [ + soma_position[1] - 30 * int(axon_index), + soma_position[1] - 30 * int(axon_index + 1), + ] + zpos = [soma_position[2], soma_position[2]] + lens = [0, 1] + + # Interpolate the coordinates for the given location x along the segment + seg_x = np.interp(x, lens, xpos) + seg_y = np.interp(x, lens, ypos) + seg_z = np.interp(x, lens, zpos) + + return np.array([seg_x, seg_y, seg_z]) + + @staticmethod + def interp_myelin_positions(x: float, myelin_index: int, soma_position: np.ndarray) -> np.ndarray: + """Interpolate the coordinates of the myelin segment for the given x, + because of no 3d point for the new myelin section. + + Assume that the myelin is oriented along the y-axis from soma, + 1000 um displaced after the 2nd axon, i.e. [soma-60, soma-1000] + """ + if myelin_index > 0: + raise ValueError("More than 1 myelin section exist!") + xpos = [soma_position[0], soma_position[0]] + ypos = [ + (soma_position[1] - 60) - 1000 * int(myelin_index), + (soma_position[1] - 60) - 1000 * int(myelin_index + 1), + ] + zpos = [soma_position[2], soma_position[2]] + lens = [0, 1] + + # Interpolate the coordinates for the given location x along the segment + seg_x = np.interp(x, lens, xpos) + seg_y = np.interp(x, lens, ypos) + seg_z = np.interp(x, lens, zpos) + + return np.array([seg_x, seg_y, seg_z]) + def add_synapse_replay( self, stimulus: SynapseReplay, spike_threshold: float, spike_location: str ) -> None: @@ -901,8 +1107,12 @@ def delete(self): """Delete the cell.""" self.delete_plottable() if hasattr(self, 'cell') and self.cell is not None: - if public_hoc_cell(self.cell) is not None and hasattr(public_hoc_cell(self.cell), 'clear'): - public_hoc_cell(self.cell).clear() + try: + hoc_cell = public_hoc_cell(self.cell) + except BluecellulabError: + hoc_cell = None + if hoc_cell is not None and hasattr(hoc_cell, 'clear'): + hoc_cell.clear() self.connections = None self.synapses = None @@ -1092,7 +1302,6 @@ def add_currents_recordings( ) -> list[str]: """Record all available currents (ionic + optionally nonspecific) at (section, segx).""" - # discover what’s available at this site available = currents_vars(section) chosen: list[str] = [] diff --git a/bluecellulab/circuit/circuit_access/sonata_circuit_access.py b/bluecellulab/circuit/circuit_access/sonata_circuit_access.py index 13c4d27d..dca79184 100644 --- a/bluecellulab/circuit/circuit_access/sonata_circuit_access.py +++ b/bluecellulab/circuit/circuit_access/sonata_circuit_access.py @@ -25,6 +25,7 @@ from bluepysnap.exceptions import BluepySnapError from bluepysnap import Circuit as SnapCircuit import neuron +import numpy as np import pandas as pd from bluecellulab import circuit from bluecellulab.circuit.circuit_access.definition import CircuitAccess, EmodelProperties @@ -93,6 +94,35 @@ def get_cell_properties( cell_id.id, properties=properties ) + def get_cell_position_rotation( + self, cell_id: CellId + ) -> tuple[np.ndarray, Optional[np.ndarray]]: + """Return (position, quaternion) for a SONATA cell if available. + + Position is a 3-element ndarray [x, y, z]. Quaternion is a + 4-element ndarray [w, x, y, z] or None. Returns (zeros, None) + when the attributes are not present. + """ + node_pop = self._circuit.nodes[cell_id.population_name] + props = node_pop.get(cell_id.id) + if "x" in props and "y" in props and "z" in props: + position = np.array( + [props["x"], props["y"], props["z"]], dtype=np.float64 + ) + orientation_keys = ( + "orientation_w", + "orientation_x", + "orientation_y", + "orientation_z", + ) + if all(k in props for k in orientation_keys): + quaternion = np.array( + [props[k] for k in orientation_keys], dtype=np.float64 + ) + return position, quaternion + return position, None + return np.zeros(3, dtype=np.float64), None + @staticmethod def _compute_pop_ids(source: str, target: str) -> tuple[int, int]: """Compute the population ids from the population names.""" diff --git a/bluecellulab/circuit_simulation.py b/bluecellulab/circuit_simulation.py index d12e00fb..7c16135a 100644 --- a/bluecellulab/circuit_simulation.py +++ b/bluecellulab/circuit_simulation.py @@ -59,6 +59,7 @@ ShotNoise, ) import bluecellulab.stimulus.circuit_stimulus_definitions as circuit_stimulus_definitions +from bluecellulab.stimulus.extracellular import ElectrodeSource from bluecellulab.exceptions import BluecellulabError from bluecellulab.simulation import ( set_global_condition_parameters, @@ -158,6 +159,7 @@ def __init__( self.gids: Optional[GidNamespace] = None self._instantiated_cells_mpi: set[CellId] | None = None + self._efield_sources: dict[CellId, ElectrodeSource] = {} def instantiate_gids( self, @@ -181,6 +183,7 @@ def instantiate_gids( add_sinusoidal_stimuli: bool = False, add_linear_stimuli: bool = False, add_seclamp_stimuli: bool = False, + add_extracellular_stimuli: bool = False, add_subthreshold_stimuli: bool = False, ): """Instantiate a list of cells. @@ -374,6 +377,7 @@ def instantiate_gids( or add_sinusoidal_stimuli or add_linear_stimuli or add_seclamp_stimuli + or add_extracellular_stimuli or add_subthreshold_stimuli ): self._add_stimuli( @@ -386,9 +390,13 @@ def instantiate_gids( add_sinusoidal_stimuli=add_sinusoidal_stimuli, add_linear_stimuli=add_linear_stimuli, add_seclamp_stimuli=add_seclamp_stimuli, + add_extracellular_stimuli=add_extracellular_stimuli, add_subthreshold_stimuli=add_subthreshold_stimuli, ) + if add_extracellular_stimuli: + self._apply_extracellular_stimuli() + self.recording_index, self.sites_index = prepare_recordings_for_reports( cells=self.cells, simulation_config=self.circuit_access.config, @@ -414,6 +422,7 @@ def _add_stimuli( add_sinusoidal_stimuli=False, add_linear_stimuli=False, add_seclamp_stimuli=False, + add_extracellular_stimuli=False, add_subthreshold_stimuli=False, ) -> None: """Instantiate all the stimuli.""" @@ -443,18 +452,33 @@ def _add_stimuli( gids_of_target = self.circuit_access.get_target_cell_ids( stimulus.node_set ) - for cell_id in self.cells: - if cell_id not in gids_of_target: - continue - sec = self.cells[cell_id].soma - sec_name = sec.name().split(".")[-1] - targets.append((cell_id, sec, 0.5, sec_name)) + if isinstance(stimulus, circuit_stimulus_definitions.SpatiallyUniformEField): + # Neurodamus target_point_list includes all sections of each cell. + for cell_id in self.cells: + if cell_id not in gids_of_target: + continue + cell = self.cells[cell_id] + for sec in cell.sections.values(): + sec_name = sec.name().split(".")[-1] + targets.append((cell_id, sec, 0.5, sec_name)) + else: + for cell_id in self.cells: + if cell_id not in gids_of_target: + continue + sec = self.cells[cell_id].soma + sec_name = sec.name().split(".")[-1] + targets.append((cell_id, sec, 0.5, sec_name)) else: raise ValueError( f"Stimulus '{stimulus}' has neither node_set nor compartment_set; " "cannot resolve target location." ) + if isinstance(stimulus, circuit_stimulus_definitions.SpatiallyUniformEField): + if add_extracellular_stimuli: + self._add_extracellular_stimulus(stimulus, targets) + continue + for cell_id, sec, segx, sec_name in targets: if isinstance(stimulus, circuit_stimulus_definitions.Noise): if add_noise_stimuli: @@ -556,6 +580,128 @@ def _add_stimuli( elif isinstance(stimulus, (OrnsteinUhlenbeck, RelativeOrnsteinUhlenbeck)): ornstein_uhlenbeck_stim_count += 1 + def _add_extracellular_stimulus( + self, + stimulus: circuit_stimulus_definitions.SpatiallyUniformEField, + targets: list[tuple], + ) -> None: + """Process extracellular e-field stimulus and accumulate into + ElectrodeSource per cell. + + Args: + stimulus: SpatiallyUniformEField stimulus definition + targets: list of (cell_id, section, segx, section_name) tuples + """ + cell_targets: dict[CellId, list[tuple]] = {} + for cell_id, sec, segx, sec_name in targets: + if cell_id not in cell_targets: + cell_targets[cell_id] = [] + cell_targets[cell_id].append((sec, segx)) + + for cell_id, seg_list in cell_targets.items(): + cell = self.cells[cell_id] + + if cell_id not in self._efield_sources: + es = ElectrodeSource( + base_amp=0, + delay=stimulus.delay, + duration=stimulus.duration, + fields=stimulus.fields, + ramp_up_time=stimulus.ramp_up_time, + ramp_down_time=stimulus.ramp_down_time, + dt=self.dt, + ) + self._efield_sources[cell_id] = es + else: + new_es = ElectrodeSource( + base_amp=0, + delay=stimulus.delay, + duration=stimulus.duration, + fields=stimulus.fields, + ramp_up_time=stimulus.ramp_up_time, + ramp_down_time=stimulus.ramp_down_time, + dt=self.dt, + ) + self._efield_sources[cell_id] += new_es + + es = self._efield_sources[cell_id] + segment_coords_local = cell.compute_segment_local_coordinates() + segment_coords_global = cell.compute_segment_global_coordinates() + + soma_coords_global = segment_coords_global.get(cell.soma.name()) + if soma_coords_global is None or len(soma_coords_global) == 0: + logger.warning( + f"Cell {cell_id} soma has no 3D coordinates, " + "cannot apply extracellular stimulus" + ) + continue + + soma_position_global = np.mean(soma_coords_global, axis=0) + soma_position_local = np.mean( + segment_coords_local.get(cell.soma.name(), soma_coords_global), + axis=0, + ) + + def local_to_global(pos: np.ndarray) -> np.ndarray: + stacked = np.vstack([soma_position_local, pos]) + transformed = cell.local_to_global_coord_mapping(stacked) + return transformed[1] + + n_segments = 0 + for sec, segx in seg_list: + segment_position = None + + if "soma" in sec.name(): + segment_position = soma_position_global + else: + sec_coords = segment_coords_global.get(sec.name()) + if sec_coords is not None and len(sec_coords) > 0: + seg_idx = int(segx * sec.nseg) + if seg_idx >= len(sec_coords): + seg_idx = len(sec_coords) - 1 + segment_position = sec_coords[seg_idx] + elif not sec.n3d(): + # Try axon/myelin interpolation for sections without 3D points + try: + segment_position = cell.get_segment_position( + np.array([]), + soma_position_local, + sec, + segx, + func_loc2glob=local_to_global, + ) + except ValueError: + logger.warning( + f"Section {sec.name()} has no 3D coordinates and " + "could not interpolate, skipping" + ) + continue + else: + logger.warning( + f"Section {sec.name()} has no 3D coordinates, skipping" + ) + continue + + if segment_position is None: + continue + + displacement_vec = (segment_position - soma_position_global) * 1e-6 + + segment = sec(segx) + es.segment_displacements[segment] = displacement_vec + n_segments += 1 + + logger.debug( + f"Added extracellular stimulus to cell {cell_id} " + f"with {n_segments} target segments" + ) + + def _apply_extracellular_stimuli(self) -> None: + """Apply all accumulated extracellular field sources to segments.""" + for cell_id, es in self._efield_sources.items(): + es.apply_segment_potentials() + logger.debug(f"Applied extracellular potentials to cell {cell_id}") + def _add_synapses(self, pre_gids=None, add_minis=False): """Instantiate all the synapses.""" for cell_id in self.cells: @@ -857,6 +1003,13 @@ def _add_cells(self, cell_ids: list[CellId]) -> None: self.cells[cell_id] = cell if self.circuit_access.node_properties_available: cell.connect_to_circuit(SonataProxy(cell_id, self.circuit_access)) + # Set local-to-global transform if position data is available. + if hasattr(self.circuit_access, "get_cell_position_rotation"): + try: + position, quaternion = self.circuit_access.get_cell_position_rotation(cell_id) + cell.set_local_to_global_matrix(position, quaternion) + except (KeyError, ValueError): + pass def _apply_modifications(self) -> None: """Apply condition modifications from the simulation config to @@ -885,8 +1038,13 @@ def _instantiate_synapse(self, cell_id: CellId, syn_id: SynapseID, syn_descripti condition_parameters = self.circuit_access.config.condition_parameters() self.cells[cell_id].add_replay_synapse( - syn_id, syn_description, syn_connection_parameters, condition_parameters, - popids=popids, extracellular_calcium=self.circuit_access.config.extracellular_calcium) + syn_id, + syn_description, + syn_connection_parameters, + condition_parameters, + popids=popids, + extracellular_calcium=self.circuit_access.config.extracellular_calcium, + ) if add_minis: mini_frequencies = self.circuit_access.fetch_mini_frequencies(cell_id) logger.debug( diff --git a/bluecellulab/stimulus/circuit_stimulus_definitions.py b/bluecellulab/stimulus/circuit_stimulus_definitions.py index 5353d6fd..7886d2a7 100644 --- a/bluecellulab/stimulus/circuit_stimulus_definitions.py +++ b/bluecellulab/stimulus/circuit_stimulus_definitions.py @@ -50,6 +50,7 @@ class Pattern(Enum): ORNSTEIN_UHLENBECK = "ornstein_uhlenbeck" RELATIVE_ORNSTEIN_UHLENBECK = "relative_ornstein_uhlenbeck" SINUSOIDAL = "sinusoidal" + SPATIALLY_UNIFORM_E_FIELD = "spatially_uniform_e_field" SECLAMP = "seclamp" SUBTHRESHOLD = "subthreshold" @@ -102,6 +103,8 @@ def from_sonata(cls, pattern: str) -> Pattern: return Pattern.RELATIVE_ORNSTEIN_UHLENBECK elif pattern == "sinusoidal": return Pattern.SINUSOIDAL + elif pattern == "spatially_uniform_e_field": + return Pattern.SPATIALLY_UNIFORM_E_FIELD elif pattern == "seclamp": return Pattern.SECLAMP elif pattern == "subthreshold": @@ -397,6 +400,17 @@ def from_sonata(cls, stimulus_entry: dict, config_dir: Optional[str] = None) -> node_set=node_set, compartment_set=compartment_set, ) + elif pattern == Pattern.SPATIALLY_UNIFORM_E_FIELD: + return SpatiallyUniformEField( + target=target_name, + delay=stimulus_entry["delay"], + duration=stimulus_entry["duration"], + fields=stimulus_entry["fields"], + ramp_up_time=stimulus_entry.get("ramp_up_time", 0.0), + ramp_down_time=stimulus_entry.get("ramp_down_time", 0.0), + node_set=node_set, + compartment_set=compartment_set, + ) elif pattern == Pattern.SECLAMP: return SEClamp( target=target_name, @@ -559,6 +573,35 @@ class SEClamp(Stimulus): series_resistance: float +@dataclass(frozen=True, config=dict(extra="forbid")) +class SpatiallyUniformEField(Stimulus): + fields: list[dict[str, float]] + ramp_up_time: NonNegativeFloat = 0.0 + ramp_down_time: NonNegativeFloat = 0.0 + + @field_validator("fields") + @classmethod + def validate_fields(cls, v): + if not v: + raise ValueError("fields list cannot be empty") + + for i, field_dict in enumerate(v): + if "Ex" not in field_dict or "Ey" not in field_dict or "Ez" not in field_dict: + raise ValueError( + f"Field {i} must contain Ex, Ey, and Ez components" + ) + + frequency = field_dict.get("frequency", 0.0) + if frequency < 0: + raise ValueError(f"Field {i} frequency must be non-negative") + + phase = field_dict.get("phase", 0.0) + if not isinstance(phase, (int, float)): + raise ValueError(f"Field {i} phase must be a number") + + return v + + @dataclass(frozen=True, config=dict(extra="forbid")) class SubThreshold(Stimulus): """Injects a current step at some percent below a cell's threshold.""" diff --git a/bluecellulab/stimulus/extracellular.py b/bluecellulab/stimulus/extracellular.py new file mode 100644 index 00000000..6f7c0c84 --- /dev/null +++ b/bluecellulab/stimulus/extracellular.py @@ -0,0 +1,237 @@ +# Copyright 2023-2024 Blue Brain Project / EPFL + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Extracellular stimulus signal synthesis and application.""" + +from __future__ import annotations + +import numpy as np +import neuron + + +class ElectrodeSource: + """Constructs an extracellular potential field as the sum of multiple user- + defined e-fields. + + Applies the resulting signal to segment's e_extracellular reference. + Adapted from Neurodamus stimuli.py for BlueCelluLab. + + Args: + base_amp: baseline amplitude when signal is inactive + delay: start time delay in ms + duration: duration of the signal, not including ramp up and ramp down + fields: list of user-defined electric field components (dicts with Ex, Ey, Ez, frequency, phase) + ramp_up_time: duration during which the signal amplitude ramps up linearly from 0, in ms + ramp_down_time: duration during which the signal amplitude ramps down linearly to 0, in ms + dt: time step in ms + """ + + def __init__(self, base_amp, delay, duration, fields, ramp_up_time, ramp_down_time, dt): + self.time_vec = neuron.h.Vector() + self._cur_t = 0 + self._base_amp = base_amp + self._delay = delay + self.fields = fields + self.duration = duration + self.dt = dt + self.ramp_up_time = ramp_up_time + self.ramp_down_time = ramp_down_time + + if delay > 0: + self.time_vec.append(self._cur_t) + self._cur_t = delay + + self.efields = self.add_cosines() + self.segment_displacements = {} + self.segment_potentials = [] + + def delay_time(self, duration): + """Increments the ref time so that the next created signal is + delayed.""" + self._cur_t += duration + return self + + def add_cosines(self): + """Add multiple cosinusoidal signals. + + Returns: + numpy array of shape (3, n_timepoints) for Ex, Ey, Ez field components + """ + total_duration = self.duration + self.ramp_up_time + self.ramp_down_time + tvec = neuron.h.Vector() + tvec.indgen(self._cur_t, self._cur_t + total_duration, self.dt) + self.time_vec.append(tvec) + self.delay_time(total_duration) + + self.time_vec.append(self._cur_t + self.dt) + + res_x = neuron.h.Vector(len(self.time_vec)) + res_y = neuron.h.Vector(len(self.time_vec)) + res_z = neuron.h.Vector(len(self.time_vec)) + + for field in self.fields: + vec = neuron.h.Vector(len(tvec)) + freq = field.get("frequency", 0) + phase = field.get("phase", 0) + Ex = field["Ex"] + Ey = field["Ey"] + Ez = field["Ez"] + + vec.sin(freq, phase + np.pi / 2, self.dt) + self.apply_ramp(vec, self.dt) + + if self._delay > 0: + vec.insrt(0, self._base_amp) + vec.append(self._base_amp) + + res_x.add(vec.c().mul(Ex)) + res_y.add(vec.c().mul(Ey)) + res_z.add(vec.c().mul(Ez)) + + return np.array([res_x, res_y, res_z]) + + def compute_potentials(self, displacement_vec): + """Compute potential at a segment given displacement from reference + point. + + Args: + displacement_vec: 3D displacement vector in meters [dx, dy, dz] + + Returns: + numpy array of potentials in mV over time + """ + return np.dot(displacement_vec, self.efields) * 1e3 + + def apply_ramp(self, signal_vec, step): + """Apply linear ramp up and down to signal. + + Args: + signal_vec: hoc Vector to apply ramp to + step: time step in ms + """ + ramp_up_number = int(self.ramp_up_time / step) + ramp_down_number = int(self.ramp_down_time / step) + + if ramp_up_number > 0: + ramp_up = np.linspace(0, 1, ramp_up_number) + for i in range(ramp_up_number): + signal_vec[i] *= ramp_up[i] + if ramp_down_number > 0: + ramp_down = np.linspace(1, 0, ramp_down_number) + vec_len = len(signal_vec) + for i in range(ramp_down_number): + signal_vec[vec_len - ramp_down_number + i] *= ramp_down[i] + + def apply_segment_potentials(self): + """Apply potentials to segment.extracellular._ref_e for all + segments.""" + for segment, displacement in self.segment_displacements.items(): + section = segment.sec + e_ext_vec = neuron.h.Vector(self.compute_potentials(displacement)) + + if not section.has_membrane("extracellular"): + section.insert("extracellular") + + e_ext_vec.play(segment.extracellular._ref_e, self.time_vec, 0) + self.segment_potentials.append(e_ext_vec) + + self.cleanup() + + def cleanup(self): + """Clear unused variables to free memory.""" + self.efields = None + self.segment_displacements = None + + def __iadd__(self, other): + """Combine with another ElectrodeSource object. + + Merges time vectors and sums overlapping e-fields. + + Args: + other: another ElectrodeSource instance + + Returns: + self with combined time vector and e-fields + """ + assert np.isclose(self.dt, other.dt), "multiple extracellular stimuli must have common dt" + + combined_time_vec, self.efields = self._combine_time_efields( + self.time_vec.as_numpy(), + self.efields, + other.time_vec.as_numpy(), + other.efields, + self._delay > 0, + other._delay > 0, + self.dt, + ) + self.time_vec = neuron.h.Vector(combined_time_vec) + return self + + @staticmethod + def _combine_time_efields(t1_vec, efields1, t2_vec, efields2, is_delay1, is_delay2, dt): + """Combine time and efields vectors from 2 ElectrodeSource objects. + + Args: + t1_vec, t2_vec: numpy arrays of time points + efields1, efields2: arrays of shape (3, n_timepoints) for Ex, Ey, Ez + is_delay1, is_delay2: whether stimuli have delays + dt: time step + + Returns: + tuple of (combined_time_vec, combined_efields) + """ + if is_delay1: + t1_vec = t1_vec[1:] + efields1 = efields1[:, 1:] + if is_delay2: + t2_vec = t2_vec[1:] + efields2 = efields2[:, 1:] + + t1_ticks = np.round(t1_vec / dt).astype(np.int64) + t2_ticks = np.round(t2_vec / dt).astype(np.int64) + + if not (t1_ticks[-1] < t2_ticks[0] or t2_ticks[-1] < t1_ticks[0]): + combined_time_ticks = np.union1d(t1_ticks, t2_ticks) + + idx1_left = np.searchsorted(t1_ticks, combined_time_ticks, side="right") - 1 + idx1_right = np.searchsorted(t1_ticks, combined_time_ticks, side="left") + mask1 = idx1_left == idx1_right + + idx2_left = np.searchsorted(t2_ticks, combined_time_ticks, side="right") - 1 + idx2_right = np.searchsorted(t2_ticks, combined_time_ticks, side="left") + mask2 = idx2_left == idx2_right + + combined_efields = np.zeros((len(efields1), len(combined_time_ticks)), dtype=float) + combined_efields[:, mask1] += efields1[:, idx1_left[mask1]] + combined_efields[:, mask2] += efields2[:, idx2_left[mask2]] + + elif t1_ticks[-1] < t2_ticks[0]: + combined_time_ticks = np.concatenate((t1_ticks, t2_ticks)) + combined_efields = np.concatenate((efields1, efields2), axis=1) + else: + combined_time_ticks = np.concatenate((t2_ticks, t1_ticks)) + combined_efields = np.concatenate((efields2, efields1), axis=1) + + combined_time_vec = combined_time_ticks.astype(float) * dt + + if combined_time_vec[0] > 0: + combined_time_vec = np.concatenate([[0.0], combined_time_vec]) + combined_efields = np.concatenate( + [np.zeros((combined_efields.shape[0], 1)), combined_efields], axis=1 + ) + + assert combined_efields.shape[1] == len(combined_time_vec), ( + "Time and efield length mismatch" + ) + + return combined_time_vec, combined_efields diff --git a/examples/1-singlecell/singlecell_H5_morph_H5_container.ipynb b/examples/1-singlecell/singlecell_H5_morph_H5_container.ipynb index 7c7c1486..84a51bcc 100644 --- a/examples/1-singlecell/singlecell_H5_morph_H5_container.ipynb +++ b/examples/1-singlecell/singlecell_H5_morph_H5_container.ipynb @@ -80,12 +80,10 @@ "metadata": {}, "outputs": [], "source": [ - "# First delete previous compiled mod files\n", - "! rm -r arm64 # for mac-based systems\n", - "! rm -r x86_64 # for linux-based systems\n", - "# Compile mod files\n", - "data_folder=\"../../tests/examples/container_nbS1-O1__202247__cADpyr__L5_TPC_A\"\n", - "!nrnivmodl $data_folder/mod\n" + "# Compile mechanisms - examples/mechanisms contains all required mod files\n", + "!nrnivmodl ../mechanisms\n", + "# Path to cell model data folder\n", + "data_folder = \"../../tests/examples/container_nbS1-O1__202247__cADpyr__L5_TPC_A\"" ] }, { diff --git a/examples/2-sonata-network/extracellular-efield-stimulus.ipynb b/examples/2-sonata-network/extracellular-efield-stimulus.ipynb new file mode 100644 index 00000000..8da9ab61 --- /dev/null +++ b/examples/2-sonata-network/extracellular-efield-stimulus.ipynb @@ -0,0 +1,361 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Extracellular E-Field Stimulus\n", + "\n", + "This notebook demonstrates how to apply a spatially uniform extracellular electric field stimulus to neurons in a SONATA network.\n", + "\n", + "The `spatially_uniform_e_field` stimulus allows you to apply electric field components (Ex, Ey, Ez) that can be constant (DC) or oscillating (AC) with configurable frequency and phase. This is useful for modeling transcranial electrical stimulation (TES), deep brain stimulation (DBS), or other extracellular field effects." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Compiling mechanisms\n", + "\n", + "First, compile the NEURON mechanisms:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!nrnivmodl ../mechanisms" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Import libraries" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "from pathlib import Path\n", + "from matplotlib import pyplot as plt\n", + "import seaborn as sns\n", + "sns.set_style(\"white\")\n", + "\n", + "from bluecellulab import CircuitSimulation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create simulation configuration with e-field stimulus\n", + "\n", + "We'll create a SONATA simulation config that applies a spatially uniform e-field to a target population. The field has:\n", + "- DC component: Ex=100 V/m, Ey=-50 V/m, Ez=50 V/m\n", + "- AC component: Ex=50 V/m at 10 Hz\n", + "- Ramp up/down for smooth transitions" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "E-field stimulus configuration:\n", + "{\n", + " \"input_type\": \"extracellular_stimulation\",\n", + " \"module\": \"spatially_uniform_e_field\",\n", + " \"delay\": 50.0,\n", + " \"duration\": 200.0,\n", + " \"node_set\": \"Mosaic_A\",\n", + " \"fields\": [\n", + " {\n", + " \"Ex\": 100,\n", + " \"Ey\": -50,\n", + " \"Ez\": 50,\n", + " \"frequency\": 0\n", + " },\n", + " {\n", + " \"Ex\": 50,\n", + " \"Ey\": 0,\n", + " \"Ez\": 0,\n", + " \"frequency\": 10\n", + " }\n", + " ],\n", + " \"ramp_up_time\": 10.0,\n", + " \"ramp_down_time\": 10.0\n", + "}\n" + ] + } + ], + "source": [ + "# Load the base simulation config\n", + "base_config_path = Path(\"./sim_quick_scx_sonata_multicircuit/simulation_config_noinput.json\")\n", + "with open(base_config_path, 'r') as f:\n", + " sim_config = json.load(f)\n", + "\n", + "# Extend simulation time to cover delay + duration + ramp\n", + "sim_config[\"run\"][\"tstop\"] = 300.0\n", + "\n", + "# Add extracellular e-field stimulus\n", + "sim_config[\"inputs\"] = {\n", + " \"extracellular_efield\": {\n", + " \"input_type\": \"extracellular_stimulation\",\n", + " \"module\": \"spatially_uniform_e_field\",\n", + " \"delay\": 50.0,\n", + " \"duration\": 200.0,\n", + " \"node_set\": \"Mosaic_A\",\n", + " \"fields\": [\n", + " {\"Ex\": 100, \"Ey\": -50, \"Ez\": 50, \"frequency\": 0},\n", + " {\"Ex\": 50, \"Ey\": 0, \"Ez\": 0, \"frequency\": 10}\n", + " ],\n", + " \"ramp_up_time\": 10.0,\n", + " \"ramp_down_time\": 10.0\n", + " }\n", + "}\n", + "\n", + "# Save modified config\n", + "modified_config_path = Path(\"./sim_quick_scx_sonata_multicircuit/simulation_config_efield.json\")\n", + "with open(modified_config_path, 'w') as f:\n", + " json.dump(sim_config, f, indent=2)\n", + "\n", + "print(\"E-field stimulus configuration:\")\n", + "print(json.dumps(sim_config[\"inputs\"][\"extracellular_efield\"], indent=2))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Initialize simulation with e-field stimulus" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Simulating 3 cells: [CellId(population_name='NodeA', id=0), CellId(population_name='NodeA', id=1), CellId(population_name='NodeA', id=2)]\n" + ] + } + ], + "source": [ + "# Create simulation\n", + "sim = CircuitSimulation(modified_config_path)\n", + "\n", + "# Select a few cells from the target population and sort them to have [id=0, id=1, id=2]\n", + "target_cells = sim.circuit_access.get_target_cell_ids(\"Mosaic_A\")\n", + "cells_to_simulate = sorted(target_cells, key=lambda c: c.id)[:3]\n", + "\n", + "print(f\"Simulating {len(cells_to_simulate)} cells: {cells_to_simulate}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Instantiate cells with e-field stimulus\n", + "\n", + "Use the `add_extracellular_stimuli=True` flag to enable e-field application:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Section cNAC_L23BTC_bluecellulab_eaa2264807614006a786fd24d3ed3ac1[0].axon[0] has no 3D points, cannot compute segment coordinates\n", + "Section cNAC_L23BTC_bluecellulab_eaa2264807614006a786fd24d3ed3ac1[0].axon[1] has no 3D points, cannot compute segment coordinates\n", + "Section cNAC_L23BTC_bluecellulab_eaa2264807614006a786fd24d3ed3ac1[0].myelin[0] has no 3D points, cannot compute segment coordinates\n", + "Section cNAC_L23BTC_bluecellulab_eaa2264807614006a786fd24d3ed3ac1[0].axon[0] has no 3D points, cannot compute segment coordinates\n", + "Section cNAC_L23BTC_bluecellulab_eaa2264807614006a786fd24d3ed3ac1[0].axon[1] has no 3D points, cannot compute segment coordinates\n", + "Section cNAC_L23BTC_bluecellulab_eaa2264807614006a786fd24d3ed3ac1[0].myelin[0] has no 3D points, cannot compute segment coordinates\n", + "Section cNAC_L23BTC_bluecellulab_68f5db97c24c45f28c8fe405c2d7ce92[0].axon[0] has no 3D points, cannot compute segment coordinates\n", + "Section cNAC_L23BTC_bluecellulab_68f5db97c24c45f28c8fe405c2d7ce92[0].axon[1] has no 3D points, cannot compute segment coordinates\n", + "Section cNAC_L23BTC_bluecellulab_68f5db97c24c45f28c8fe405c2d7ce92[0].myelin[0] has no 3D points, cannot compute segment coordinates\n", + "Section cNAC_L23BTC_bluecellulab_68f5db97c24c45f28c8fe405c2d7ce92[0].axon[0] has no 3D points, cannot compute segment coordinates\n", + "Section cNAC_L23BTC_bluecellulab_68f5db97c24c45f28c8fe405c2d7ce92[0].axon[1] has no 3D points, cannot compute segment coordinates\n", + "Section cNAC_L23BTC_bluecellulab_68f5db97c24c45f28c8fe405c2d7ce92[0].myelin[0] has no 3D points, cannot compute segment coordinates\n", + "Section cADpyr_L2TPC_bluecellulab_b39caeaf01114e0296ac87544085a895[0].axon[0] has no 3D points, cannot compute segment coordinates\n", + "Section cADpyr_L2TPC_bluecellulab_b39caeaf01114e0296ac87544085a895[0].axon[1] has no 3D points, cannot compute segment coordinates\n", + "Section cADpyr_L2TPC_bluecellulab_b39caeaf01114e0296ac87544085a895[0].myelin[0] has no 3D points, cannot compute segment coordinates\n", + "Section cADpyr_L2TPC_bluecellulab_b39caeaf01114e0296ac87544085a895[0].axon[0] has no 3D points, cannot compute segment coordinates\n", + "Section cADpyr_L2TPC_bluecellulab_b39caeaf01114e0296ac87544085a895[0].axon[1] has no 3D points, cannot compute segment coordinates\n", + "Section cADpyr_L2TPC_bluecellulab_b39caeaf01114e0296ac87544085a895[0].myelin[0] has no 3D points, cannot compute segment coordinates\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Instantiated 3 cells\n", + "E-field sources created: 3\n" + ] + } + ], + "source": [ + "sim.instantiate_gids(\n", + " cells=cells_to_simulate,\n", + " add_extracellular_stimuli=True # Enable e-field stimuli\n", + ")\n", + "\n", + "print(f\"Instantiated {len(sim.cells)} cells\")\n", + "print(f\"E-field sources created: {len(sim._efield_sources)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add voltage recordings" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# Record soma voltage for all cells\n", + "for cell_id in sim.cells:\n", + " sim.cells[cell_id].add_voltage_recording(sim.cells[cell_id].soma)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Run simulation" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Simulation completed!\n" + ] + } + ], + "source": [ + "sim.run()\n", + "print(\"Simulation completed!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Plot results\n", + "\n", + "Visualize the voltage traces showing the effect of the e-field stimulus:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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ySdNoXP3TCoN5mjtphEjRog/0GEVQ4YMkUceecS0V6ONWtT80ksvFavVKhdeeKHMmDFDxo8fL9ddd50sXrxYfvjhBzN8DyIRNovUrx4lW/alE5QCAAAAAADlzuuaUnv37pXExMRj1kdGRkpqaqr4E60hNWnSJLnxxhtNUKp///5y1113FQStNONLa0ppgEpnD9RZ+rS2FPI1TYqRzfvSpEfzGuwSAAAAAMCJOXK0aFfl7akQq4glzOOH9+7dW3bs2PHv00NCJC4uTjp37myScOrUqSP+5v777zeXTzzxhEhVD0qdeeaZ8sADD5gC4pohpcEeHdL36KOPyvnnny/+JCwszBQ116U4zZo1kzfeeKPS2xUomtSINkEpAAAAAAA8CkgdWGpmWKw0OiFRYhevAlMa0+jXr5+5rrPHbdq0SR566CG57777CpUvgh8GpR5++GHzYQ0bNqxgmkKtyXTxxRebQBWCR9OaMfLZqp2+bgYAAAAAIBBohpQGpELDRCzhFf9+juz89zOZWZ4HpXS2uKSkpILbtWrVkttvv13uueceOXLkiLkflePfqec8pMPepkyZIr/88oupv6TLzz//bDKldPp2BNfwPa0pBQAAAACAxzQgZYmohCW8XEdaqdDQ/DCJZk9p/elOnTqZSd6uvPJKMzma0niIDgN8//33Tb3tU089VV588UX59ddfpW/fvuY59957b0Eijyb16ORwV1xxhZl0zf21ivPbb7/JwIEDpX379nLHHXdIZmZmofu/+eabgvs142vBggVm/auvviqDBg0qeNwnn3wiLVu2lG3btpnb6enp0q5dO/nnn39Mm3TyN91GfZ3zzjvP1Nn2+6CUq66U7vDnn39ennvuObMhf//9d/m3Dj7VNCladqVkSVp2JY4HBgAAAACgEm3dulVeeOEFOeOMMyQ6OtoEk26++WYzodvHH38sb7/9tjgcDlOz2j0u8vXXX8vrr79uHjt16lSZMGGCqfuk17/44gtZuHBhweM1fqKBnw8//NBkZmnt65ycnGPacvDgQbnllluke/fuMnfuXFN26Msvvyy4f8mSJTJy5EgZMGCAaZtO5vbf//5X/vjjD+nRo4esX7/eZHspDZJpzazly5cX3NaaWQ0bNjS3Z86cKRdccIF89tlnpjzTgw8+WBBI89ug1LJly8yO1MhgvXr1zKIbpjtEo3kI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jmsm1ptvvnncx65cubLEwuSataUnDWPGjCnIvtK6UXfeeacJQIWGhhZkerVu3VqaNWsmpaHZWP42dLE00UvdDk86fFycU2IjbLI7M1Q61Dh+VhxQFtYcq4SHh5vvV6Q10ic7M8dhF1tohGlDhDVwv+eomOOm9otA/qGA4EPfhD+iX6JYdrtIRoSILUbE4pvfWE67RUIcaRIdFy8htmiftAEoj3N0f2WxWComKKWZTpGRx54gagZSSRlD48ePlyZNmnj0+hpA0mXz5s0nfGzXrl1NhlVJwS3l/r6NGzc2mRc6bLBGjRpmnQ4lPPvss6W0tJMEckdx3wZPtkMf0zY5TtbuOiIdG1SrlPahairol+K775h5X/Pegf89h++Om0Blom/CH9EvUUynKLz4gttvPP49h78JCYLfmp62PT9dyAu9e/c2NZrcU7F0CJwO3evVq1exz/nmm29KHGZXEi2iXhZao8pms8n69esL1mmgKzo62gwbdEUgV69eLSeffHKZ3quqaVs3XtbsTPF1MwAAAAAAQADzOig1duxYM/RNs5m0uLnWftIC4nFxcYVqNbl799135ayzzjLD+PS61mGqaDqsYsiQIWZY4e+//y4rVqyQyZMny6WXXmpmDFQ7duwwmV+lHbpXVbWtGydrdqb6uhkAAAAAACCAeT18LzY2Vp599lmTHaWZR3a73QyLO15mk9ZtWrt2rXz55Zcya9YsEyjq0aOHXHTRRWbonNaNqQhaQ0rrWN14440mK6p///5y1113Fdx/4MABc3miGQNxbFBq/e5UceQ5xRIauOmEAAAAAADAd0o11YHOlqd1mXQonNLruqhTTz21xOF0umih8T/++MMEqLQYuRYi18DUhRdeaAJVnhbD8kRYWJgpal7SzHodOnQosSYVSta4Royp87NlX5o0rxXLrgIAAAAAABUflHr99dflySefNBlSxRWyWrdu3Qlfo127dma5++67TU2nr7/+2rzmqFGj5KeffvK2Sahkmh3Vqk6srN2VSlAKAAAAAABUTlDqueeek1tuuUWuu+66Mg+70+wqLUSuy65du6RVq1Zlej1UnjZ18utKDeiYzG4HAAAAAAAVH5TSIud9+/YtdUBKZ+FbsGCBWZYvXy7NmzeXfv36yUMPPSR169Yt1WvCNzPwfb56J7seAAAAAABUTlBKs6S0eLjWgkpO9ixL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92vGkg67K0tQSmt9DBkyxLzXk08+aYJfGky79NJLxWq1mkudAfDVV18176+z7y1evNjMAojyGcL3w5/7CEoBAAAAAIBihYqXdPibzmJXWlu3bj1m2Jxq3ry5yVRSjRo1kgMHDkhZaQ2pnj17yo033miWM844Q+666y5zn2ZpPfvssyYI1b9/f/nkk0/khRdeMO1A2fVqmSTfb9zHrgQAAAAAAOUTlLrooovMzHmuivDecgWDXFMcKr0+Y8YMad++vbn93XffScOGDaWswsLCTFFzrS/166+/mln3dJ2LZkh9/PHHsmLFClMDq3v37mV+T+Q7vVkN2bwvXXYezmSXAAAAAACAsg/f01pP77//vnz22Wem/lLRGfZ09rrj0eF0N998s8la0owoDW5p4fQ6derIM888Y4bQTZgwQZ5++mlvmwY/Eh9pk5MbJMh3f+6TK7o08HVzAAAAAABAoAelNJCkQaXSql+/vhkqt2TJEvnzzz/FYrGYIXPdunUzhdK1CLlmSlWvXr3U7wH/oEXOv9tAUAoAAAAAAJRDUOq2224rlDXlcDjMTHze0ECUzojnmhVP5eTkyLp166RDhw7eNgl+qleLmvL8d1sk15EnNovXI0UBAAAAAEAQ8zoopV577TWZNWuW7N+/39zWrKYrrriiUMCqJMuXL5fx48fLpk2bJC8v75hg1R9//FGaJsEPta0bJ+G2UFmx9bB0aUzmGwAAAAAA+JfX6StakHzmzJly6623yty5c02B8FtuuUXefPNNM3vdiTz66KOSnJxsXiMyMtIUPR8zZowZtjdx4kRvmwM/FhoaIj2bJ8m3G/b6uikAAAAAACDQM6Xeffddeeyxx6R3794F61q3bi21atUy62+88cbjPn/jxo0yadIkadq0qbRt29YUSh86dKgkJibKiy++KP369SvdlsAv9WqZZIbw3du3la+bAgAAAAAAAjlTSutIabHzoho3biwHDx484fM1O0qH6akmTZrIhg0bzPX27dvLX3/95W1zEADFzv/cc0R2HM70dVMAAAAAAEAgB6U6deokL7/8cqF6UFrsXNdpYOlETjvtNJkyZYrs2bPHvNYXX3whhw8flkWLFklcXJz3WwC/lhAVJqc2qi5frdnt66YAAAAAAIBAHr43atQoM9zup59+MsPv1Jo1a8zseVr8/ERGjx4t99xzjyxYsEAu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uuaaa+psWL4nZureZZddppEjR9qKKNNTyvSo6tKli+6//369++67uv322/WHP/yhwfcNAMkiEAxGCqUst8tM30vggAAAAAAg1UMp04D8+eef10svvaTu3bvXOsLe3Llzd3v7Hj166MUXX9TixYv11VdfyeVy2al7w4cPtyFXQUGBrZRq3759w58NACTh0fcifaVodA4AAAAAexFKmeomU+m0N0wQNWLECLtEeL1erVixQkOGDNmr+waA5Gl0XsP0lapm+h4AAAAAND6UmjRpUlzVlN/vt0fiq69PPvlEN910k1atWqVAIFArrPriiy8aOiQASEqRnlKGx+WQn+l7AAAAAND4RufGE088YXtCHXbYYTriiCN01FFHafbs2fW67a233qpu3brpj3/8ozIzM23T8+nTp9tpe3fddVdjhgMASSdg5+/VnHeZSim7EQAAAADQqEopc5S8efPm6YorrtDQoUNttZOpfjKhVFpami699NLd3v7rr7/WrFmz1LdvXx1wwAG2J9W5556rwsJCPfLIIxo9ejT/MwBa3fQ9j2l07k/ggAAAAAAg1UOpZ599VrfddptGjRoV3bb//vurc+fOdvueQilTHWWm6Rl9+vTRypUrdcwxx2jw4MH65ptvGvMcACApQylnTKdzt8shH5VSAAAAAND46Xumj5Rpdr6z3r17a9u2bXu8vZnud88992jjxo220urll19WUVGR3njjDeXl5TV0OACQlALBYNzR98z0PSqlAAAAAGAvQikTJD322GNxTcpNs3OzzVQ77cm0adNUXFysRYsWacyYMcrJybFB1cyZM3X55Zc3dDgAkJRsS6nYRudOKqUAAAAAYK+m702ZMsX2gHrvvfdsTyhj2bJl8nq9evTRR/d4ezPNb+7cudHzTz75pD0Sn6mSMv2lAKBV2KmnlJueUgAAAACwd5VSpkH5K6+8ogsvvNA2J+/atavtI/XPf/5TAwcO3OPtTf+p2Gl+DodD/fv3l8/n03HHHdfQ4QBAUjLto5wx77BuWymVyBEBAAAAQIpXShnt2rXThAkT6n39F154QX/961/tejAYtNP0dq6K2rRpkzp27NiY4QBAcoZSjp0qpWh0DgAAAAANC6VMBdPzzz9vwyhz1D1T3bQrr7/+eq1tJ5xwgr7//nu7/sEHH+jggw9WdnZ23HWysrLs9ZrS8uXLdcYZ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yylVUykFAAAAAA0Lpbp166amZKqqPvzwQ9ubykznM03Sjz/+eF199dVN+jgAkDxH33PIT08pAAAAAGh8o/OBAwfWmroXa1fT93w+n9577z07Zc9UXpkg6phjjrFN0I8++milpaU1dCgAkNw9pXaavkelFAAAAADsRSg1d+7cuPN+v1/fffedHn/8cf32t7+t8zamAuqtt96yQdTw4cN1zTXX6IQTTlB2dnZDHx4AUuboe7FcTgdH3wMAAACAvQmlhg0bVmubCZp69eple0KddNJJtS43R+UzgZW5rH379g19SABIOb6AmbIXXynlDyRyRAAAAACQ4qHUrpiwac2aNXVeNn/+/KZ6GABICT6/lJ4e31OK6XsAAAAAsBeh1AsvvFBrW1lZmZ5//nkdfPDBDb07AGiVfIGgDaIi3C4TVMVP6QMAAACAtqzBodT9998fd940Pfd4PDrooIN22VMKANpipZTbFV8pZab0AQAAAAAaGUq98cYbDb0JALQ5flsppfhKqQCVUgAAAACwVz2lVq9erQULFtgeUqZSauDAgRo/fry6devWmLsDgFan2jQ6d9WcNwGVqZ4CAAAAAITE/B2//pVSY8eO1eeff67evXurR48eev/99zVmzBh9+OGHDb07AGjFlVKxPaXM9D0qpQAAAACg0ZVSs2bN0hVXXKFLLrkkbvtDDz2k2267rc5G6ADQ1pgj7cVO3/NQKQUAAAAAe1cp9cMPP+i4446rtf2kk07SN99809C7A4BWyUzVc8U0OnfR6BwAAAAA9i6UOvnkk/Xoo4+quro6bvtzzz2n0aNHN/TuAKBVqvYHlebaqdG5n+l7AAAAANDo6XtVVVVatGiR3n77bR144IHyeDxauXKl1q5dqyFDhmjChAnR686dO7ehdw8ArUK5N6jMtJpKKY/tKZXQIQEAAABAaodSffr00WWXXRa3bcCAAU05JgBoFaFUlid2+p5odA4AAAAAexNKTZo0KbpeWloqv9+v/Px8JaNgMKgHHnhAzzzzjHw+n37yk59o+vTpSk9Pt5eb6q7rr79en376qbp27aqpU6dqxIgRiR42gFagojqorLhKqVCfKQAAAABAI3tKGU888YRGjhypww47TEcccYSOOuoozZ49W8nmkUce0VNPPaV77rnH9sFasmRJdJwmsLr88svVoUMHLViwQGPHjrWB2/r1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XRt0X4cffrhWrly52+uYo+7ddddduuaaa3TBBRdEt3fu3FlbtmyJu645byqsGsLsJKm8o8Q+BzNNyuEMH33PH0j55wW1iv3S/kvQvmgf1z526r/O0Uz7J/sFkgz7JpIR+yXq2Cnil0SI+YzH73MkG0cr+KxZ37E3OJQyVVKzZs2yvaC6detWr9uYI91FgijTT8rcbsyYMTaI6tevn5rLwoULbSBlKqQuvPDCuMsOPvhgrVu3Ths2bNA+++xjt3388cd2e1tlAlmnw2GPvlftS+35qwAAAAAAILk1OJTq1auXfv/730enxu2srp5SJ554oq1AMhVWJswaPHiwmltRUZFuvvlmnXHGGTYAM43NI0yj8x49etipfFdffbWmTZtme1u99NJLtvdUWxUImr8VyFZKlVYyrxoAAAAAACRRKDVjxgwb5piwp779lx5//HE71c5M/Wsp//nPf2xzMFMtZZZYr7/+urp3726rqEwgdfbZZ9vQ7Pbbb2+RwCx5BW0Vq6mU8voCiR4MAAAAAABoxdyNaU5+5ZVX2kqj+ho+fHh03QRFzz33nNasWVPnke5mzpyppmCqo8yyO4WFhfrjH//YJI/XmqbvpbkcqvYTSgEAAAAAgObT4NKlcePG6YUXXmj0A5pAyxzBr6SkpNH3geYRCHf5jzQ6BwAAAAAASJpKqR07dugvf/mLnn/+eTsFbufD/M2dO3e3t3///ff12GOPaejQoQ0fLZqViaSYvgcAAAAAAJIylDLT9n71q181+gH79OmjysrKRt8eLTB9z+1k+h4AAAAAAEiuUGrSpEl79YB33HGHvY9TTz1VXbt2rdX8/PTTT9+r+8feTd+LVEpV+0NT+QAAAAAAAJIilKqoqLDT91atWiW/3x/dbpqWL1++XK+88spub//ss8/q22+/1dNPP6309PS4yxwOB6FUIgUlh0yjc46+BwAAAAAAkiyUmj59ut577z0deeSRevXVV3XyySfbkOnzzz+vVxWV6UV17733avTo0Y0dM5qJqY1yOmh0DgAAAAAAkjCUevvtt/WHP/zBhlJff/21LrzwQh144IF2Wp45vyft2rVTv379GjtetNj0PY6+BwAAAAAAmk98Q6d6qKqqUq9evex6//799cUXX9j1c845Rx999NEeb3/DDTfo5ptv1uLFi7V27VqtX78+bkFiG52bCXwel0Ne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ceNkz549Qd0nAADwlghHj1QAAAAQFFeuXJHr16/b6y1btsjSpUtl/fr1/vVRUVGSl5cn1apVk9KlS4f8r65JsC+//FLefffdoO737Nmz8vzzz8umTZukbNmyQd03AADwBiqlAAAAgqhSpUpSs2ZNe+hrTTz53utDEzj67EZCSq89fvjhhzJgwICg7zsmJkZq165tiTcAAICiICkFAADgIp22d/f0PX29detW6d69u8TFxcnLL78sP/30kwwePNjeDxw4UC5evOj/+e3bt0tycrKtS0lJkQMHDhT6WV988YVcu3bNtlVaMZWUlGSVW+3bt5fWrVvLokWL5ODBg9KtWzeJj4+XCRMmyJ07d2z7kydPSv/+/e3nExMTZe7cufn2r/vKzMwM0V8KAACUdCSlAAAAwiwjI0NmzpwpCxYskG3btlllkz404ZObm2uJI1+SaOLEiTZt7pNPPpFevXrJiBEjbCpdIFlZWfL0009LRESEf9mlS5dkx44dsnLlShk9erTMmTNHpk+fbp+vr7XyaefOnbatJqiaNm0qn376qUybNk0WL14su3bt8u9LE1tHjx6Vy5cvh/xvBAAASh6SUgAAAGE2dOhQq0Zq27atJYHatWtnlVP6ukuXLnLmzBnbbsmSJfLMM89Iz549bfqcVlN16NBBVq9eHXC/3333nTRo0CDfslu3blliq379+vLss89aVZQ+t2zZUjp16mSfefr0adv2559/lipVqkh0dLR9zrJly6RZs2b+fdWpU0ciIyPlxIkTIf37AACAkiky3F8AAADA6zS541O+fHlLAt39/ubNm/Za76CnU/3WrFmTL8mUkJAQcL+///67VK1atdDP032rwj5v1KhRVj2ln9exY0fp3bu39cPyKVWqlDVu/+233x7o9wcAAN5EUgoAACDMCjY912RPILdv37bpen369Mm33JdcKkin7enPFKTVTf/l80aOHGkVWzrd7/PPP5chQ4bIlClTpF+/fv5ttNKqsJ8HAAC4H44gAAAAHhL16tWzBuk6dc/30Cqm3bt3B9y+evXq8ueffxbps27cuCFTp061uwWmpaVZDyqdOvjZZ5/lS0jl5eVJjRo1ivw7AQAA7yIpBQAA8BD1ntJG5CtWrJCcnBxZvny5PerWrRtwe+3/lJ2dXaTPKleunBw+fNgqo7TH1PHjx+Wrr77K11NKpxOqJk2aFPE3AgAAXkZSCgAA4CGhzchnzZolq1atkuTkZFm7dq2kp6dL69atA26fmJhoiSXHcYr0ee+9955cu3ZNUlJSZPjw4fLUU0/JmDFj/OsPHTok8fHxUrFixSL/TgAAwLsinKIepQAAAKBY035SXbt2lRkzZhSauHoQgwYNsoSVNkAHAAD4v6iUAgAAKMEN1LVZeWZmZtD3rVP3Lly4YBVbAAAARUFSCgAAoATTSqbz58/7+z8Fy7x58+Stt96SMmXKBHW/AADAO5i+BwAAAAAAANdRKQUAAAAAAADXkZQCAAAAAACA60hKAQAAAAAAwHUkpQAAAAAAAOA6klIAAAAAAABwHUkpAAAAAAAAuI6kFAAAAAAAAFxHUgoAAAAAAADitn8AfOVbf/KmpmYAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axes = plt.subplots(len(cells_to_simulate), 1, figsize=(12, 3*len(cells_to_simulate)), sharex=True)\n", + "if len(cells_to_simulate) == 1:\n", + " axes = [axes]\n", + "\n", + "for i, cell_id in enumerate(cells_to_simulate):\n", + " cell = sim.cells[cell_id]\n", + " time = cell.get_time()\n", + " voltage = cell.get_soma_voltage()\n", + "\n", + " axes[i].plot(time, voltage, linewidth=0.8)\n", + " axes[i].set_ylabel(f\"Cell {cell_id}\\nVoltage (mV)\")\n", + " axes[i].axvspan(50, 60, alpha=0.2, color='green', label='Ramp up')\n", + " axes[i].axvspan(60, 240, alpha=0.2, color='yellow', label='E-field active')\n", + " axes[i].axvspan(240, 250, alpha=0.2, color='orange', label='Ramp down')\n", + " axes[i].grid(True, alpha=0.3)\n", + " if i == 0:\n", + " axes[i].legend(loc='upper right')\n", + "\n", + "axes[-1].set_xlabel(\"Time (ms)\")\n", + "plt.suptitle(\"Voltage Response to Extracellular E-Field Stimulus\", fontsize=14, y=1.0)\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "This notebook demonstrated:\n", + "1. **Creating** a SONATA config with `spatially_uniform_e_field` stimulus\n", + "2. **Configuring** multiple field components (DC + AC)\n", + "3. **Applying** the stimulus with ramp up/down\n", + "4. **Visualizing** the neuronal response to the e-field\n", + "\n", + "### Key parameters:\n", + "- `fields`: List of field components with Ex, Ey, Ez (V/m), frequency (Hz), phase (rad)\n", + "- `ramp_up_time`, `ramp_down_time`: Smooth transitions (ms)\n", + "- `delay`, `duration`: Timing control (ms)\n", + "- `node_set`: Target population\n", + "\n", + "The e-field is applied to all segments of each neuron based on their 3D spatial positions relative to the soma, providing realistic modeling of extracellular stimulation effects." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.13" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/examples/2-sonata-network/sim_quick_scx_sonata_multicircuit/output_sonata_compartment_set/compartment_set.h5 b/examples/2-sonata-network/sim_quick_scx_sonata_multicircuit/output_sonata_compartment_set/compartment_set.h5 index a6cff9c3..12278f4f 100644 Binary files a/examples/2-sonata-network/sim_quick_scx_sonata_multicircuit/output_sonata_compartment_set/compartment_set.h5 and b/examples/2-sonata-network/sim_quick_scx_sonata_multicircuit/output_sonata_compartment_set/compartment_set.h5 differ diff --git a/examples/2-sonata-network/sim_quick_scx_sonata_multicircuit/output_sonata_compartment_set/compartment_set_ik.h5 b/examples/2-sonata-network/sim_quick_scx_sonata_multicircuit/output_sonata_compartment_set/compartment_set_ik.h5 new file mode 100644 index 00000000..27ba76cf Binary files /dev/null and b/examples/2-sonata-network/sim_quick_scx_sonata_multicircuit/output_sonata_compartment_set/compartment_set_ik.h5 differ diff --git a/examples/2-sonata-network/sim_quick_scx_sonata_multicircuit/output_sonata_compartment_set/node_set.h5 b/examples/2-sonata-network/sim_quick_scx_sonata_multicircuit/output_sonata_compartment_set/node_set.h5 index 80331282..24277325 100644 Binary files a/examples/2-sonata-network/sim_quick_scx_sonata_multicircuit/output_sonata_compartment_set/node_set.h5 and b/examples/2-sonata-network/sim_quick_scx_sonata_multicircuit/output_sonata_compartment_set/node_set.h5 differ diff --git a/examples/2-sonata-network/sim_quick_scx_sonata_multicircuit/output_sonata_compartment_set/spikes.h5 b/examples/2-sonata-network/sim_quick_scx_sonata_multicircuit/output_sonata_compartment_set/spikes.h5 index e9ff9c7c..6893b371 100644 Binary files a/examples/2-sonata-network/sim_quick_scx_sonata_multicircuit/output_sonata_compartment_set/spikes.h5 and b/examples/2-sonata-network/sim_quick_scx_sonata_multicircuit/output_sonata_compartment_set/spikes.h5 differ diff --git a/examples/2-sonata-network/sim_quick_scx_sonata_multicircuit/simulation_config_efield.json b/examples/2-sonata-network/sim_quick_scx_sonata_multicircuit/simulation_config_efield.json new file mode 100644 index 00000000..53c6a55e --- /dev/null +++ b/examples/2-sonata-network/sim_quick_scx_sonata_multicircuit/simulation_config_efield.json @@ -0,0 +1,58 @@ +{ + "manifest": { + "$OUTPUT_DIR": "." + }, + "run": { + "tstop": 300.0, + "dt": 0.025, + "random_seed": 1 + }, + "conditions": { + "v_init": -65 + }, + "target_simulator": "NEURON", + "network": "circuit_sonata.json", + "node_set": "Mosaic_A", + "output": { + "output_dir": "$OUTPUT_DIR/output_sonata_noinput", + "spikes_file": "out.h5", + "spikes_sort_order": "by_time" + }, + "inputs": { + "extracellular_efield": { + "input_type": "extracellular_stimulation", + "module": "spatially_uniform_e_field", + "delay": 50.0, + "duration": 200.0, + "node_set": "Mosaic_A", + "fields": [ + { + "Ex": 100, + "Ey": -50, + "Ez": 50, + "frequency": 0 + }, + { + "Ex": 50, + "Ey": 0, + "Ez": 0, + "frequency": 10 + } + ], + "ramp_up_time": 10.0, + "ramp_down_time": 10.0 + } + }, + "reports": { + "soma": { + "cells": "Mosaic_A", + "variable_name": "v", + "type": "compartment", + "dt": 1.0, + "start_time": 0.0, + "end_time": 20.0, + "sections": "soma", + "compartments": "center" + } + } +} \ No newline at end of file diff --git a/tests/test_cell/test_segment_coordinates.py b/tests/test_cell/test_segment_coordinates.py new file mode 100644 index 00000000..7b0497c5 --- /dev/null +++ b/tests/test_cell/test_segment_coordinates.py @@ -0,0 +1,150 @@ +"""Tests for cell coordinate helpers.""" +from unittest.mock import MagicMock, patch + +import numpy as np +import pytest +import neuron +from bluecellulab.cell.core import Cell +from bluecellulab.circuit.node_id import CellId + + +def _patched_public_hoc_cell(sections_cache): + """Return a mock public_hoc_cell that exposes .all.""" + def _mock(cell): + m = MagicMock() + m.all = list(sections_cache.values()) + return m + return _mock + + +@pytest.fixture +def simple_cell(): + soma = neuron.h.Section(name="soma") + soma.nseg = 1 + soma.L = 10.0 + soma.diam = 10.0 + neuron.h.pt3dadd(0, 0, 0, 10, sec=soma) + neuron.h.pt3dadd(10, 0, 0, 10, sec=soma) + dend = neuron.h.Section(name="dend[0]") + dend.nseg = 3 + dend.L = 100.0 + dend.diam = 2.0 + dend.connect(soma(1), 0) + neuron.h.pt3dadd(10, 0, 0, 2, sec=dend) + neuron.h.pt3dadd(110, 0, 0, 2, sec=dend) + cell = Cell.__new__(Cell) + cell.cell_id = CellId("", 0) + cell.cell = neuron.h.Cell() + cell.soma = soma + cell._sections_cache = {"soma": soma, "dend[0]": dend} + cell._local_to_global_matrix = None + return cell + + +def test_local_coords(simple_cell): + with patch("bluecellulab.cell.core.public_hoc_cell", _patched_public_hoc_cell(simple_cell._sections_cache)): + coords = simple_cell.compute_segment_local_coordinates() + assert "soma" in coords + assert "dend[0]" in coords + assert coords["soma"].shape == (2, 3) + assert coords["dend[0]"].shape == (4, 3) + + +def test_global_coords_identity(): + soma = neuron.h.Section(name="soma") + soma.nseg = 1 + soma.L = 10.0 + neuron.h.pt3dadd(5, 5, 5, 10, sec=soma) + neuron.h.pt3dadd(15, 5, 5, 10, sec=soma) + cell = Cell.__new__(Cell) + cell.cell_id = CellId("", 1) + cell.cell = neuron.h.Cell() + cell.soma = soma + cell._sections_cache = {"soma": soma} + cell._local_to_global_matrix = None + with patch("bluecellulab.cell.core.public_hoc_cell", _patched_public_hoc_cell(cell._sections_cache)): + local_c = cell.compute_segment_local_coordinates() + global_c = cell.compute_segment_global_coordinates() + np.testing.assert_array_equal(local_c["soma"], global_c["soma"]) + + +def test_l2g_identity(): + cell = Cell.__new__(Cell) + cell.cell_id = CellId("", 2) + cell.cell = neuron.h.Cell() + cell.soma = neuron.h.Section(name="soma") + cell.set_local_to_global_matrix(np.array([10.0, 20.0, 30.0]), np.array([1.0, 0.0, 0.0, 0.0])) + pts = np.array([[1.0, 0.0, 0.0]]) + np.testing.assert_allclose(cell.local_to_global_coord_mapping(pts), [[11.0, 20.0, 30.0]]) + + +def test_l2g_90z(): + cell = Cell.__new__(Cell) + cell.cell_id = CellId("", 3) + cell.cell = neuron.h.Cell() + cell.soma = neuron.h.Section(name="soma") + angle = np.pi / 4 + cell.set_local_to_global_matrix(np.zeros(3), np.array([np.cos(angle), 0.0, 0.0, np.sin(angle)])) + pts = np.array([[1.0, 0.0, 0.0]]) + np.testing.assert_allclose(cell.local_to_global_coord_mapping(pts)[0], [0.0, 1.0, 0.0], atol=1e-6) + + +def test_l2g_translation_only(): + cell = Cell.__new__(Cell) + cell.cell_id = CellId("", 4) + cell.cell = neuron.h.Cell() + cell.soma = neuron.h.Section(name="soma") + cell.set_local_to_global_matrix(np.array([5.0, -3.0, 2.0]), None) + pts = np.array([[1.0, 2.0, 3.0]]) + np.testing.assert_allclose(cell.local_to_global_coord_mapping(pts), [[6.0, -1.0, 5.0]]) + + +def test_get_seg_pos_3d(): + sec = neuron.h.Section(name="testsec") + sec.nseg = 2 + sec.L = 100.0 + neuron.h.pt3dadd(0, 0, 0, 2, sec=sec) + neuron.h.pt3dadd(100, 0, 0, 2, sec=sec) + pts = np.array([[0, 0, 0], [50, 0, 0], [100, 0, 0]]) + np.testing.assert_allclose(Cell.get_segment_position(pts, np.zeros(3), sec, 0.0), [0, 0, 0]) + np.testing.assert_allclose(Cell.get_segment_position(pts, np.zeros(3), sec, 0.5), [50, 0, 0]) + np.testing.assert_allclose(Cell.get_segment_position(pts, np.zeros(3), sec, 1.0), [100, 0, 0]) + + +def test_get_seg_pos_axon(): + axon = neuron.h.Section(name="axon[0]") + axon.nseg = 1 + axon.L = 30.0 + soma_pos = np.array([10.0, 20.0, 30.0]) + pos = Cell.get_segment_position(np.array([]), soma_pos, axon, 0.5, func_loc2glob=None) + np.testing.assert_allclose(pos, [10.0, 5.0, 30.0]) + + +def test_get_seg_pos_axon_l2g(): + axon = neuron.h.Section(name="axon[0]") + axon.nseg = 1 + axon.L = 30.0 + soma_pos = np.array([1.0, 0.0, 0.0]) + pos = Cell.get_segment_position(np.array([]), soma_pos, axon, 0.5, func_loc2glob=lambda p: p + 10.0) + np.testing.assert_allclose(pos, [11.0, -5.0, 10.0]) + + +def test_get_seg_pos_value_error(): + sec = neuron.h.Section(name="dend_sec") + sec.nseg = 1 + sec.L = 10.0 + with pytest.raises(ValueError, match="has no 3d points defined"): + Cell.get_segment_position(np.array([]), np.zeros(3), sec, 0.5) + + +def test_no_3d_warning(simple_cell, caplog): + axon = neuron.h.Section(name="axon[0]") + axon.nseg = 1 + axon.L = 30.0 + axon.connect(simple_cell._sections_cache["dend[0]"](1), 0) + simple_cell._sections_cache["axon[0]"] = axon + with patch("bluecellulab.cell.core.public_hoc_cell", _patched_public_hoc_cell(simple_cell._sections_cache)): + with caplog.at_level("WARNING", logger="bluecellulab.cell.core"): + coords = simple_cell.compute_segment_local_coordinates() + assert coords["axon[0]"].size == 0 + assert "has no 3D points" in caplog.text diff --git a/tests/test_stimulus/test_circuit_stimulus_definitions.py b/tests/test_stimulus/test_circuit_stimulus_definitions.py index 8d382cc6..4a836f93 100644 --- a/tests/test_stimulus/test_circuit_stimulus_definitions.py +++ b/tests/test_stimulus/test_circuit_stimulus_definitions.py @@ -14,7 +14,7 @@ # limitations under the License. import pytest -from bluecellulab.stimulus.circuit_stimulus_definitions import Noise, Pattern, Stimulus, SubThreshold +from bluecellulab.stimulus.circuit_stimulus_definitions import Noise, Pattern, Stimulus, SubThreshold, SpatiallyUniformEField def test_pattern_from_sonata_valid(): @@ -30,6 +30,7 @@ def test_pattern_from_sonata_valid(): "relative_shot_noise": Pattern.RELATIVE_SHOT_NOISE, "ornstein_uhlenbeck": Pattern.ORNSTEIN_UHLENBECK, "relative_ornstein_uhlenbeck": Pattern.RELATIVE_ORNSTEIN_UHLENBECK, + "spatially_uniform_e_field": Pattern.SPATIALLY_UNIFORM_E_FIELD, "seclamp": Pattern.SECLAMP, "subthreshold": Pattern.SUBTHRESHOLD, } @@ -44,6 +45,30 @@ def test_pattern_from_sonata_invalid(): Pattern.from_sonata("unknown_pattern") +def test_pattern_from_blueconfig_valid(): + """Test valid mappings from blueconfig strings to Pattern enum values.""" + valid_patterns = { + "Noise": Pattern.NOISE, + "Hyperpolarizing": Pattern.HYPERPOLARIZING, + "Pulse": Pattern.PULSE, + "RelativeLinear": Pattern.RELATIVE_LINEAR, + "SynapseReplay": Pattern.SYNAPSE_REPLAY, + "ShotNoise": Pattern.SHOT_NOISE, + "RelativeShotNoise": Pattern.RELATIVE_SHOT_NOISE, + "OrnsteinUhlenbeck": Pattern.ORNSTEIN_UHLENBECK, + "RelativeOrnsteinUhlenbeck": Pattern.RELATIVE_ORNSTEIN_UHLENBECK, + } + + for blueconfig_pattern, expected_enum in valid_patterns.items(): + assert Pattern.from_blueconfig(blueconfig_pattern) == expected_enum + + +def test_pattern_from_blueconfig_invalid(): + """Test that an invalid blueconfig pattern raises a ValueError.""" + with pytest.raises(ValueError, match="Unknown pattern unknown_pattern"): + Pattern.from_blueconfig("unknown_pattern") + + def test_noise_requires_exactly_one_mean_field(): """Noise dataclass should validate exactly one of mean/mean_percent.""" with pytest.raises(ValueError, match="Noise stimulus must define exactly one of 'mean' or 'mean_percent'."): @@ -76,6 +101,151 @@ def test_from_sonata_noise_requires_one_mean_field(): Stimulus.from_sonata({**base, "mean": 0.01, "mean_percent": 5.0}) +def test_pattern_spatially_uniform_e_field(): + """Test that spatially_uniform_e_field maps correctly.""" + assert Pattern.from_sonata("spatially_uniform_e_field") == Pattern.SPATIALLY_UNIFORM_E_FIELD + + +def test_spatially_uniform_e_field_valid(): + """Test valid SpatiallyUniformEField construction.""" + stim = SpatiallyUniformEField( + target="T", + delay=0.0, + duration=10.0, + fields=[{"Ex": 100, "Ey": -50, "Ez": 75}], + ramp_up_time=2.0, + ramp_down_time=3.0, + node_set="T", + compartment_set=None, + ) + assert stim.fields == [{"Ex": 100, "Ey": -50, "Ez": 75}] + assert stim.ramp_up_time == 2.0 + assert stim.ramp_down_time == 3.0 + + +def test_spatially_uniform_e_field_empty_fields(): + """Test that empty fields list raises validation error.""" + with pytest.raises(ValueError, match="fields list cannot be empty"): + SpatiallyUniformEField( + target="T", + delay=0.0, + duration=10.0, + fields=[], + node_set="T", + compartment_set=None, + ) + + +def test_spatially_uniform_e_field_missing_components(): + """Test that missing Ex/Ey/Ez raises validation error.""" + with pytest.raises(ValueError, match="Field 0 must contain Ex, Ey, and Ez components"): + SpatiallyUniformEField( + target="T", + delay=0.0, + duration=10.0, + fields=[{"Ex": 100, "Ey": -50}], + node_set="T", + compartment_set=None, + ) + + +def test_spatially_uniform_e_field_negative_frequency(): + """Test that negative frequency raises validation error.""" + with pytest.raises(ValueError, match="Field 0 frequency must be non-negative"): + SpatiallyUniformEField( + target="T", + delay=0.0, + duration=10.0, + fields=[{"Ex": 100, "Ey": -50, "Ez": 75, "frequency": -10}], + node_set="T", + compartment_set=None, + ) + + +def test_from_sonata_spatially_uniform_e_field(): + """Test parsing SONATA spatially_uniform_e_field stimulus.""" + entry = { + "module": "spatially_uniform_e_field", + "delay": 0.0, + "duration": 10.0, + "node_set": "TestTarget", + "fields": [ + {"Ex": 50, "Ey": -25, "Ez": 75, "frequency": 100}, + {"Ex": 100, "Ey": -50, "Ez": 50, "frequency": 0}, + ], + "ramp_up_time": 2.0, + "ramp_down_time": 3.0, + } + + stim = Stimulus.from_sonata(entry) + assert isinstance(stim, SpatiallyUniformEField) + assert stim.delay == 0.0 + assert stim.duration == 10.0 + assert stim.node_set == "TestTarget" + assert stim.compartment_set is None + assert len(stim.fields) == 2 + assert stim.ramp_up_time == 2.0 + assert stim.ramp_down_time == 3.0 + + +def test_from_sonata_spatially_uniform_e_field_defaults(): + """Test parsing with default ramp times.""" + entry = { + "module": "spatially_uniform_e_field", + "delay": 0.0, + "duration": 10.0, + "node_set": "TestTarget", + "fields": [{"Ex": 50, "Ey": -25, "Ez": 75}], + } + + stim = Stimulus.from_sonata(entry) + assert isinstance(stim, SpatiallyUniformEField) + assert stim.ramp_up_time == 0.0 + assert stim.ramp_down_time == 0.0 + + +def test_from_sonata_sinusoidal(): + """Test parsing SONATA sinusoidal stimulus.""" + from bluecellulab.stimulus.circuit_stimulus_definitions import Sinusoidal + + entry = { + "module": "sinusoidal", + "delay": 0.0, + "duration": 10.0, + "node_set": "TestTarget", + "amp_start": 0.1, + "frequency": 10.0, + } + + stim = Stimulus.from_sonata(entry) + assert isinstance(stim, Sinusoidal) + assert stim.delay == 0.0 + assert stim.duration == 10.0 + assert stim.amp_start == 0.1 + assert stim.frequency == 10.0 + + +def test_from_sonata_seclamp(): + """Test parsing SONATA seclamp stimulus.""" + from bluecellulab.stimulus.circuit_stimulus_definitions import SEClamp + + entry = { + "module": "seclamp", + "delay": 0.0, + "duration": 10.0, + "node_set": "TestTarget", + "voltage": -70.0, + "series_resistance": 0.02, + } + + stim = Stimulus.from_sonata(entry) + assert isinstance(stim, SEClamp) + assert stim.delay == 0.0 + assert stim.duration == 10.0 + assert stim.voltage == -70.0 + assert stim.series_resistance == 0.02 + + def test_pattern_from_blueconfig_subthreshold(): """Test SubThreshold mapping from BlueConfig.""" assert Pattern.from_blueconfig("SubThreshold") == Pattern.SUBTHRESHOLD diff --git a/tests/test_stimulus/test_efield_integration.py b/tests/test_stimulus/test_efield_integration.py new file mode 100644 index 00000000..7b7cfad4 --- /dev/null +++ b/tests/test_stimulus/test_efield_integration.py @@ -0,0 +1,129 @@ +"""Integration tests for spatially-uniform e-field stimulus (PR #82).""" +from pathlib import Path +from unittest.mock import patch + +import numpy as np + +from bluecellulab import CircuitSimulation +from bluecellulab.stimulus import circuit_stimulus_definitions + +parent_dir = Path(__file__).resolve().parent.parent.parent + + +def _fake_efield(node_set="Mosaic_A", compartment_set=None): + """Return a minimal SpatiallyUniformEField stimulus.""" + target = compartment_set if compartment_set is not None else node_set + return circuit_stimulus_definitions.SpatiallyUniformEField( + target=target, + node_set=None if compartment_set is not None else node_set, + compartment_set=compartment_set, + fields=[{"Ex": 100, "Ey": 0, "Ez": 0, "frequency": 0}], + delay=0, + duration=10, + ramp_up_time=0, + ramp_down_time=0, + ) + + +def _instantiate_with_efield(sim, gids, **patches): + """Instantiate cells with the patched stimulus list and skip the apply step + so that segment_displacements stay populated for inspection.""" + with patch.object( + sim, + "_apply_extracellular_stimuli", + lambda: None, + ): + with patch.object( + sim.circuit_access.config, + "get_all_stimuli_entries", + **patches, + ): + sim.instantiate_gids( + cells=gids, + add_stimuli=True, + add_extracellular_stimuli=True, + ) + + +def test_efield_targets_all_sections(): + """EField stimulus on node_set should target all sections, not just soma.""" + base = parent_dir / "examples" / "2-sonata-network" / "sim_quick_scx_sonata_multicircuit" / "simulation_config_noinput.json" + sim = CircuitSimulation(base) + target = sim.circuit_access.get_target_cell_ids("Mosaic_A") + gids = list(target)[:1] + _instantiate_with_efield(sim, gids, return_value=[_fake_efield()]) + cell = sim.cells[gids[0]] + assert gids[0] in sim._efield_sources + es = sim._efield_sources[gids[0]] + assert es.segment_displacements is not None + assert len(es.segment_displacements) > 1 + + +def test_efield_soma_displacement_is_zero(): + """Soma segments should have zero displacement vector.""" + base = parent_dir / "examples" / "2-sonata-network" / "sim_quick_scx_sonata_multicircuit" / "simulation_config_noinput.json" + sim = CircuitSimulation(base) + target = sim.circuit_access.get_target_cell_ids("Mosaic_A") + gids = list(target)[:1] + _instantiate_with_efield(sim, gids, return_value=[_fake_efield()]) + cell = sim.cells[gids[0]] + es = sim._efield_sources[gids[0]] + soma_seg = cell.soma(0.5) + assert soma_seg in es.segment_displacements + np.testing.assert_allclose( + es.segment_displacements[soma_seg], [0.0, 0.0, 0.0], atol=1e-12 + ) + + +def test_efield_compartment_set_targets(): + """EField stimulus should honour compartment_set targets.""" + base = parent_dir / "examples" / "2-sonata-network" / "sim_quick_scx_sonata_multicircuit" / "simulation_config_noinput.json" + sim = CircuitSimulation(base) + target = sim.circuit_access.get_target_cell_ids("Mosaic_A") + gids = list(target)[:1] + fake_compartment_sets = { + "dend_only": { + "population": gids[0].population_name, + "compartment_set": [[gids[0].id, 1, 0.5]], + } + } + with patch.object( + sim.circuit_access.config, + "get_compartment_sets", + return_value=fake_compartment_sets, + ): + _instantiate_with_efield( + sim, gids, return_value=[_fake_efield(compartment_set="dend_only")] + ) + cell = sim.cells[gids[0]] + es = sim._efield_sources[gids[0]] + assert es.segment_displacements is not None + # Should have at least one segment displacement, and soma(0.5) is not present. + assert len(es.segment_displacements) >= 1 + assert cell.soma(0.5) not in es.segment_displacements + + +def test_get_cell_position_rotation_returns_quaternion(): + """SonataCircuitAccess returns position and quaternion when present.""" + base = parent_dir / "examples" / "2-sonata-network" / "sim_quick_scx_sonata_multicircuit" / "simulation_config_noinput.json" + sim = CircuitSimulation(base) + target = sim.circuit_access.get_target_cell_ids("Mosaic_A") + gid = next(iter(target)) + pos, quat = sim.circuit_access.get_cell_position_rotation(gid) + assert pos.shape == (3,) + assert quat is not None + assert quat.shape == (4,) + + +def test_cell_transform_is_populated(): + """Cell transform should be populated during instantiate_gids when SONATA coords exist.""" + base = parent_dir / "examples" / "2-sonata-network" / "sim_quick_scx_sonata_multicircuit" / "simulation_config_noinput.json" + sim = CircuitSimulation(base) + target = sim.circuit_access.get_target_cell_ids("Mosaic_A") + gid = next(iter(target)) + sim.instantiate_gids(cells=[gid]) + cell = sim.cells[gid] + assert cell._local_to_global_matrix is not None + # Verify the translation part matches SONATA node position + pos, _ = sim.circuit_access.get_cell_position_rotation(gid) + np.testing.assert_allclose(cell._local_to_global_matrix[:, 3], pos) diff --git a/tests/test_stimulus/test_extracellular.py b/tests/test_stimulus/test_extracellular.py new file mode 100644 index 00000000..91fb3469 --- /dev/null +++ b/tests/test_stimulus/test_extracellular.py @@ -0,0 +1,412 @@ +# Copyright 2023-2024 Blue Brain Project / EPFL + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +import pytest + +from bluecellulab.stimulus.extracellular import ElectrodeSource + + +def test_electrode_source_init(): + """Test ElectrodeSource initialization.""" + es = ElectrodeSource( + base_amp=0, + delay=0, + duration=10, + fields=[{"Ex": 50, "Ey": -25, "Ez": 75}], + ramp_up_time=0, + ramp_down_time=0, + dt=1.0, + ) + assert es.duration == 10 + assert es.dt == 1.0 + assert len(es.fields) == 1 + + +def test_apply_ramp_case1(): + """Test ramp application when ramp_up/down_time > dt and is multiple of dt.""" + import neuron + + dt = 0.5 + ramp_up_time = 2.0 + ramp_down_time = 1.5 + + es = ElectrodeSource(0, 0, 100, [], ramp_up_time, ramp_down_time, dt) + stim_vec = neuron.h.Vector(list(range(1, 11))) + + assert np.isclose(es.ramp_up_time, ramp_up_time) + assert np.isclose(es.ramp_down_time, ramp_down_time) + assert np.isclose(es.dt, dt) + + es.apply_ramp(stim_vec, es.dt) + + expected = [0, 2 / 3, 2, 4, 5, 6, 7, 8, 4.5, 0] + np.testing.assert_allclose(stim_vec.as_numpy(), expected) + + +def test_apply_ramp_case2(): + """Test ramp when ramp_up/down_time > dt but not multiple of dt.""" + import neuron + + dt = 0.5 + ramp_up_time = 2.4 + ramp_down_time = 1.7 + + es = ElectrodeSource(0, 0, 100, [], ramp_up_time, ramp_down_time, dt) + stim_vec = neuron.h.Vector(list(range(1, 11))) + + es.apply_ramp(stim_vec, es.dt) + + expected = [0, 2 / 3, 2, 4, 5, 6, 7, 8, 4.5, 0] + np.testing.assert_allclose(stim_vec.as_numpy(), expected) + + +def test_apply_ramp_case3(): + """Test ramp when ramp_up/down_time < dt (no ramp applied).""" + import neuron + + dt = 0.5 + ramp_up_time = 0.3 + ramp_down_time = 0.4 + + es = ElectrodeSource(0, 0, 100, [], ramp_up_time, ramp_down_time, dt) + stim_vec = neuron.h.Vector(list(range(1, 11))) + + es.apply_ramp(stim_vec, es.dt) + + expected = list(range(1, 11)) + np.testing.assert_allclose(stim_vec.as_numpy(), expected) + + +def test_dc_field(): + """Test constant (DC) field when frequency=0.""" + es = ElectrodeSource( + base_amp=0, + delay=0, + duration=10, + fields=[{"Ex": 100, "Ey": -50, "Ez": 50, "frequency": 0}], + ramp_up_time=0, + ramp_down_time=0, + dt=1.0, + ) + + assert es.efields.shape[0] == 3 + + for i in range(3): + field_values = es.efields[i] + assert np.all(field_values[1:-1] != 0) + + +def test_ac_field(): + """Test AC (cosine) field with non-zero frequency.""" + es = ElectrodeSource( + base_amp=0, + delay=0, + duration=10, + fields=[{"Ex": 100, "Ey": 0, "Ez": 0, "frequency": 10}], + ramp_up_time=0, + ramp_down_time=0, + dt=1.0, + ) + + assert es.efields.shape[0] == 3 + + +def test_multi_field_summation(): + """Test that multiple fields sum correctly.""" + es = ElectrodeSource( + base_amp=0, + delay=0, + duration=10, + fields=[ + {"Ex": 50, "Ey": -25, "Ez": 75, "frequency": 100}, + {"Ex": 100, "Ey": -50, "Ez": 50, "frequency": 0}, + ], + ramp_up_time=0, + ramp_down_time=0, + dt=1.0, + ) + + assert len(es.fields) == 2 + assert es.efields.shape[0] == 3 + + +def test_compute_potentials(): + """Test potential calculation from displacement vector.""" + es = ElectrodeSource( + base_amp=0, + delay=0, + duration=10, + fields=[{"Ex": 100, "Ey": -50, "Ez": 50, "frequency": 0}], + ramp_up_time=0, + ramp_down_time=0, + dt=1.0, + ) + + displacement = np.array([1e-6, 2e-6, 3e-6]) + potentials = es.compute_potentials(displacement) + + assert len(potentials) == len(es.time_vec) + + +def test_iadd_combining_sources(): + """Test combining two ElectrodeSource objects.""" + es1 = ElectrodeSource( + base_amp=0, + delay=0, + duration=10, + fields=[{"Ex": 50, "Ey": -25, "Ez": 75, "frequency": 0}], + ramp_up_time=0, + ramp_down_time=0, + dt=1.0, + ) + + es2 = ElectrodeSource( + base_amp=0, + delay=5, + duration=10, + fields=[{"Ex": 100, "Ey": -50, "Ez": 50, "frequency": 0}], + ramp_up_time=0, + ramp_down_time=0, + dt=1.0, + ) + + original_len = len(es1.time_vec) + es1 += es2 + + assert len(es1.time_vec) >= original_len + + +def test_iadd_different_dt_raises(): + """Test that combining sources with different dt raises assertion error.""" + es1 = ElectrodeSource( + base_amp=0, delay=0, duration=10, fields=[], + ramp_up_time=0, ramp_down_time=0, dt=1.0 + ) + + es2 = ElectrodeSource( + base_amp=0, delay=0, duration=10, fields=[], + ramp_up_time=0, ramp_down_time=0, dt=0.5 + ) + + with pytest.raises(AssertionError, match="multiple extracellular stimuli must have common dt"): + es1 += es2 + + +def test_iadd_with_delay(): + """Test combining sources with delay.""" + es1 = ElectrodeSource( + base_amp=0, + delay=2, + duration=10, + fields=[{"Ex": 50, "Ey": -25, "Ez": 75, "frequency": 0}], + ramp_up_time=0, + ramp_down_time=0, + dt=1.0, + ) + + es2 = ElectrodeSource( + base_amp=0, + delay=5, + duration=10, + fields=[{"Ex": 100, "Ey": -50, "Ez": 50, "frequency": 0}], + ramp_up_time=0, + ramp_down_time=0, + dt=1.0, + ) + + es1 += es2 + + assert len(es1.time_vec) > 0 + assert es1.time_vec[0] == 0 + + +def test_iadd_non_overlapping(): + """Test combining sources with non-overlapping time ranges.""" + es1 = ElectrodeSource( + base_amp=0, + delay=0, + duration=5, + fields=[{"Ex": 50, "Ey": -25, "Ez": 75, "frequency": 0}], + ramp_up_time=0, + ramp_down_time=0, + dt=1.0, + ) + + es2 = ElectrodeSource( + base_amp=0, + delay=10, + duration=5, + fields=[{"Ex": 100, "Ey": -50, "Ez": 50, "frequency": 0}], + ramp_up_time=0, + ramp_down_time=0, + dt=1.0, + ) + + es1 += es2 + + assert len(es1.time_vec) > 0 + + +def test_iadd_time_concatenation(): + """Test combining sources with time vector concatenation edge case.""" + es1 = ElectrodeSource( + base_amp=0, + delay=0, + duration=5, + fields=[{"Ex": 50, "Ey": -25, "Ez": 75, "frequency": 0}], + ramp_up_time=0, + ramp_down_time=0, + dt=1.0, + ) + + es2 = ElectrodeSource( + base_amp=0, + delay=5, + duration=5, + fields=[{"Ex": 100, "Ey": -50, "Ez": 50, "frequency": 0}], + ramp_up_time=0, + ramp_down_time=0, + dt=1.0, + ) + + es1 += es2 + + assert len(es1.time_vec) > 0 + assert es1.time_vec[0] == 0 + + +def test_cleanup(): + """Test that cleanup() clears references.""" + es = ElectrodeSource( + base_amp=0, + delay=0, + duration=10, + fields=[{"Ex": 50, "Ey": -25, "Ez": 75, "frequency": 0}], + ramp_up_time=0, + ramp_down_time=0, + dt=1.0, + ) + + assert es.efields is not None + assert es.segment_displacements is not None + + es.cleanup() + + assert es.efields is None + assert es.segment_displacements is None + + +def test_interp_axon_positions(): + """Test axon position interpolation.""" + from bluecellulab.cell import Cell + + soma_position = np.array([0.0, 0.0, 0.0]) + + # Test axon[0] + pos = Cell.interp_axon_positions(0.0, 0, soma_position) + np.testing.assert_allclose(pos, [0.0, 0.0, 0.0]) + + pos = Cell.interp_axon_positions(0.5, 0, soma_position) + np.testing.assert_allclose(pos, [0.0, -15.0, 0.0]) + + pos = Cell.interp_axon_positions(1.0, 0, soma_position) + np.testing.assert_allclose(pos, [0.0, -30.0, 0.0]) + + # Test axon[1] + pos = Cell.interp_axon_positions(0.0, 1, soma_position) + np.testing.assert_allclose(pos, [0.0, -30.0, 0.0]) + + pos = Cell.interp_axon_positions(0.5, 1, soma_position) + np.testing.assert_allclose(pos, [0.0, -45.0, 0.0]) + + pos = Cell.interp_axon_positions(1.0, 1, soma_position) + np.testing.assert_allclose(pos, [0.0, -60.0, 0.0]) + + +def test_interp_axon_positions_error(): + """Test that more than 2 axon sections raises error.""" + from bluecellulab.cell import Cell + + soma_position = np.array([0.0, 0.0, 0.0]) + + with pytest.raises(ValueError, match="More than 2 axon sections exist"): + Cell.interp_axon_positions(0.5, 2, soma_position) + + +def test_interp_myelin_positions(): + """Test myelin position interpolation.""" + from bluecellulab.cell import Cell + + soma_position = np.array([0.0, 0.0, 0.0]) + + # Test myelin[0] + pos = Cell.interp_myelin_positions(0.0, 0, soma_position) + np.testing.assert_allclose(pos, [0.0, -60.0, 0.0]) + + pos = Cell.interp_myelin_positions(0.5, 0, soma_position) + np.testing.assert_allclose(pos, [0.0, -560.0, 0.0]) + + pos = Cell.interp_myelin_positions(1.0, 0, soma_position) + np.testing.assert_allclose(pos, [0.0, -1060.0, 0.0]) + + +def test_interp_myelin_positions_error(): + """Test that more than 1 myelin section raises error.""" + from bluecellulab.cell import Cell + + soma_position = np.array([0.0, 0.0, 0.0]) + + with pytest.raises(ValueError, match="More than 1 myelin section exist"): + Cell.interp_myelin_positions(0.5, 1, soma_position) + + +def test_apply_segment_potentials_inserts_extracellular(): + """Test that apply_segment_potentials inserts the extracellular mechanism.""" + import neuron + + es = ElectrodeSource( + base_amp=0, + delay=0, + duration=2, + fields=[{"Ex": 100, "Ey": 0, "Ez": 0, "frequency": 0}], + ramp_up_time=0, + ramp_down_time=0, + dt=1.0, + ) + # Create a minimal section with one segment + sec = neuron.h.Section(name="testsec") + sec.nseg = 1 + seg = sec(0.5) + assert not sec.has_membrane("extracellular") + es.segment_displacements[seg] = np.array([1e-6, 0.0, 0.0]) + es.apply_segment_potentials() + assert sec.has_membrane("extracellular") + + +def test_combine_time_efields_disjoint_reverse_order(): + """Test _combine_time_efields when t1 is strictly after t2.""" + dt = 1.0 + t1 = np.array([5.0, 6.0, 7.0]) + e1 = np.ones((3, 3)) + t2 = np.array([0.0, 1.0, 2.0]) + e2 = np.ones((3, 3)) * 2 + combined_t, combined_e = ElectrodeSource._combine_time_efields( + t1, e1, t2, e2, False, False, dt + ) + expected_t = np.array([0.0, 1.0, 2.0, 5.0, 6.0, 7.0]) + np.testing.assert_allclose(combined_t, expected_t) + assert combined_e.shape == (3, 6) + np.testing.assert_allclose(combined_e[:, :3], e2) + np.testing.assert_allclose(combined_e[:, 3:], e1) diff --git a/tox.ini b/tox.ini index 27621d68..b864d694 100644 --- a/tox.ini +++ b/tox.ini @@ -41,7 +41,8 @@ deps = pandas-stubs>=2.0.0 types-setuptools>=67.8.0.0 ruff>=0.0.270 - docformatter>=1.7.2 + # docformatter 1.7.8 has tokenisation regressions; pin until upstream fixes + docformatter>=1.7.2,<1.7.8 commands = ruff check . --select F541,F401 --per-file-ignores="__init__.py:F401" pycodestyle {[base]name} --ignore=E501,W504,W503 @@ -59,6 +60,11 @@ deps = nbmake>=1.5.1 scipy>=1.11.1 seaborn>=0.12.2 +allowlist_externals = + bash +commands_pre = + bash -c "cd examples/1-singlecell && rm -rf arm64 x86_64 && nrnivmodl ../mechanisms" + bash -c "cd examples/2-sonata-network && rm -rf arm64 x86_64 && nrnivmodl ../mechanisms" commands = pytest --ignore=examples/7-Extra-Simulation/ --numprocesses=auto --forked --nbmake examples # exclude long tests