Source code for nrv.nmod.results._nerve_results

"""
NRV-:class:`.nerve_results` handling.
"""

from ...backend._NRV_Results import sim_results
from ._fascicles_results import fascicle_results
from ._axons_results import axon_results
from ...backend._log_interface import rise_error, rise_warning, pass_info
from ...utils._units import nm, convert, from_nrv_unit
from ...utils._misc import membrane_capacitance_from_model


import matplotlib.pyplot as plt
import numpy as np


def number_in_str(s: str) -> bool:
    return any(i.isdigit() for i in s)


[docs] class nerve_results(sim_results): """ """
[docs] def __init__(self, context=None): super().__init__(context)
@property def fascicle_keys(self) -> list: """ Number of fascicles in the fascicle """ all_keys = self.keys() return [i for i in all_keys if ("fascicle" in i and number_in_str(i))] @property def n_fasc(self) -> int: """ Number of fascicles in the fascicle """ return len(self.fascicles) @property def n_ax(self) -> int: """ Number of axons in the fascicle """ fasc_keys = self.fascicle_keys _n_ax = 0 for key in fasc_keys: _n_ax += self[key].n_ax return _n_ax @property def axons_type(self) -> np.ndarray: """ type of axons of each fascicles Returns ------- np.ndarray (self.n_ax, 2) _description_ """ return self.axons_pop_properties[:,[0,2]] @property def fasc_properties(self) -> np.ndarray: """ Porperties of axons population of each fascicles Returns ------- np.ndarray (self.n_ax, 6) ndarray gathering, for all fascicles in the nerve, fascicle IDs, diameter and y and z positions. """ fasc_keys = self.fascicle_keys _fasc = np.zeros((self.n_fasc, 4)) for i_fasc, key in enumerate(fasc_keys): fasc_ = self[key] _fasc[i_fasc, :] = np.array([ fasc_.ID, fasc_.D, fasc_.y_grav_center, fasc_.z_grav_center, ]) return _fasc @property def axons_pop_properties(self) -> np.ndarray: """ Porperties of axons population of each fascicles Returns ------- np.ndarray (self.n_ax, 6) ndarray gathering, for all axons in the nerve, corresponding fascicle and axon IDs, myelinating type, diameter and y and z positions. """ fasc_keys = self.fascicle_keys _mye = np.zeros((self.n_ax, 6)) _offset = 0 for key in fasc_keys: fasc_n_ax = self[key].n_ax fasc_axons = np.vstack( ( self[key].ID * np.ones(fasc_n_ax), np.arange(fasc_n_ax), self[key].axons_type, self[key].axons_diameter, self[key].axons_y, self[key].axons_z, ) ).T _mye[_offset : _offset + fasc_n_ax, :] = fasc_axons _offset += fasc_n_ax return _mye
[docs] def get_fascicle_results(self, ID: int) -> fascicle_results: if ID not in self.fascicles_IDs: rise_error(("Fascicle ID does not exists.")) else: return self[f"fascicle{ID}"]
[docs] def get_axon_results(self, fasc_ID: int, ax_ID: int) -> axon_results: fasc_ID = int(fasc_ID) ax_ID = int(ax_ID) if fasc_ID not in self.fascicles_IDs: rise_error(f"Fascicle ID: {fasc_ID} does not exists.") else: if ax_ID >= self[f"fascicle{fasc_ID}"].n_ax: rise_error(f"Axon ID: {ax_ID} does not exists in Fascicle {fasc_ID} .") else: return self[f"fascicle{fasc_ID}"][f"axon{ax_ID}"]
[docs] def get_fascicle_key(self) -> list: all_keys = self.keys() fascicle_keys = [i for i in all_keys if ("fascicle" in i and number_in_str(i))] return fascicle_keys
[docs] def get_recruited_axons( self, ax_type: str = "all", normalize: bool = False ) -> int | float: """ Return the number or the ratio of recruited axons in the nerve Parameters ---------- diam : float diameter above wich axon should be counted. ax_type : str, optional type of axon counted,possible options: - "all" (default) - "unmyelinated" - "myelinated" normalize : bool, optional if False the total number of recruited axons is returned, else the ratio is returned, by default False Returns ------- float number of recruited axons """ fasc_keys = self.fascicle_keys n_recr = 0 for key in fasc_keys: fasc_res = self[key] n_recr += fasc_res.get_recruited_axons(ax_type=ax_type, normalize=normalize) if normalize: n_recr /= self.n_fasc return n_recr
# impeddance related methods
[docs] def get_membrane_conductivity( self, x: float = 0, t: float = 0, unit: str = "S/cm**2", mem_th: float = 7 * nm ) -> np.array: """ get the membran conductivity of each axon at a position x and a time t Parameters ---------- x : float, optional x-position in um where to get the conductivity, by default 0 t : float, optional simulation time in ms when to get the conductivity, by default 0 unit : str, optional unit of the returned conductivity see `units`, by default "S/cm**2" mem_th : float, optional membrane thickness in um, by default 7*nm Note ---- depending of the unit parameter this function either return : - the surface conductivity in [S]/([m]*[m]): from neuron simulation - the conductivity in [S]/[m]: by multiplying surface conductivity by membrane thickness """ g = [] f_keys = self.get_fascicle_key() for key in f_keys: g_ = self[key].get_membrane_conductivity(x=x, t=t, unit=unit, mem_th=mem_th) if g_ is not None: g = np.concatenate((g, g_)) else: return None return g
[docs] def get_membrane_capacitance( self, unit: str = "uF/cm**2", mem_th: float = 7 * nm ) -> tuple[float]: """ get the membrane capacitance or permitivity of unmyelinated and myelinated axons filling the ner Parameters ---------- unit : str, optional unit of the returned conductivity see `units`, by default "S/cm**2" mem_th : float, optional membrane thickness in um, by default 7*nm Note ---- depending of the unit parameter this function either return : - the surface conductivity in [S]/([m]*[m]): from neuron simulation - the conductivity in [S]/[m]: by multiplying surface conductivity by membrane thickness """ u_c_mem = membrane_capacitance_from_model(self.unmyelinated_param["model"]) m_c_mem = membrane_capacitance_from_model(self.myelinated_param["model"]) # Surface capacity in [F]/([m]*[m]) if "2" in unit: return convert(u_c_mem, "S/cm**2", unit), convert(m_c_mem, "S/cm**2", unit) # permitivity in [F]/[m] else: u_c_mem *= from_nrv_unit(mem_th, "cm") m_c_mem *= from_nrv_unit(mem_th, "cm") return convert(u_c_mem, "S/cm", unit), convert(m_c_mem, "S/cm", unit)
[docs] def get_membrane_complexe_admitance( self, f: float = 1.0, x: float = 0, t: float = 0, unit: str = "S/m", mem_th: float = 7 * nm, ) -> np.array: """ get the membran complexe admitance of each axon at a position x and a time t for a given frequency Parameters ---------- f : float or np.array, optional effective frequency in kHz, by default 1 x : float, optional x-position in um where to get the conductivity, by default 0 t : float, optional simulation time in ms when to get the conductivity, by default 0 unit : str, optional unit of the returned conductivity see `units`, by default "S/cm**2" mem_th : float, optional membrane thickness in um, by default 7*nm """ Y = [] f_keys = self.get_fascicle_key() for key in f_keys: Y_ = self[key].get_membrane_complexe_admitance( f=f, x=x, t=t, unit=unit, mem_th=mem_th ) if Y_ is not None: Y = np.concatenate((Y, Y_)) else: return None return Y
## Representation methods
[docs] def plot_recruited_fibers( self, axes: plt.axes, contour_color: str = "k", myel_color: str = "r", unmyel_color: str = "b", elec_color: str = "gold", num: bool = False, **kwgs, ): """ """ ## plot contour axes.add_patch( plt.Circle( (self.y_grav_center, self.z_grav_center), self.D / 2, color=contour_color, fill=False, linewidth=4, ) ) fasc_keys = self.get_fascicle_key() for key in fasc_keys: fasc_res = self[key] fasc_res.plot_recruited_fibers( axes=axes, contour_color="grey", myel_color=myel_color, unmyel_color=unmyel_color, num=num, ) if "extra_stim" in self: if self.extra_stim is not None: self.extra_stim.plot(axes=axes, color=elec_color, nerve_d=self.D) axes.set_xlim((-1.1 * self.D / 2, 1.1 * self.D / 2)) axes.set_ylim((-1.1 * self.D / 2, 1.1 * self.D / 2))