diff --git a/PySONIC/core/__init__.py b/PySONIC/core/__init__.py index 2b5ae38..1d59ec3 100644 --- a/PySONIC/core/__init__.py +++ b/PySONIC/core/__init__.py @@ -1,12 +1,12 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # @Author: Theo Lemaire # @Date: 2017-06-06 13:36:00 # @Email: theo.lemaire@epfl.ch # @Last Modified by: Theo Lemaire -# @Last Modified time: 2018-09-23 15:26:55 +# @Last Modified time: 2018-09-24 23:07:58 from .pneuron import PointNeuron -from .bls import BilayerSonophore +from .bls import BilayerSonophore, PmCompMethod from .nbls import NeuronalBilayerSonophore diff --git a/PySONIC/core/bls.py b/PySONIC/core/bls.py index 2533e51..d25eac3 100644 --- a/PySONIC/core/bls.py +++ b/PySONIC/core/bls.py @@ -1,841 +1,849 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # @Author: Theo Lemaire # @Date: 2016-09-29 16:16:19 # @Email: theo.lemaire@epfl.ch # @Last Modified by: Theo Lemaire -# @Last Modified time: 2018-09-24 21:30:13 +# @Last Modified time: 2018-09-24 23:06:47 +from enum import Enum import time import os import json import inspect import logging import warnings import numpy as np import pandas as pd import scipy.integrate as integrate from scipy.optimize import brentq, curve_fit from ..utils import * from ..constants import * # Get package logger logger = logging.getLogger('PySONIC') +class PmCompMethod(Enum): + """ Enum: types of computation method for the intermolecular pressure """ + direct = 1 + predict = 2 + + + class BilayerSonophore: """ This class contains the geometric and mechanical parameters of the Bilayer Sonophore Model, as well as all the core functions needed to compute the dynamics (kinetics and kinematics) of the bilayer membrane cavitation, and run dynamic BLS simulations. """ # BIOMECHANICAL PARAMETERS T = 309.15 # Temperature (K) Rg = 8.314 # Universal gas constant (Pa.m^3.mol^-1.K^-1) delta0 = 2.0e-9 # Thickness of the leaflet (m) Delta_ = 1.4e-9 # Initial gap between the two leaflets on a non-charged membrane at equil. (m) pDelta = 1.0e5 # Attraction/repulsion pressure coefficient (Pa) m = 5.0 # Exponent in the repulsion term (dimensionless) n = 3.3 # Exponent in the attraction term (dimensionless) rhoL = 1075.0 # Density of the surrounding fluid (kg/m^3) muL = 7.0e-4 # Dynamic viscosity of the surrounding fluid (Pa.s) muS = 0.035 # Dynamic viscosity of the leaflet (Pa.s) kA = 0.24 # Area compression modulus of the leaflet (N/m) alpha = 7.56 # Tissue shear loss modulus frequency coefficient (Pa.s) C0 = 0.62 # Initial gas molar concentration in the surrounding fluid (mol/m^3) kH = 1.613e5 # Henry's constant (Pa.m^3/mol) P0 = 1.0e5 # Static pressure in the surrounding fluid (Pa) Dgl = 3.68e-9 # Diffusion coefficient of gas in the fluid (m^2/s) xi = 0.5e-9 # Boundary layer thickness for gas transport across leaflet (m) c = 1515.0 # Speed of sound in medium (m/s) # BIOPHYSICAL PARAMETERS epsilon0 = 8.854e-12 # Vacuum permittivity (F/m) epsilonR = 1.0 # Relative permittivity of intramembrane cavity (dimensionless) def __init__(self, a, Cm0, Qm0, Fdrive=None, embedding_depth=0.0): """ Constructor of the class. :param a: in-plane diameter of the sonophore structure within the membrane (m) :param Cm0: membrane resting capacitance (F/m2) :param Qm0: membrane resting charge density (C/m2) :param Fdrive: frequency of acoustic perturbation (Hz) :param embedding_depth: depth of the embedding tissue around the membrane (m) """ # Extract resting constants and geometry self.Cm0 = Cm0 self.Qm0 = Qm0 self.a = a self.d = embedding_depth self.S0 = np.pi * self.a**2 # Derive frequency-dependent tissue elastic modulus if Fdrive is not None: G_tissue = self.alpha * Fdrive # G'' (Pa) self.kA_tissue = 2 * G_tissue * self.d # kA of the tissue layer (N/m) else: self.kA_tissue = 0. # Check existence of lookups for derived parameters lookups = self.getLookups() akey = '{:.1f}'.format(a * 1e9) Qkey = '{:.2f}'.format(Qm0 * 1e5) # If no lookup, compute parameters and store them in lookup if akey not in lookups or Qkey not in lookups[akey]: # Find Delta that cancels out Pm + Pec at Z = 0 (m) if self.Qm0 == 0.0: D_eq = self.Delta_ else: (D_eq, Pnet_eq) = self.findDeltaEq(self.Qm0) assert Pnet_eq < PNET_EQ_MAX, 'High Pnet at Z = 0 with ∆ = %.2f nm' % (D_eq * 1e9) self.Delta = D_eq # Find optimal Lennard-Jones parameters to approximate PMavg (LJ_approx, std_err, _) = self.LJfitPMavg() assert std_err < PMAVG_STD_ERR_MAX, 'High error in PmAvg nonlinear fit:'\ ' std_err = %.2f Pa' % std_err self.LJ_approx = LJ_approx if akey not in lookups: lookups[akey] = {Qkey: {'LJ_approx': LJ_approx, 'Delta_eq': D_eq}} else: lookups[akey][Qkey] = {'LJ_approx': LJ_approx, 'Delta_eq': D_eq} logger.debug('Saving BLS derived parameters to lookup file') self.saveLookups(lookups) # If lookup exists, load parameters from it else: logger.debug('Loading BLS derived parameters from lookup file') self.LJ_approx = lookups[akey][Qkey]['LJ_approx'] self.Delta = lookups[akey][Qkey]['Delta_eq'] # Compute initial volume and gas content self.V0 = np.pi * self.Delta * self.a**2 self.ng0 = self.gasPa2mol(self.P0, self.V0) def __repr__(self): return 'BilayerSonophore({}m, {}F/cm2, {}C/cm2, embedding_depth={}m'.format( si_format([self.a, self.Cm0 * 1e-4, self.Qm0 * 1e-4, self.embedding_depth], precision=1, space=' ')) def pprint(self): return '{}m diameter BilayerSonophore'.format( si_format(self.a, precision=0, space=' ')) def getLookupsPath(self): return os.path.join(os.path.split(__file__)[0], 'bls_lookups.json') def getLookups(self): try: with open(self.getLookupsPath()) as fh: sample = json.load(fh) return sample except FileNotFoundError: return {} def saveLookups(self, lookups): with open(self.getLookupsPath(), 'w') as fh: json.dump(lookups, fh, indent=2) def pparams(self): s = '-------- Bilayer Sonophore --------\n' s += 'class attributes:\n' class_attrs = inspect.getmembers(self.__class__, lambda a: not(inspect.isroutine(a))) class_attrs = [a for a in class_attrs if not(a[0].startswith('__') and a[0].endswith('__'))] for ca in class_attrs: s += '{} = {}\n'.format(ca[0], ca[1]) s += 'instance attributes:\n' inst_attrs = inspect.getmembers(self, lambda a: not(inspect.isroutine(a))) inst_attrs = [a for a in inst_attrs if not(a[0].startswith('__') and a[0].endswith('__')) and a not in class_attrs] for ia in inst_attrs: s += '{} = {}\n'.format(ia[0], ia[1]) return s def reinit(self): logger.debug('Re-initializing BLS object') # Find Delta that cancels out Pm + Pec at Z = 0 (m) if self.Qm0 == 0.0: D_eq = self.Delta_ else: (D_eq, Pnet_eq) = self.findDeltaEq(self.Qm0) assert Pnet_eq < PNET_EQ_MAX, 'High Pnet at Z = 0 with ∆ = %.2f nm' % (D_eq * 1e9) self.Delta = D_eq # Compute initial volume and gas content self.V0 = np.pi * self.Delta * self.a**2 self.ng0 = self.gasPa2mol(self.P0, self.V0) def curvrad(self, Z): """ Return the (signed) instantaneous curvature radius of the leaflet. :param Z: leaflet apex outward deflection value (m) :return: leaflet curvature radius (m) """ if Z == 0.0: return np.inf else: return (self.a**2 + Z**2) / (2 * Z) def v_curvrad(self, Z): ''' Vectorized curvrad function ''' return np.array(list(map(self.curvrad, Z))) def surface(self, Z): """ Return the surface area of the stretched leaflet (spherical cap). :param Z: leaflet apex outward deflection value (m) :return: surface of the stretched leaflet (m^2) """ return np.pi * (self.a**2 + Z**2) def volume(self, Z): """ Return the total volume of the inter-leaflet space (cylinder +/- spherical cap). :param Z: leaflet apex outward deflection value (m) :return: inner volume of the bilayer sonophore structure (m^3) """ return np.pi * self.a**2 * self.Delta\ * (1 + (Z / (3 * self.Delta) * (3 + Z**2 / self.a**2))) def arealstrain(self, Z): """ Compute the areal strain of the stretched leaflet. epsilon = (S - S0)/S0 = (Z/a)^2 :param Z: leaflet apex outward deflection value (m) :return: areal strain (dimensionless) """ return (Z / self.a)**2 def Capct(self, Z): """ Compute the membrane capacitance per unit area, under the assumption of parallel-plate capacitor with average inter-layer distance. :param Z: leaflet apex outward deflection value (m) :return: capacitance per unit area (F/m2) """ if Z == 0.0: return self.Cm0 else: return ((self.Cm0 * self.Delta / self.a**2) * (Z + (self.a**2 - Z**2 - Z * self.Delta) / (2 * Z) * np.log((2 * Z + self.Delta) / self.Delta))) def v_Capct(self, Z): ''' Vectorized Capct function ''' return np.array(list(map(self.Capct, Z))) def derCapct(self, Z, U): """ Compute the derivative of the membrane capacitance per unit area with respect to time, under the assumption of parallel-plate capacitor. :param Z: leaflet apex outward deflection value (m) :param U: leaflet apex outward deflection velocity (m/s) :return: derivative of capacitance per unit area (F/m2.s) """ dCmdZ = ((self.Cm0 * self.Delta / self.a**2) * ((Z**2 + self.a**2) / (Z * (2 * Z + self.Delta)) - ((Z**2 + self.a**2) * np.log((2 * Z + self.Delta) / self.Delta)) / (2 * Z**2))) return dCmdZ * U def localdef(self, r, Z, R): """ Compute the (signed) local transverse leaflet deviation at a distance r from the center of the dome. :param r: in-plane distance from center of the sonophore (m) :param Z: leaflet apex outward deflection value (m) :param R: leaflet curvature radius (m) :return: local transverse leaflet deviation (m) """ if np.abs(Z) == 0.0: return 0.0 else: return np.sign(Z) * (np.sqrt(R**2 - r**2) - np.abs(R) + np.abs(Z)) def Pacoustic(self, t, Adrive, Fdrive, phi=np.pi): """ Compute the acoustic pressure at a specific time, given the amplitude, frequency and phase of the acoustic stimulus. :param t: time of interest :param Adrive: acoustic drive amplitude (Pa) :param Fdrive: acoustic drive frequency (Hz) :param phi: acoustic drive phase (rad) """ return Adrive * np.sin(2 * np.pi * Fdrive * t - phi) def PMlocal(self, r, Z, R): """ Compute the local intermolecular pressure. :param r: in-plane distance from center of the sonophore (m) :param Z: leaflet apex outward deflection value (m) :param R: leaflet curvature radius (m) :return: local intermolecular pressure (Pa) """ z = self.localdef(r, Z, R) relgap = (2 * z + self.Delta) / self.Delta_ return self.pDelta * ((1 / relgap)**self.m - (1 / relgap)**self.n) def PMavg(self, Z, R, S): """ Compute the average intermolecular pressure felt across the leaflet by quadratic integration. :param Z: leaflet apex outward deflection value (m) :param R: leaflet curvature radius (m) :param S: surface of the stretched leaflet (m^2) :return: averaged intermolecular resultant pressure across the leaflet (Pa) .. warning:: quadratic integration is computationally expensive. """ # Integrate intermolecular force over an infinitely thin ring of radius r from 0 to a fTotal, _ = integrate.quad(lambda r, Z, R: 2 * np.pi * r * self.PMlocal(r, Z, R), 0, self.a, args=(Z, R)) return fTotal / S def v_PMavg(self, Z, R, S): ''' Vectorized PMavg function ''' return np.array(list(map(self.PMavg, Z, R, S))) def LJfitPMavg(self): """ Determine optimal parameters of a Lennard-Jones expression approximating the average intermolecular pressure. These parameters are obtained by a nonlinear fit of the Lennard-Jones function for a range of deflection values between predetermined Zmin and Zmax. :return: 3-tuple with optimized LJ parameters for PmAvg prediction (Map) and the standard and max errors of the prediction in the fitting range (in Pascals) """ # Determine lower bound of deflection range: when Pm = Pmmax PMmax = LJFIT_PM_MAX # Pa Zminlb = -0.49 * self.Delta Zminub = 0.0 Zmin = brentq(lambda Z, Pmmax: self.PMavg(Z, self.curvrad(Z), self.surface(Z)) - PMmax, Zminlb, Zminub, args=(PMmax), xtol=1e-16) # Create vectors for geometric variables Zmax = 2 * self.a Z = np.arange(Zmin, Zmax, 1e-11) Pmavg = self.v_PMavg(Z, self.v_curvrad(Z), self.surface(Z)) # Compute optimal nonlinear fit of custom LJ function with initial guess x0_guess = self.delta0 C_guess = 0.1 * self.pDelta nrep_guess = self.m nattr_guess = self.n pguess = (x0_guess, C_guess, nrep_guess, nattr_guess) popt, _ = curve_fit(lambda x, x0, C, nrep, nattr: LennardJones(x, self.Delta, x0, C, nrep, nattr), Z, Pmavg, p0=pguess, maxfev=10000) (x0_opt, C_opt, nrep_opt, nattr_opt) = popt Pmavg_fit = LennardJones(Z, self.Delta, x0_opt, C_opt, nrep_opt, nattr_opt) # Compute prediction error residuals = Pmavg - Pmavg_fit ss_res = np.sum(residuals**2) N = residuals.size std_err = np.sqrt(ss_res / N) max_err = max(np.abs(residuals)) logger.debug('LJ approx: x0 = %.2f nm, C = %.2f kPa, m = %.2f, n = %.2f', x0_opt * 1e9, C_opt * 1e-3, nrep_opt, nattr_opt) LJ_approx = {"x0": x0_opt, "C": C_opt, "nrep": nrep_opt, "nattr": nattr_opt} return (LJ_approx, std_err, max_err) def PMavgpred(self, Z): """ Return the predicted intermolecular pressure based on a specific Lennard-Jones function fitted on the deflection physiological range. :param Z: leaflet apex outward deflection value (m) :return: predicted average intermolecular pressure (Pa) """ return LennardJones(Z, self.Delta, self.LJ_approx['x0'], self.LJ_approx['C'], self.LJ_approx['nrep'], self.LJ_approx['nattr']) def Pelec(self, Z, Qm): """ Compute the electric equivalent pressure term. :param Z: leaflet apex outward deflection value (m) :param Qm: membrane charge density (C/m2) :return: electric equivalent pressure (Pa) """ relS = self.S0 / self.surface(Z) abs_perm = self.epsilon0 * self.epsilonR # F/m return -relS * Qm**2 / (2 * abs_perm) # Pa def findDeltaEq(self, Qm): """ Compute the Delta that cancels out the (Pm + Pec) equation at Z = 0 for a given membrane charge density, using the Brent method to refine the pressure root iteratively. :param Qm: membrane charge density (C/m2) :return: equilibrium value (m) and associated pressure (Pa) """ f = lambda Delta: (self.pDelta * ((self.Delta_ / Delta)**self.m - (self.Delta_ / Delta)**self.n) + self.Pelec(0.0, Qm)) Delta_lb = 0.1 * self.Delta_ Delta_ub = 2.0 * self.Delta_ Delta_eq = brentq(f, Delta_lb, Delta_ub, xtol=1e-16) logger.debug('∆eq = %.2f nm', Delta_eq * 1e9) return (Delta_eq, f(Delta_eq)) def gasflux(self, Z, P): """ Compute the gas molar flux through the BLS boundary layer for an unsteady system. :param Z: leaflet apex outward deflection value (m) :param P: internal gas pressure in the inter-leaflet space (Pa) :return: gas molar flux (mol/s) """ dC = self.C0 - P / self.kH return 2 * self.surface(Z) * self.Dgl * dC / self.xi def gasmol2Pa(self, ng, V): """ Compute the gas pressure in the inter-leaflet space for an unsteady system, from the value of gas molar content. :param ng: internal molar content (mol) :param V: inner volume of the bilayer sonophore structure (m^3) :return: internal gas pressure (Pa) """ return ng * self.Rg * self.T / V def gasPa2mol(self, P, V): """ Compute the gas molar content in the inter-leaflet space for an unsteady system, from the value of internal gas pressure. :param P: internal gas pressure in the inter-leaflet space (Pa) :param V: inner volume of the bilayer sonophore structure (m^3) :return: internal gas molar content (mol) """ return P * V / (self.Rg * self.T) def PtotQS(self, Z, ng, Qm, Pac, Pm_comp_method): """ Compute the balance pressure of the quasi-steady system, upon application of an external perturbation on a charged membrane: Ptot = Pm + Pg + Pec - P0 - Pac. :param Z: leaflet apex outward deflection value (m) :param ng: internal molar content (mol) :param Qm: membrane charge density (C/m2) :param Pac: external acoustic perturbation (Pa) :param Pm_comp_method: type of method used to compute average intermolecular pressure :return: total balance pressure (Pa) """ if Pm_comp_method is PmCompMethod.direct: Pm = self.PMavg(Z, self.curvrad(Z), self.surface(Z)) elif Pm_comp_method is PmCompMethod.predict: Pm = self.PMavgpred(Z) return Pm + self.gasmol2Pa(ng, self.volume(Z)) - self.P0 - Pac + self.Pelec(Z, Qm) def balancedefQS(self, ng, Qm, Pac=0.0, Pm_comp_method=PmCompMethod.predict): """ Compute the leaflet deflection upon application of an external perturbation to a quasi-steady system with a charged membrane. This function uses the Brent method (progressive approximation of function root) to solve the following transcendental equation for Z: Pm + Pg + Pec - P0 - Pac = 0. :param ng: internal molar content (mol) :param Qm: membrane charge density (C/m2) :param Pac: external acoustic perturbation (Pa) :param Pm_comp_method: type of method used to compute average intermolecular pressure :return: leaflet deflection (Z) canceling out the balance equation """ lb = -0.49 * self.Delta ub = self.a Plb = self.PtotQS(lb, ng, Qm, Pac, Pm_comp_method) Pub = self.PtotQS(ub, ng, Qm, Pac, Pm_comp_method) assert (Plb > 0 > Pub), '[%d, %d] is not a sign changing interval for PtotQS' % (lb, ub) return brentq(self.PtotQS, lb, ub, args=(ng, Qm, Pac, Pm_comp_method), xtol=1e-16) def TEleaflet(self, Z): """ Compute the circumferential elastic tension felt across the entire leaflet upon stretching. :param Z: leaflet apex outward deflection value (m) :return: circumferential elastic tension (N/m) """ return self.kA * self.arealstrain(Z) def TEtissue(self, Z): """ Compute the circumferential elastic tension felt across the embedding viscoelastic tissue layer upon stretching. :param Z: leaflet apex outward deflection value (m) :return: circumferential elastic tension (N/m) """ return self.kA_tissue * self.arealstrain(Z) def TEtot(self, Z): """ Compute the total circumferential elastic tension (leaflet and embedding tissue) felt upon stretching. :param Z: leaflet apex outward deflection value (m) :return: circumferential elastic tension (N/m) """ return self.TEleaflet(Z) + self.TEtissue(Z) def PEtot(self, Z, R): """ Compute the total elastic tension pressure (leaflet + embedding tissue) felt upon stretching. :param Z: leaflet apex outward deflection value (m) :param R: leaflet curvature radius (m) :return: elastic tension pressure (Pa) """ return - self.TEtot(Z) / R def PVleaflet(self, U, R): """ Compute the viscous stress felt across the entire leaflet upon stretching. :param U: leaflet apex outward deflection velocity (m/s) :param R: leaflet curvature radius (m) :return: leaflet viscous stress (Pa) """ return - 12 * U * self.delta0 * self.muS / R**2 def PVfluid(self, U, R): """ Compute the viscous stress felt across the entire fluid upon stretching. :param U: leaflet apex outward deflection velocity (m/s) :param R: leaflet curvature radius (m) :return: fluid viscous stress (Pa) """ return - 4 * U * self.muL / np.abs(R) def accP(self, Pres, R): """ Compute the pressure-driven acceleration of the leaflet in the unsteady system, upon application of an external perturbation. :param Pres: net resultant pressure (Pa) :param R: leaflet curvature radius (m) :return: pressure-driven acceleration (m/s^2) """ return Pres / (self.rhoL * np.abs(R)) def accNL(self, U, R): """ Compute the non-linear term of the leaflet acceleration in the unsteady system, upon application of an external perturbation. :param U: leaflet apex outward deflection velocity (m/s) :param R: leaflet curvature radius (m) :return: nonlinear acceleration (m/s^2) .. note:: A simplified version of nonlinear acceleration (neglecting dR/dH) is used here. """ # return - (3/2 - 2*R/H) * U**2 / R return -(3 * U**2) / (2 * R) def derivatives(self, y, t, Adrive, Fdrive, Qm, phi, Pm_comp_method=PmCompMethod.predict): """ Compute the derivatives of the 3-ODE mechanical system variables, with an imposed constant charge density. :param y: vector of HH system variables at time t :param t: specific instant in time (s) :param Adrive: acoustic drive amplitude (Pa) :param Fdrive: acoustic drive frequency (Hz) :param Qm: membrane charge density (F/m2) :param phi: acoustic drive phase (rad) :param Pm_comp_method: type of method used to compute average intermolecular pressure :return: vector of mechanical system derivatives at time t """ # Split input vector explicitly (U, Z, ng) = y # Correct deflection value is below critical compression if Z < -0.5 * self.Delta: logger.warning('Deflection out of range: Z = %.2f nm', Z * 1e9) Z = -0.49 * self.Delta # Compute curvature radius R = self.curvrad(Z) # Compute total pressure Pg = self.gasmol2Pa(ng, self.volume(Z)) if Pm_comp_method is PmCompMethod.direct: Pm = self.PMavg(Z, self.curvrad(Z), self.surface(Z)) elif Pm_comp_method is PmCompMethod.predict: Pm = self.PMavgpred(Z) Ptot = (Pm + Pg - self.P0 - self.Pacoustic(t, Adrive, Fdrive, phi) + self.PEtot(Z, R) + self.PVleaflet(U, R) + self.PVfluid(U, R) + self.Pelec(Z, Qm)) # Compute derivatives dUdt = self.accP(Ptot, R) + self.accNL(U, R) dZdt = U dngdt = self.gasflux(Z, Pg) # Return derivatives vector return [dUdt, dZdt, dngdt] def checkInputs(self, Fdrive, Adrive, Qm, phi): ''' Check validity of stimulation parameters. :param Fdrive: acoustic drive frequency (Hz) :param Adrive: acoustic drive amplitude (Pa) :param phi: acoustic drive phase (rad) :param Qm: imposed membrane charge density (C/m2) ''' if not all(isinstance(param, float) for param in [Fdrive, Adrive, Qm, phi]): raise TypeError('Invalid stimulation parameters (must be float typed)') if Fdrive <= 0: raise ValueError('Invalid US driving frequency: {} kHz (must be strictly positive)' .format(Fdrive * 1e-3)) if Adrive < 0: raise ValueError('Invalid US pressure amplitude: {} kPa (must be positive or null)' .format(Adrive * 1e-3)) if Qm < CHARGE_RANGE[0] or Qm > CHARGE_RANGE[1]: raise ValueError('Invalid applied charge: {} nC/cm2 (must be within [{}, {}] interval' .format(Qm * 1e5, CHARGE_RANGE[0] * 1e5, CHARGE_RANGE[1] * 1e5)) if phi < 0 or phi >= 2 * np.pi: raise ValueError('Invalid US pressure phase: {:.2f} rad (must be within [0, 2 PI[ rad' .format(phi)) def simulate(self, Fdrive, Adrive, Qm, phi=np.pi, Pm_comp_method=PmCompMethod.predict): """ Compute short solutions of the mechanical system for specific US stimulation parameters and with an imposed membrane charge density. :param Fdrive: acoustic drive frequency (Hz) :param Adrive: acoustic drive amplitude (Pa) :param phi: acoustic drive phase (rad) :param Qm: imposed membrane charge density (C/m2) :param Pm_comp_method: type of method used to compute average intermolecular pressure :return: 3-tuple with the time profile, the solution matrix and a state vector """ # Check validity of stimulation parameters self.checkInputs(Fdrive, Adrive, Qm, phi) # Raise warnings as error warnings.filterwarnings('error') # Determine mechanical system time step Tdrive = 1 / Fdrive dt_mech = Tdrive / NPC_FULL t_mech_cycle = np.linspace(0, Tdrive - dt_mech, NPC_FULL) # Initialize system variables t0 = 0.0 Z0 = 0.0 U0 = 0.0 ng0 = self.ng0 # Solve quasi-steady equation to compute first deflection value Pac1 = self.Pacoustic(t0 + dt_mech, Adrive, Fdrive, phi) Z1 = self.balancedefQS(ng0, Qm, Pac1, Pm_comp_method) U1 = (Z1 - Z0) / dt_mech # Construct arrays to hold system variables states = np.array([1, 1]) t = np.array([t0, t0 + dt_mech]) y = np.array([[U0, U1], [Z0, Z1], [ng0, ng0]]) # Integrate mechanical system for a few acoustic cycles until stabilization j = 0 ng_last = None Z_last = None periodic_conv = False while not periodic_conv and j < NCYCLES_MAX: t_mech = t_mech_cycle + t[-1] + dt_mech y_mech = integrate.odeint(self.derivatives, y[:, -1], t_mech, args=(Adrive, Fdrive, Qm, phi, Pm_comp_method)).T # Compare Z and ng signals over the last 2 acoustic periods if j > 0: Z_rmse = rmse(Z_last, y_mech[1, :]) ng_rmse = rmse(ng_last, y_mech[2, :]) logger.debug('step %u: Z_rmse = %.2e m, ng_rmse = %.2e mol', j, Z_rmse, ng_rmse) if Z_rmse < Z_ERR_MAX and ng_rmse < NG_ERR_MAX: periodic_conv = True # Update last vectors for next comparison Z_last = y_mech[1, :] ng_last = y_mech[2, :] # Concatenate time and solutions to global vectors states = np.concatenate([states, np.ones(NPC_FULL)], axis=0) t = np.concatenate([t, t_mech], axis=0) y = np.concatenate([y, y_mech], axis=1) # Increment loop index j += 1 if j == NCYCLES_MAX: logger.warning('No convergence: stopping after %u cycles', j) else: logger.debug('Periodic convergence after %u cycles', j) states[-1] = 0 # return output variables return (t, y[1:, :], states) def runAndSave(self, outdir, logpath, Fdrive, Adrive, Qm): ''' Run a simulation of the mechanical system with specific parameters and an imposed value of charge density, and save the results in a PKL file. :param outdir: full path to output directory :param logpath: full path log file :param Fdrive: acoustic drive frequency (Hz) :param Adrive: acoustic drive amplitude (Pa) :param Qm: applided membrane charge density (C/m2) ''' # Get date and time info date_str = time.strftime("%Y.%m.%d") daytime_str = time.strftime("%H:%M:%S") logger.info('%s: simulation @ f = %sHz, A = %sPa, Q = %sC/cm2', self.pprint(), *si_format([Fdrive, Adrive, Qm * 1e-4], 2, space=' ')) # Run simulation tstart = time.time() (t, y, states) = self.simulate(Fdrive, Adrive, Qm) (Z, ng) = y tcomp = time.time() - tstart logger.debug('completed in %s', si_format(tcomp, 1)) U = np.insert(np.diff(Z) / np.diff(t), 0, 0.0) # Store dataframe and metadata df = pd.DataFrame({'t': t, 'states': states, 'U': U, 'Z': Z, 'ng': ng}) meta = {'a': self.a, 'd': self.d, 'Cm0': self.Cm0, 'Qm0': self.Qm0, 'Fdrive': Fdrive, 'Adrive': Adrive, 'phi': np.pi, 'Qm': Qm, 'tcomp': tcomp} # Export into to PKL file simcode = 'MECH_{:.0f}nm_{:.0f}kHz_{:.1f}kPa_{:.1f}nCcm2'.format( self.a * 1e9, Fdrive * 1e-3, Adrive * 1e-3, Qm * 1e5) outpath = '{}/{}.pkl'.format(outdir, simcode) with open(outpath, 'wb') as fh: pickle.dump({'meta': meta, 'data': df}, fh) logger.debug('simulation data exported to "%s"', outpath) # Compute key output metrics Zmax = np.amax(Z) Zmin = np.amin(Z) Zabs_max = np.amax(np.abs([Zmin, Zmax])) eAmax = self.arealstrain(Zabs_max) Tmax = self.TEtot(Zabs_max) Pmmax = self.PMavgpred(Zmin) ngmax = np.amax(ng) dUdtmax = np.amax(np.abs(np.diff(U) / np.diff(t)**2)) # Export key metrics to log file log = { 'A': date_str, 'B': daytime_str, 'C': self.a * 1e9, 'D': self.d * 1e6, 'E': Fdrive * 1e-3, 'F': Adrive * 1e-3, 'G': Qm * 1e5, 'H': t.size, 'I': round(tcomp, 2), 'J': self.kA + self.kA_tissue, 'K': Zmax * 1e9, 'L': eAmax, 'M': Tmax * 1e3, 'N': (ngmax - self.ng0) / self.ng0, 'O': Pmmax * 1e-3, 'P': dUdtmax } if xlslog(logpath, 'Data', log) == 1: logger.debug('log exported to "%s"', logpath) else: logger.error('log export to "%s" aborted', self.logpath) return outpath def getCycleProfiles(self, Fdrive, Adrive, Qm): ''' Run a mechanical simulation until periodic stabilization, and compute pressure profiles over the last acoustic cycle. :param Fdrive: acoustic drive frequency (Hz) :param Adrive: acoustic drive amplitude (Pa) :param Qm: imposed membrane charge density (C/m2) :return: dataframe with the time, kinematic and pressure profiles over the last cycle. ''' # Run default simulation and compute relevant profiles logger.info('Running mechanical simulation (a = %sm, f = %sHz, A = %sPa)', si_format(self.a, 1), si_format(Fdrive, 1), si_format(Adrive, 1)) t, y, _ = self.simulate(Fdrive, Adrive, Qm, Pm_comp_method=PmCompMethod.direct) dt = (t[-1] - t[0]) / (t.size - 1) Z, ng = y[:, -NPC_FULL:] t = t[-NPC_FULL:] t -= t[0] logger.info('Computing pressure cyclic profiles') R = self.v_curvrad(Z) U = np.diff(Z) / dt U = np.hstack((U, U[-1])) data = { 't': t, 'Z': Z, 'Cm': self.v_Capct(Z), 'P_M': self.v_PMavg(Z, R, self.surface(Z)), 'P_Q': self.Pelec(Z, Qm), 'P_{VE}': self.PEtot(Z, R) + self.PVleaflet(U, R), 'P_V': self.PVfluid(U, R), 'P_G': self.gasmol2Pa(ng, self.volume(Z)), 'P_0': - np.ones(Z.size) * self.P0 } return pd.DataFrame(data, columns=data.keys()) diff --git a/PySONIC/neurons/__init__.py b/PySONIC/neurons/__init__.py index 8d2ff12..f84fd0e 100644 --- a/PySONIC/neurons/__init__.py +++ b/PySONIC/neurons/__init__.py @@ -1,24 +1,23 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # @Author: Theo Lemaire # @Date: 2017-06-06 13:36:00 # @Email: theo.lemaire@epfl.ch # @Last Modified by: Theo Lemaire -# @Last Modified time: 2018-09-24 21:40:15 +# @Last Modified time: 2018-09-24 23:00:00 import inspect import sys -import os from .cortical import CorticalRS, CorticalFS, CorticalLTS, CorticalIB from .thalamic import ThalamicRE, ThalamoCortical from .leech import LeechTouch, LeechPressure, LeechRetzius def getNeuronsDict(): current_module = sys.modules[__name__] neurons_dict = {} for _, obj in inspect.getmembers(current_module): if inspect.isclass(obj) and isinstance(obj.name, str): neurons_dict[obj.name] = obj return neurons_dict diff --git a/PySONIC/plt/pltutils.py b/PySONIC/plt/pltutils.py index 778decc..4cff117 100644 --- a/PySONIC/plt/pltutils.py +++ b/PySONIC/plt/pltutils.py @@ -1,936 +1,936 @@ # -*- coding: utf-8 -*- # @Author: Theo Lemaire # @Date: 2017-08-23 14:55:37 # @Last Modified by: Theo Lemaire -# @Last Modified time: 2018-09-24 20:56:48 +# @Last Modified time: 2018-09-24 23:51:37 ''' Plotting utilities ''' import sys import os import pickle import ntpath import re import logging import tkinter as tk from tkinter import filedialog import numpy as np import matplotlib import matplotlib.pyplot as plt from matplotlib.patches import Rectangle import matplotlib.cm as cm from matplotlib.ticker import FormatStrFormatter -from ..utils import rescale, InputError, computeMeshEdges, si_format +from ..utils import rescale, computeMeshEdges, si_format from ..core import BilayerSonophore from .pltvars import pltvars from ..neurons import getNeuronsDict matplotlib.rcParams['pdf.fonttype'] = 42 matplotlib.rcParams['ps.fonttype'] = 42 matplotlib.rcParams['font.family'] = 'arial' # Get package logger logger = logging.getLogger('PySONIC') # Define global variables neuron = None bls = None timeunits = {'ASTIM': 't_ms', 'ESTIM': 't_ms', 'MECH': 't_us'} # Regular expression for input files rgxp = re.compile('(ESTIM|ASTIM)_([A-Za-z]*)_(.*).pkl') rgxp_mech = re.compile('(MECH)_(.*).pkl') # Figure naming conventions ESTIM_CW_title = '{} neuron: CW E-STIM {:.2f}mA/m2, {:.0f}ms' ESTIM_PW_title = '{} neuron: PW E-STIM {:.2f}mA/m2, {:.0f}ms, {:.2f}Hz PRF, {:.0f}% DC' ASTIM_CW_title = '{} neuron: CW A-STIM {:.0f}kHz, {:.0f}kPa, {:.0f}ms' ASTIM_PW_title = '{} neuron: PW A-STIM {:.0f}kHz, {:.0f}kPa, {:.0f}ms, {:.2f}Hz PRF, {:.2f}% DC' MECH_title = '{:.0f}nm BLS structure: MECH-STIM {:.0f}kHz, {:.0f}kPa' def cm2inch(*tupl): inch = 2.54 if isinstance(tupl[0], tuple): return tuple(i / inch for i in tupl[0]) else: return tuple(i / inch for i in tupl) class InteractiveLegend(object): """ Class defining an interactive matplotlib legend, where lines visibility can be toggled by simply clicking on the corresponding legend label. Other graphic objects can also be associated to the toggle of a specific line Adapted from: http://stackoverflow.com/questions/31410043/hiding-lines-after-showing-a-pyplot-figure """ def __init__(self, legend, aliases): self.legend = legend self.fig = legend.axes.figure self.lookup_artist, self.lookup_handle = self._build_lookups(legend) self._setup_connections() self.handles_aliases = aliases self.update() def _setup_connections(self): for artist in self.legend.texts + self.legend.legendHandles: artist.set_picker(10) # 10 points tolerance self.fig.canvas.mpl_connect('pick_event', self.on_pick) def _build_lookups(self, legend): ''' Method of the InteractiveLegend class building the legend lookups. ''' labels = [t.get_text() for t in legend.texts] handles = legend.legendHandles label2handle = dict(zip(labels, handles)) handle2text = dict(zip(handles, legend.texts)) lookup_artist = {} lookup_handle = {} for artist in legend.axes.get_children(): if artist.get_label() in labels: handle = label2handle[artist.get_label()] lookup_handle[artist] = handle lookup_artist[handle] = artist lookup_artist[handle2text[handle]] = artist lookup_handle.update(zip(handles, handles)) lookup_handle.update(zip(legend.texts, handles)) return lookup_artist, lookup_handle def on_pick(self, event): handle = event.artist if handle in self.lookup_artist: artist = self.lookup_artist[handle] artist.set_visible(not artist.get_visible()) self.update() def update(self): for artist in self.lookup_artist.values(): handle = self.lookup_handle[artist] if artist.get_visible(): handle.set_visible(True) if artist in self.handles_aliases: for al in self.handles_aliases[artist]: al.set_visible(True) else: handle.set_visible(False) if artist in self.handles_aliases: for al in self.handles_aliases[artist]: al.set_visible(False) self.fig.canvas.draw() def show(self): ''' showing the interactive legend ''' plt.show() def getPatchesLoc(t, states): ''' Determine the location of stimulus patches. :param t: simulation time vector (s). :param states: a vector of stimulation state (ON/OFF) at each instant in time. :return: 3-tuple with number of patches, timing of STIM-ON an STIM-OFF instants. ''' # Compute states derivatives and identify bounds indexes of pulses dstates = np.diff(states) ipatch_on = np.insert(np.where(dstates > 0.0)[0] + 1, 0, 0) ipatch_off = np.where(dstates < 0.0)[0] + 1 if ipatch_off.size < ipatch_on.size: ioff = t.size - 1 if ipatch_off.size == 0: ipatch_off = np.array([ioff]) else: ipatch_off = np.insert(ipatch_off, ipatch_off.size - 1, ioff) # Get time instants for pulses ON and OFF npatches = ipatch_on.size tpatch_on = t[ipatch_on] tpatch_off = t[ipatch_off] # return 3-tuple with #patches, pulse ON and pulse OFF instants return (npatches, tpatch_on, tpatch_off) def SaveFigDialog(dirname, filename): """ Open a FileSaveDialogBox to set the directory and name of the figure to be saved. The default directory and filename are given, and the default extension is ".pdf" :param dirname: default directory :param filename: default filename :return: full path to the chosen filename """ root = tk.Tk() root.withdraw() filename_out = filedialog.asksaveasfilename(defaultextension=".pdf", initialdir=dirname, initialfile=filename) return filename_out def plotComp(varname, filepaths, labels=None, fs=15, lw=2, colors=None, lines=None, patches='one', xticks=None, yticks=None, blacklegend=False, straightlegend=False, showfig=True, inset=None, figsize=(11, 4)): ''' Compare profiles of several specific output variables of NICE simulations. :param varname: name of variable to extract and compare :param filepaths: list of full paths to output data files to be compared :param labels: list of labels to use in the legend :param fs: labels fontsize :param patches: string indicating whether to indicate periods of stimulation with colored rectangular patches ''' # Input check 1: variable name if varname not in pltvars: - raise InputError('Unknown plot variable: "{}"'.format(varname)) + raise KeyError('Unknown plot variable: "{}"'.format(varname)) pltvar = pltvars[varname] # Input check 2: labels if labels is not None: if len(labels) != len(filepaths): - raise InputError('Invalid labels ({}): not matching number of compared files ({})' - .format(len(labels), len(filepaths))) + raise AssertionError('Invalid labels ({}): not matching number of compared files ({})' + .format(len(labels), len(filepaths))) if not all(isinstance(x, str) for x in labels): - raise InputError('Invalid labels: must be string typed') + raise TypeError('Invalid labels: must be string typed') # Input check 3: line styles and colors if colors is None: colors = ['C{}'.format(j) for j in range(len(filepaths))] if lines is None: lines = ['-'] * len(filepaths) # Input check 4: STIM-ON patches greypatch = False if patches == 'none': patches = [False] * len(filepaths) elif patches == 'all': patches = [True] * len(filepaths) elif patches == 'one': patches = [True] + [False] * (len(filepaths) - 1) greypatch = True elif isinstance(patches, list): if len(patches) != len(filepaths): - raise InputError('Invalid patches ({}): not matching number of compared files ({})' - .format(len(patches), len(filepaths))) + raise AssertionError('Invalid patches ({}): not matching number of compared files ({})' + .format(len(patches), len(filepaths))) if not all(isinstance(p, bool) for p in patches): - raise InputError('Invalid patch sequence: all list items must be boolean typed') + raise TypeError('Invalid patch sequence: all list items must be boolean typed') else: - raise InputError('Invalid patches: must be either "none", all", "one", or a boolean list') + raise ValueError('Invalid patches: must be either "none", all", "one", or a boolean list') # Initialize figure and axis fig, ax = plt.subplots(figsize=figsize) ax.set_zorder(0) for item in ['top', 'right']: ax.spines[item].set_visible(False) if 'min' in pltvar and 'max' in pltvar: # optional min and max on y-axis ax.set_ylim(pltvar['min'], pltvar['max']) if pltvar['unit']: # y-label with optional unit ax.set_ylabel('$\\rm {}\ ({})$'.format(pltvar['label'], pltvar['unit']), fontsize=fs) else: ax.set_ylabel('$\\rm {}$'.format(pltvar['label']), fontsize=fs) if xticks is not None: # optional x-ticks ax.set_xticks(xticks) if yticks is not None: # optional y-ticks ax.set_yticks(yticks) else: ax.locator_params(axis='y', nbins=2) if any(ax.get_yticks() < 0): ax.yaxis.set_major_formatter(FormatStrFormatter('%+.0f')) for tick in ax.xaxis.get_major_ticks() + ax.yaxis.get_major_ticks(): tick.label.set_fontsize(fs) # Optional inset axis if inset is not None: inset_ax = fig.add_axes(ax.get_position()) inset_ax.set_xlim(inset['xlims'][0], inset['xlims'][1]) inset_ax.set_ylim(inset['ylims'][0], inset['ylims'][1]) inset_ax.set_xticks([]) inset_ax.set_yticks([]) # inset_ax.patch.set_alpha(1.0) inset_ax.set_zorder(1) inset_ax.add_patch(Rectangle((inset['xlims'][0], inset['ylims'][0]), inset['xlims'][1] - inset['xlims'][0], inset['ylims'][1] - inset['ylims'][0], color='w')) # Retrieve neurons dictionary neurons_dict = getNeuronsDict() # Loop through data files aliases = {} for j, filepath in enumerate(filepaths): # Retrieve sim type pkl_filename = ntpath.basename(filepath) mo1 = rgxp.fullmatch(pkl_filename) mo2 = rgxp_mech.fullmatch(pkl_filename) if mo1: mo = mo1 elif mo2: mo = mo2 else: logger.error('Error: "%s" file does not match regexp pattern', pkl_filename) sys.exit(1) sim_type = mo.group(1) if sim_type not in ('MECH', 'ASTIM', 'ESTIM'): - raise InputError('Invalid simulation type: {}'.format(sim_type)) + raise ValueError('Invalid simulation type: {}'.format(sim_type)) if j == 0: sim_type_ref = sim_type t_plt = pltvars[timeunits[sim_type]] elif sim_type != sim_type_ref: - raise InputError('Invalid comparison: different simulation types') + raise ValueError('Invalid comparison: different simulation types') # Load data logger.info('Loading data from "%s"', pkl_filename) with open(filepath, 'rb') as fh: frame = pickle.load(fh) df = frame['data'] meta = frame['meta'] # Extract variables t = df['t'].values states = df['states'].values nsamples = t.size # Initialize neuron object if ESTIM or ASTIM sim type if sim_type in ['ASTIM', 'ESTIM']: neuron_name = mo.group(2) global neuron neuron = neurons_dict[neuron_name]() Cm0 = neuron.Cm0 Qm0 = Cm0 * neuron.Vm0 * 1e-3 # Extract neuron states if needed if 'alias' in pltvar and 'neuron_states' in pltvar['alias']: neuron_states = [df[sn].values for sn in neuron.states_names] else: Cm0 = meta['Cm0'] Qm0 = meta['Qm0'] # Initialize BLS if needed if sim_type in ['MECH', 'ASTIM'] and 'alias' in pltvar and 'bls' in pltvar['alias']: global bls bls = BilayerSonophore(meta['a'], Cm0, Qm0) # Determine patches location npatches, tpatch_on, tpatch_off = getPatchesLoc(t, states) # Add onset to time vectors if t_plt['onset'] > 0.0: tonset = np.array([-t_plt['onset'], -t[0] - t[1]]) t = np.hstack((tonset, t)) states = np.hstack((states, np.zeros(2))) # Set x-axis label ax.set_xlabel('$\\rm {}\ ({})$'.format(t_plt['label'], t_plt['unit']), fontsize=fs) # Extract variable to plot if 'alias' in pltvar: var = eval(pltvar['alias']) elif 'key' in pltvar: var = df[pltvar['key']].values elif 'constant' in pltvar: var = eval(pltvar['constant']) * np.ones(nsamples) else: var = df[varname].values if var.size == t.size - 2: if varname is 'Vm': var = np.hstack((np.array([neuron.Vm0] * 2), var)) else: var = np.hstack((np.array([var[0]] * 2), var)) # var = np.insert(var, 0, var[0]) # Determine legend label if labels is not None: label = labels[j] else: if sim_type == 'ESTIM': if meta['DC'] == 1.0: label = ESTIM_CW_title.format(neuron_name, meta['Astim'], meta['tstim'] * 1e3) else: label = ESTIM_PW_title.format(neuron_name, meta['Astim'], meta['tstim'] * 1e3, meta['PRF'], meta['DC'] * 1e2) elif sim_type == 'ASTIM': if meta['DC'] == 1.0: label = ASTIM_CW_title.format(neuron_name, meta['Fdrive'] * 1e-3, meta['Adrive'] * 1e-3, meta['tstim'] * 1e3) else: label = ASTIM_PW_title.format(neuron_name, meta['Fdrive'] * 1e-3, meta['Adrive'] * 1e-3, meta['tstim'] * 1e3, meta['PRF'], meta['DC'] * 1e2) elif sim_type == 'MECH': label = MECH_title.format(meta['a'] * 1e9, meta['Fdrive'] * 1e-3, meta['Adrive'] * 1e-3) # Plot trace handle = ax.plot(t * t_plt['factor'], var * pltvar['factor'], linewidth=lw, linestyle=lines[j], color=colors[j], label=label) if inset is not None: inset_window = np.logical_and(t > (inset['xlims'][0] / t_plt['factor']), t < (inset['xlims'][1] / t_plt['factor'])) inset_ax.plot(t[inset_window] * t_plt['factor'], var[inset_window] * pltvar['factor'], linewidth=lw, linestyle=lines[j], color=colors[j]) # Add optional STIM-ON patches if patches[j]: (ybottom, ytop) = ax.get_ylim() la = [] color = '#8A8A8A' if greypatch else handle[0].get_color() for i in range(npatches): la.append(ax.axvspan(tpatch_on[i] * t_plt['factor'], tpatch_off[i] * t_plt['factor'], edgecolor='none', facecolor=color, alpha=0.2)) aliases[handle[0]] = la if inset is not None: cond_on = np.logical_and(tpatch_on > (inset['xlims'][0] / t_plt['factor']), tpatch_on < (inset['xlims'][1] / t_plt['factor'])) cond_off = np.logical_and(tpatch_off > (inset['xlims'][0] / t_plt['factor']), tpatch_off < (inset['xlims'][1] / t_plt['factor'])) cond_glob = np.logical_and(tpatch_on < (inset['xlims'][0] / t_plt['factor']), tpatch_off > (inset['xlims'][1] / t_plt['factor'])) cond_onoff = np.logical_or(cond_on, cond_off) cond = np.logical_or(cond_onoff, cond_glob) npatches_inset = np.sum(cond) for i in range(npatches_inset): inset_ax.add_patch(Rectangle((tpatch_on[cond][i] * t_plt['factor'], ybottom), (tpatch_off[cond][i] - tpatch_on[cond][i]) * t_plt['factor'], ytop - ybottom, color=color, alpha=0.1)) fig.tight_layout() # Optional operations on inset: if inset is not None: # Re-position inset axis axpos = ax.get_position() left, right, = rescale(inset['xcoords'], ax.get_xlim()[0], ax.get_xlim()[1], axpos.x0, axpos.x0 + axpos.width) bottom, top, = rescale(inset['ycoords'], ax.get_ylim()[0], ax.get_ylim()[1], axpos.y0, axpos.y0 + axpos.height) inset_ax.set_position([left, bottom, right - left, top - bottom]) for i in inset_ax.spines.values(): i.set_linewidth(2) # Materialize inset target region with contour frame ax.plot(inset['xlims'], [inset['ylims'][0]] * 2, linestyle='-', color='k') ax.plot(inset['xlims'], [inset['ylims'][1]] * 2, linestyle='-', color='k') ax.plot([inset['xlims'][0]] * 2, inset['ylims'], linestyle='-', color='k') ax.plot([inset['xlims'][1]] * 2, inset['ylims'], linestyle='-', color='k') # Link target and inset with dashed lines if possible if inset['xcoords'][1] < inset['xlims'][0]: ax.plot([inset['xcoords'][1], inset['xlims'][0]], [inset['ycoords'][0], inset['ylims'][0]], linestyle='--', color='k') ax.plot([inset['xcoords'][1], inset['xlims'][0]], [inset['ycoords'][1], inset['ylims'][1]], linestyle='--', color='k') elif inset['xcoords'][0] > inset['xlims'][1]: ax.plot([inset['xcoords'][0], inset['xlims'][1]], [inset['ycoords'][0], inset['ylims'][0]], linestyle='--', color='k') ax.plot([inset['xcoords'][0], inset['xlims'][1]], [inset['ycoords'][1], inset['ylims'][1]], linestyle='--', color='k') else: logger.warning('Inset x-coordinates intersect with those of target region') # Create interactive legend leg = ax.legend(loc=1, fontsize=fs, frameon=False) if blacklegend: for l in leg.get_lines(): l.set_color('k') if straightlegend: for l in leg.get_lines(): l.set_linestyle('-') interactive_legend = InteractiveLegend(ax.legend_, aliases) if showfig: plt.show() return fig def plotBatch(directory, filepaths, vars_dict=None, plt_show=True, plt_save=False, ask_before_save=True, fig_ext='png', tag='fig', fs=15, lw=2, title=True, show_patches=True): ''' Plot a figure with profiles of several specific NICE output variables, for several NICE simulations. :param positions: subplot indexes of each variable :param filepaths: list of full paths to output data files to be compared :param vars_dict: dict of lists of variables names to extract and plot together :param plt_show: boolean stating whether to show the created figures :param plt_save: boolean stating whether to save the created figures :param ask_before_save: boolean stating whether to show the created figures :param fig_ext: file extension for the saved figures :param tag: suffix added to the end of the figures name :param fs: labels font size :param lw: curves line width :param title: boolean stating whether to display a general title on the figures :param show_patches: boolean indicating whether to indicate periods of stimulation with colored rectangular patches ''' # Check validity of plot variables if vars_dict: yvars = list(sum(list(vars_dict.values()), [])) for key in yvars: if key not in pltvars: - raise InputError('Unknown plot variable: "{}"'.format(key)) + raise KeyError('Unknown plot variable: "{}"'.format(key)) # Dictionary of neurons neurons_dict = getNeuronsDict() # Loop through data files for filepath in filepaths: # Get code from file name pkl_filename = ntpath.basename(filepath) filecode = pkl_filename[0:-4] # Retrieve sim type mo1 = rgxp.fullmatch(pkl_filename) mo2 = rgxp_mech.fullmatch(pkl_filename) if mo1: mo = mo1 elif mo2: mo = mo2 else: logger.error('Error: "%s" file does not match regexp pattern', pkl_filename) sys.exit(1) sim_type = mo.group(1) if sim_type not in ('MECH', 'ASTIM', 'ESTIM'): - raise InputError('Invalid simulation type: {}'.format(sim_type)) + raise ValueError('Invalid simulation type: {}'.format(sim_type)) # Load data logger.info('Loading data from "%s"', pkl_filename) with open(filepath, 'rb') as fh: frame = pickle.load(fh) df = frame['data'] meta = frame['meta'] # Extract variables logger.info('Extracting variables') t = df['t'].values states = df['states'].values nsamples = t.size # Initialize channel mechanism if sim_type in ['ASTIM', 'ESTIM']: neuron_name = mo.group(2) global neuron neuron = neurons_dict[neuron_name]() neuron_states = [df[sn].values for sn in neuron.states_names] Cm0 = neuron.Cm0 Qm0 = Cm0 * neuron.Vm0 * 1e-3 t_plt = pltvars['t_ms'] else: Cm0 = meta['Cm0'] Qm0 = meta['Qm0'] t_plt = pltvars['t_us'] # Initialize BLS if sim_type in ['MECH', 'ASTIM']: global bls Fdrive = meta['Fdrive'] a = meta['a'] bls = BilayerSonophore(a, Cm0, Qm0) # Determine patches location npatches, tpatch_on, tpatch_off = getPatchesLoc(t, states) # Adding onset to time vector if t_plt['onset'] > 0.0: tonset = np.array([-t_plt['onset'], -t[0] - t[1]]) t = np.hstack((tonset, t)) states = np.hstack((states, np.zeros(2))) # Determine variables to plot if not provided if not vars_dict: if sim_type == 'ASTIM': vars_dict = {'Z': ['Z'], 'Q_m': ['Qm']} elif sim_type == 'ESTIM': vars_dict = {'V_m': ['Vm']} elif sim_type == 'MECH': vars_dict = {'P_{AC}': ['Pac'], 'Z': ['Z'], 'n_g': ['ng']} if sim_type in ['ASTIM', 'ESTIM'] and hasattr(neuron, 'pltvars_scheme'): vars_dict.update(neuron.pltvars_scheme) labels = list(vars_dict.keys()) naxes = len(vars_dict) # Plotting if naxes == 1: _, ax = plt.subplots(figsize=(11, 4)) axes = [ax] else: _, axes = plt.subplots(naxes, 1, figsize=(11, min(3 * naxes, 9))) for i in range(naxes): ax = axes[i] for item in ['top', 'right']: ax.spines[item].set_visible(False) ax_pltvars = [pltvars[j] for j in vars_dict[labels[i]]] nvars = len(ax_pltvars) # X-axis if i < naxes - 1: ax.get_xaxis().set_ticklabels([]) else: ax.set_xlabel('${}\ ({})$'.format(t_plt['label'], t_plt['unit']), fontsize=fs) for tick in ax.xaxis.get_major_ticks(): tick.label.set_fontsize(fs) # Y-axis if ax_pltvars[0]['unit']: ax.set_ylabel('${}\ ({})$'.format(labels[i], ax_pltvars[0]['unit']), fontsize=fs) else: ax.set_ylabel('${}$'.format(labels[i]), fontsize=fs) if 'min' in ax_pltvars[0] and 'max' in ax_pltvars[0]: ax_min = min([ap['min'] for ap in ax_pltvars]) ax_max = max([ap['max'] for ap in ax_pltvars]) ax.set_ylim(ax_min, ax_max) ax.locator_params(axis='y', nbins=2) for tick in ax.yaxis.get_major_ticks(): tick.label.set_fontsize(fs) # Time series icolor = 0 for j in range(nvars): # Extract variable pltvar = ax_pltvars[j] if 'alias' in pltvar: var = eval(pltvar['alias']) elif 'key' in pltvar: var = df[pltvar['key']].values elif 'constant' in pltvar: var = eval(pltvar['constant']) * np.ones(nsamples) else: var = df[vars_dict[labels[i]][j]].values if var.size == t.size - 2: if pltvar['desc'] == 'membrane potential': var = np.hstack((np.array([neuron.Vm0] * 2), var)) else: var = np.hstack((np.array([var[0]] * 2), var)) # var = np.insert(var, 0, var[0]) # Plot variable if 'constant' in pltvar or pltvar['desc'] in ['net current']: ax.plot(t * t_plt['factor'], var * pltvar['factor'], '--', c='black', lw=lw, label='${}$'.format(pltvar['label'])) else: ax.plot(t * t_plt['factor'], var * pltvar['factor'], c='C{}'.format(icolor), lw=lw, label='${}$'.format(pltvar['label'])) icolor += 1 # Patches if show_patches == 1: (ybottom, ytop) = ax.get_ylim() for j in range(npatches): ax.axvspan(tpatch_on[j] * t_plt['factor'], tpatch_off[j] * t_plt['factor'], edgecolor='none', facecolor='#8A8A8A', alpha=0.2) # Legend if nvars > 1: ax.legend(fontsize=fs, loc=7, ncol=nvars // 4 + 1) # Title if title: if sim_type == 'ESTIM': if meta['DC'] == 1.0: fig_title = ESTIM_CW_title.format(neuron.name, meta['Astim'], meta['tstim'] * 1e3) else: fig_title = ESTIM_PW_title.format(neuron.name, meta['Astim'], meta['tstim'] * 1e3, meta['PRF'], meta['DC'] * 1e2) elif sim_type == 'ASTIM': if meta['DC'] == 1.0: fig_title = ASTIM_CW_title.format(neuron.name, Fdrive * 1e-3, meta['Adrive'] * 1e-3, meta['tstim'] * 1e3) else: fig_title = ASTIM_PW_title.format(neuron.name, Fdrive * 1e-3, meta['Adrive'] * 1e-3, meta['tstim'] * 1e3, meta['PRF'], meta['DC'] * 1e2) elif sim_type == 'MECH': fig_title = MECH_title.format(a * 1e9, Fdrive * 1e-3, meta['Adrive'] * 1e-3) axes[0].set_title(fig_title, fontsize=fs) plt.tight_layout() # Save figure if needed (automatic or checked) if plt_save: if ask_before_save: plt_filename = SaveFigDialog(directory, '{}_{}.{}'.format(filecode, tag, fig_ext)) else: plt_filename = '{}/{}_{}.{}'.format(directory, filecode, tag, fig_ext) if plt_filename: plt.savefig(plt_filename) logger.info('Saving figure as "{}"'.format(plt_filename)) plt.close() # Show all plots if needed if plt_show: plt.show() def plotActivationMap(DCs, amps, actmap, FRlims, title=None, Ascale='log', FRscale='log', fs=8): ''' Plot a neuron's activation map over the amplitude x duty cycle 2D space. :param DCs: duty cycle vector :param amps: amplitude vector :param actmap: 2D activation matrix :param FRlims: lower and upper bounds of firing rate color-scale :param title: figure title :param Ascale: scale to use for the amplitude dimension ('lin' or 'log') :param FRscale: scale to use for the firing rate coloring ('lin' or 'log') :param fs: fontsize to use for the title and labels :return: 3-tuple with the handle to the generated figure and the mesh x and y coordinates ''' # Check firing rate bounding minFR, maxFR = (actmap[actmap > 0].min(), actmap.max()) logger.info('FR range: %.0f - %.0f Hz', minFR, maxFR) if minFR < FRlims[0]: logger.warning('Minimal firing rate (%.0f Hz) is below defined lower bound (%.0f Hz)', minFR, FRlims[0]) if maxFR > FRlims[1]: logger.warning('Maximal firing rate (%.0f Hz) is above defined upper bound (%.0f Hz)', maxFR, FRlims[1]) # Plot activation map if FRscale == 'lin': norm = matplotlib.colors.Normalize(*FRlims) elif FRscale == 'log': norm = matplotlib.colors.LogNorm(*FRlims) fig, ax = plt.subplots(figsize=cm2inch(8, 5.8)) fig.subplots_adjust(left=0.15, bottom=0.15, right=0.8, top=0.92) if title is not None: ax.set_title(title, fontsize=fs) if Ascale == 'log': ax.set_yscale('log') ax.set_xlabel('Duty cycle (%)', fontsize=fs, labelpad=-0.5) ax.set_ylabel('Amplitude (kPa)', fontsize=fs) ax.set_xlim(np.array([DCs.min(), DCs.max()]) * 1e2) for item in ax.get_xticklabels() + ax.get_yticklabels(): item.set_fontsize(fs) xedges = computeMeshEdges(DCs) yedges = computeMeshEdges(amps, scale='log') actmap[actmap == -1] = np.nan actmap[actmap == 0] = 1e-3 cmap = plt.get_cmap('viridis') cmap.set_bad('silver') cmap.set_under('k') ax.pcolormesh(xedges * 1e2, yedges * 1e-3, actmap, cmap=cmap, norm=norm) # Plot firing rate colorbar sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm) sm._A = [] pos1 = ax.get_position() # get the map axis position cbarax = fig.add_axes([pos1.x1 + 0.02, pos1.y0, 0.03, pos1.height]) fig.colorbar(sm, cax=cbarax) cbarax.set_ylabel('Firing rate (Hz)', fontsize=fs) for item in cbarax.get_yticklabels(): item.set_fontsize(fs) return (fig, xedges, yedges) def plotRawTrace(fpath, key, ybounds): ''' Plot the raw signal of a given variable within specified bounds. :param foath: full path to the data file :param key: key to the target variable :param ybounds: y-axis bounds :return: handle to the generated figure ''' # Check file existence fname = ntpath.basename(fpath) if not os.path.isfile(fpath): - raise InputError('Error: "{}" file does not exist'.format(fname)) + raise FileNotFoundError('Error: "{}" file does not exist'.format(fname)) # Load data logger.debug('Loading data from "%s"', fname) with open(fpath, 'rb') as fh: frame = pickle.load(fh) df = frame['data'] t = df['t'].values y = df[key].values * pltvars[key]['factor'] Δy = y.max() - y.min() logger.info('d%s = %.1f %s', key, Δy, pltvars[key]['unit']) # Plot trace fig, ax = plt.subplots(figsize=cm2inch(12.5, 5.8)) fig.canvas.set_window_title(fname) for s in ['top', 'bottom', 'left', 'right']: ax.spines[s].set_visible(False) ax.set_xticks([]) ax.set_yticks([]) ax.set_ylim(ybounds) ax.plot(t, y, color='k', linewidth=1) fig.tight_layout() return fig def plotTraces(fpath, keys, tbounds): ''' Plot the raw signal of sevral variables within specified bounds. :param foath: full path to the data file :param key: key to the target variable :param tbounds: x-axis bounds :return: handle to the generated figure ''' # Check file existence fname = ntpath.basename(fpath) if not os.path.isfile(fpath): - raise InputError('Error: "{}" file does not exist'.format(fname)) + raise FileNotFoundError('Error: "{}" file does not exist'.format(fname)) # Load data logger.debug('Loading data from "%s"', fname) with open(fpath, 'rb') as fh: frame = pickle.load(fh) df = frame['data'] t = df['t'].values * 1e3 # Plot trace fs = 8 fig, ax = plt.subplots(figsize=cm2inch(7, 3)) fig.canvas.set_window_title(fname) plt.subplots_adjust(left=0.2, bottom=0.2, right=0.95, top=0.95) for s in ['top', 'right']: ax.spines[s].set_visible(False) for s in ['bottom', 'left']: ax.spines[s].set_position(('axes', -0.03)) ax.spines[s].set_linewidth(2) ax.yaxis.set_tick_params(width=2) # ax.spines['bottom'].set_linewidth(2) ax.set_xlim(tbounds) ax.set_xticks([]) ymin = np.nan ymax = np.nan dt = tbounds[1] - tbounds[0] ax.set_xlabel('{}s'.format(si_format(dt * 1e-3, space=' ')), fontsize=fs) ax.set_ylabel('mV - $\\rm nC/cm^2$', fontsize=fs, labelpad=-15) colors = {'Vm': 'darkgrey', 'Qm': 'k'} for key in keys: y = df[key].values * pltvars[key]['factor'] ymin = np.nanmin([ymin, y.min()]) ymax = np.nanmax([ymax, y.max()]) # if key == 'Qm': # y0 = y[0] # ax.plot(t, y0 * np.ones(t.size), '--', color='k', linewidth=1) Δy = y.max() - y.min() logger.info('d%s = %.1f %s', key, Δy, pltvars[key]['unit']) ax.plot(t, y, color=colors[key], linewidth=1) ax.yaxis.set_major_formatter(FormatStrFormatter('%.0f')) # ax.set_yticks([ymin, ymax]) ax.set_ylim([-200, 100]) ax.set_yticks([-200, 100]) for item in ax.get_yticklabels(): item.set_fontsize(fs) # fig.tight_layout() return fig def plotSignals(t, signals, states=None, ax=None, onset=None, lbls=None, fs=10, cmode='qual'): ''' Plot several signals on one graph. :param t: time vector :param signals: list of signal vectors :param states (optional): stimulation state vector :param ax (optional): handle to figure axis :param onset (optional): onset to add to signals on graph :param lbls (optional): list of legend labels :param fs (optional): font size to use on graph :param cmode: color mode ('seq' for sequentiual or 'qual' for qualitative) :return: figure handle (if no axis provided as input) ''' # If no axis provided, create figure if ax is None: fig, ax = plt.subplots(figsize=(8, 3)) argout = fig else: argout = None # Set axis aspect ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) for item in ax.get_xticklabels() + ax.get_xticklabels(): item.set_fontsize(fs) # Compute number of signals nsignals = len(signals) # Adapt labels for sequential color mode if cmode == 'seq' and lbls is not None: lbls[1:-1] = ['.'] * (nsignals - 2) # Add stimulation patches if states provided if states is not None: npatches, tpatch_on, tpatch_off = getPatchesLoc(t, states) for i in range(npatches): ax.axvspan(tpatch_on[i], tpatch_off[i], edgecolor='none', facecolor='#8A8A8A', alpha=0.2) # Add onset of provided if onset is not None: t0, y0 = onset t = np.hstack((np.array([t0, 0.]), t)) signals = np.hstack((np.ones((nsignals, 2)) * y0, signals)) # Determine colorset nlevels = nsignals if cmode == 'seq': norm = matplotlib.colors.Normalize(0, nlevels - 1) sm = cm.ScalarMappable(norm=norm, cmap=cm.get_cmap('viridis')) sm._A = [] colors = [sm.to_rgba(i) for i in range(nlevels)] elif cmode == 'qual': nlevels_max = 10 if nlevels > nlevels_max: raise Warning('Number of signals higher than number of color levels') colors = ['C{}'.format(i) for i in range(nlevels)] else: - raise InputError('Unknown color mode') + raise ValueError('Unknown color mode') # Plot signals for i, var in enumerate(signals): ax.plot(t, var, label=lbls[i] if lbls is not None else None, c=colors[i]) # Add legend if lbls is not None: ax.legend(fontsize=fs, frameon=False) return argout diff --git a/PySONIC/utils.py b/PySONIC/utils.py index e946fb6..90de36b 100644 --- a/PySONIC/utils.py +++ b/PySONIC/utils.py @@ -1,565 +1,494 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # @Author: Theo Lemaire # @Date: 2016-09-19 22:30:46 # @Email: theo.lemaire@epfl.ch # @Last Modified by: Theo Lemaire -# @Last Modified time: 2018-09-24 21:43:11 +# @Last Modified time: 2018-09-24 23:53:10 """ Definition of generic utility functions used in other modules """ -from enum import Enum import operator import os import pickle import tkinter as tk from tkinter import filedialog import lockfile import shutil -import yaml from openpyxl import load_workbook import numpy as np import colorlog from scipy.interpolate import interp1d def setLogger(): log_formatter = colorlog.ColoredFormatter( '%(log_color)s %(asctime)s %(message)s', datefmt='%d/%m/%Y %H:%M:%S:', reset=True, log_colors={ 'DEBUG': 'green', 'INFO': 'white', 'WARNING': 'yellow', 'ERROR': 'red', 'CRITICAL': 'red,bg_white', }, style='%' ) log_handler = colorlog.StreamHandler() log_handler.setFormatter(log_formatter) color_logger = colorlog.getLogger('PySONIC') color_logger.addHandler(log_handler) return color_logger # Get package logger logger = setLogger() -class InputError(Exception): - ''' Exception raised for errors in the input. ''' - pass - - -class PmCompMethod(Enum): - """ Enum: types of computation method for the intermolecular pressure """ - direct = 1 - predict = 2 - - -def loadYAML(filepath): - """ Load a dictionary of parameters from an external YAML file. - - :param filepath: full path to the YAML file - :return: parameters dictionary - """ - - _, filename = os.path.split(filepath) - logger.debug('Loading parameters from "%s"', filename) - with open(filepath, 'r') as f: - stream = f.read() - params = yaml.load(stream) - return ParseNestedDict(params) - - -def ParseNestedDict(dict_in): - """ Loop through a nested dictionary object and convert all string fields - to floats. - """ - for key, value in dict_in.items(): - if isinstance(value, dict): # If value itself is dictionary - dict_in[key] = ParseNestedDict(value) - elif isinstance(dict_in[key], str): - dict_in[key] = float(dict_in[key]) - return dict_in - - def OpenFilesDialog(filetype, dirname=''): """ Open a FileOpenDialogBox to select one or multiple file. The default directory and file type are given. :param dirname: default directory :param filetype: default file type :return: tuple of full paths to the chosen filenames """ root = tk.Tk() root.withdraw() filenames = filedialog.askopenfilenames(filetypes=[(filetype + " files", '.' + filetype)], initialdir=dirname) if filenames: par_dir = os.path.abspath(os.path.join(filenames[0], os.pardir)) else: par_dir = None return (filenames, par_dir) def selectDirDialog(): """ Open a dialog box to select a directory. :return: full path to selected directory """ root = tk.Tk() root.withdraw() return filedialog.askdirectory() def ImportExcelCol(filename, sheetname, colstr, startrow): """ Load a specific column of an excel workbook as a numpy array. :param filename: absolute or relative path to the Excel workbook :param sheetname: name of the Excel spreadsheet to which data is appended :param colstr: string of the column to import :param startrow: index of the first row to consider :return: 1D numpy array with the column data """ wb = load_workbook(filename, read_only=True) ws = wb[sheetname] range_start_str = colstr + str(startrow) range_stop_str = colstr + str(ws.max_row) tmp = np.array([[i.value for i in j] for j in ws[range_start_str:range_stop_str]]) return tmp[:, 0] def ConstructMatrix(serialized_inputA, serialized_inputB, serialized_output): """ Construct a 2D output matrix from serialized input. :param serialized_inputA: serialized input variable A :param serialized_inputB: serialized input variable B :param serialized_output: serialized output variable :return: 4-tuple with vectors of unique values of A (m) and B (n), output variable 2D matrix (m,n) and number of holes in the matrix """ As = np.unique(serialized_inputA) Bs = np.unique(serialized_inputB) nA = As.size nB = Bs.size output = np.zeros((nA, nB)) output[:] = np.NAN nholes = 0 for i in range(nA): iA = np.where(serialized_inputA == As[i]) for j in range(nB): iB = np.where(serialized_inputB == Bs[j]) iMatch = np.intersect1d(iA, iB) if iMatch.size == 0: nholes += 1 elif iMatch.size > 1: logger.warning('Identical serialized inputs with values (%f, %f)', As[i], Bs[j]) else: iMatch = iMatch[0] output[i, j] = serialized_output[iMatch] return (As, Bs, output, nholes) def rmse(x1, x2): """ Compute the root mean square error between two 1D arrays """ return np.sqrt(((x1 - x2) ** 2).mean()) def rsquared(x1, x2): ''' compute the R-squared coefficient between two 1D arrays ''' residuals = x1 - x2 ss_res = np.sum(residuals**2) ss_tot = np.sum((x1 - np.mean(x1))**2) return 1 - (ss_res / ss_tot) def DownSample(t_dense, y, nsparse): """ Decimate periodic signals to a specified number of samples.""" if(y.ndim) > 1: nsignals = y.shape[0] else: nsignals = 1 y = np.array([y]) # determine time step and period of input signal T = t_dense[-1] - t_dense[0] dt_dense = t_dense[1] - t_dense[0] # resample time vector linearly t_ds = np.linspace(t_dense[0], t_dense[-1], nsparse) # create MAV window nmav = int(0.03 * T / dt_dense) if nmav % 2 == 0: nmav += 1 mav = np.ones(nmav) / nmav # determine signals padding npad = int((nmav - 1) / 2) # determine indexes of sampling on convolved signals ids = np.round(np.linspace(0, t_dense.size - 1, nsparse)).astype(int) y_ds = np.empty((nsignals, nsparse)) # loop through signals for i in range(nsignals): # pad, convolve and resample pad_left = y[i, -(npad + 2):-2] pad_right = y[i, 1:npad + 1] y_ext = np.concatenate((pad_left, y[i, :], pad_right), axis=0) y_mav = np.convolve(y_ext, mav, mode='valid') y_ds[i, :] = y_mav[ids] if nsignals == 1: y_ds = y_ds[0, :] return (t_ds, y_ds) def Pressure2Intensity(p, rho=1075.0, c=1515.0): """ Return the spatial peak, pulse average acoustic intensity (ISPPA) associated with the specified pressure amplitude. :param p: pressure amplitude (Pa) :param rho: medium density (kg/m3) :param c: speed of sound in medium (m/s) :return: spatial peak, pulse average acoustic intensity (W/m2) """ return p**2 / (2 * rho * c) def Intensity2Pressure(I, rho=1075.0, c=1515.0): """ Return the pressure amplitude associated with the specified spatial peak, pulse average acoustic intensity (ISPPA). :param I: spatial peak, pulse average acoustic intensity (W/m2) :param rho: medium density (kg/m3) :param c: speed of sound in medium (m/s) :return: pressure amplitude (Pa) """ return np.sqrt(2 * rho * c * I) def find_nearest(array, value): ''' Find nearest element in 1D array. ''' idx = (np.abs(array - value)).argmin() return (idx, array[idx]) def rescale(x, lb=None, ub=None, lb_new=0, ub_new=1): ''' Rescale a value to a specific interval by linear transformation. ''' if lb is None: lb = x.min() if ub is None: ub = x.max() xnorm = (x - lb) / (ub - lb) return xnorm * (ub_new - lb_new) + lb_new def LennardJones(x, beta, alpha, C, m, n): """ Generic expression of a Lennard-Jones function, adapted for the context of symmetric deflection (distance = 2x). :param x: deflection (i.e. half-distance) :param beta: x-shifting factor :param alpha: x-scaling factor :param C: y-scaling factor :param m: exponent of the repulsion term :param n: exponent of the attraction term :return: Lennard-Jones potential at given distance (2x) """ return C * (np.power((alpha / (2 * x + beta)), m) - np.power((alpha / (2 * x + beta)), n)) def extractCompTimes(filenames): ''' Extract computation times from a list of simulation files. ''' tcomps = np.empty(len(filenames)) for i, fn in enumerate(filenames): logger.info('Loading data from "%s"', fn) with open(fn, 'rb') as fh: frame = pickle.load(fh) meta = frame['meta'] tcomps[i] = meta['tcomp'] return tcomps def computeMeshEdges(x, scale='lin'): ''' Compute the appropriate edges of a mesh that quads a linear or logarihtmic distribution. :param x: the input vector :param scale: the type of distribution ('lin' for linear, 'log' for logarihtmic) :return: the edges vector ''' if scale is 'log': x = np.log10(x) dx = x[1] - x[0] if scale is 'lin': y = np.linspace(x[0] - dx / 2, x[-1] + dx / 2, x.size + 1) elif scale is 'log': y = np.logspace(x[0] - dx / 2, x[-1] + dx / 2, x.size + 1) return y si_prefixes = { 'y': 1e-24, # yocto 'z': 1e-21, # zepto 'a': 1e-18, # atto 'f': 1e-15, # femto 'p': 1e-12, # pico 'n': 1e-9, # nano 'u': 1e-6, # micro 'm': 1e-3, # mili '': 1e0, # None 'k': 1e3, # kilo 'M': 1e6, # mega 'G': 1e9, # giga 'T': 1e12, # tera 'P': 1e15, # peta 'E': 1e18, # exa 'Z': 1e21, # zetta 'Y': 1e24, # yotta } def si_format(x, precision=0, space=''): ''' Format a float according to the SI unit system, with the appropriate prefix letter. ''' if isinstance(x, float) or isinstance(x, int) or isinstance(x, np.float) or\ isinstance(x, np.int32) or isinstance(x, np.int64): if x == 0: factor = 1e0 prefix = '' else: sorted_si_prefixes = sorted(si_prefixes.items(), key=operator.itemgetter(1)) vals = [tmp[1] for tmp in sorted_si_prefixes] # vals = list(si_prefixes.values()) ix = np.searchsorted(vals, np.abs(x)) - 1 if np.abs(x) == vals[ix + 1]: ix += 1 factor = vals[ix] prefix = sorted_si_prefixes[ix][0] # prefix = list(si_prefixes.keys())[ix] return '{{:.{}f}}{}{}'.format(precision, space, prefix).format(x / factor) elif isinstance(x, list) or isinstance(x, tuple): return [si_format(item, precision, space) for item in x] elif isinstance(x, np.ndarray) and x.ndim == 1: return [si_format(float(item), precision, space) for item in x] else: print(type(x)) def getCycleAverage(t, y, T): ''' Compute the cycle-averaged profile of a signal given a specific periodicity. :param t: time vector (s) :param y: signal vector :param T: period (s) :return: cycle-averaged signal vector ''' nsamples = y.size ncycles = int((t[-1] - t[0]) // T) npercycle = int(nsamples // ncycles) return np.mean(np.reshape(y[:ncycles * npercycle], (ncycles, npercycle)), axis=1) def getNeuronLookupsFile(mechname): return os.path.join( os.path.split(__file__)[0], 'neurons', '{}_lookups.pkl'.format(mechname)) def getLookups2D(mechname, a, Fdrive): ''' Retrieve appropriate 2D lookup tables and reference vectors for a given membrane mechanism, sonophore diameter and US frequency. :param mechname: name of membrane density mechanism :param a: sonophore diameter (m) :param Fdrive: US frequency (Hz) :return: 3-tuple with 1D numpy arrays of reference acoustic amplitudes and charge densities, and a dictionary of 2D lookup numpy arrays ''' # Check lookup file existence lookup_path = getNeuronLookupsFile(mechname) if not os.path.isfile(lookup_path): raise FileNotFoundError('Missing lookup file: "{}"'.format(lookup_path)) # Load lookups dictionary logger.debug('Loading lookup table') with open(lookup_path, 'rb') as fh: lookups4D = pickle.load(fh) # Retrieve 1D inputs from lookups dictionary aref = lookups4D.pop('a') Fref = lookups4D.pop('f') Aref = lookups4D.pop('A') Qref = lookups4D.pop('Q') # Check that sonophore diameter is within lookup range arange = (aref.min() - 1e-12, aref.max() + 1e-12) if a < arange[0] or a > arange[1]: raise ValueError('Invalid sonophore diameter: {}m (must be within {}m - {}m lookup interval)' .format(*si_format([a, *arange], precision=2, space=' '))) # Check that US frequency is within lookup range Frange = (Fref.min() - 1e-9, Fref.max() + 1e-9) if Fdrive < Frange[0] or Fdrive > Frange[1]: raise ValueError('Invalid frequency: {}Hz (must be within {}Hz - {}Hz lookup interval)' .format(*si_format([Fdrive, *Frange], precision=2, space=' '))) # Interpolate 4D lookups at sonophore diameter and then at US frequency logger.debug('Interpolating lookups at a = {}m'.format(si_format(a, space=' '))) lookups3D = {key: interp1d(aref, y4D, axis=0)(a) for key, y4D in lookups4D.items()} logger.debug('Interpolating lookups at f = {}Hz'.format(si_format(Fdrive, space=' '))) lookups2D = {key: interp1d(Fref, y3D, axis=0)(Fdrive) for key, y3D in lookups3D.items()} return Aref, Qref, lookups2D -def nDindexes(dims, index): - ''' Find index positions in a n-dimensional array. - - :param dims: dimensions of the n-dimensional array (tuple or list) - :param index: index position in the flattened n-dimensional array - :return: list of indexes along each array dimension - ''' - - dims = list(dims) - - # Find size of each array dimension - dims.reverse() - dimsizes = [1] - r = 1 - for x in dims[:-1]: - r *= x - dimsizes.append(r) - dims.reverse() - dimsizes.reverse() - - # Find indexes - indexes = [] - remainder = index - for dimsize in dimsizes[:-1]: - idim, remainder = divmod(remainder, dimsize) - indexes.append(idim) - indexes.append(remainder) - - return indexes - - def pow10_format(number, precision=2): ''' Format a number in power of 10 notation. ''' ret_string = '{0:.{1:d}e}'.format(number, precision) a, b = ret_string.split("e") a = float(a) b = int(b) return '{}10^{{{}}}'.format('{} * '.format(a) if a != 1. else '', b) def checkNumBounds(values, bounds): ''' Check if a set of numbers is within predefined bounds. ''' # checking parameters against reference bounds for x, bound in zip(values, bounds): if x < bound[0] or x > bound[1]: raise ValueError('Input value {} out of [{}, {}] range'.format(x, bound[0], bound[1])) pass def getDefaultIndexes(params, defaults): ''' Return the indexes of default values found in lists of parameters. :param params: dictionary of parameter arrays :param defaults: dictionary of default values :return: dictionary of resolved default indexes ''' idefs = {} for key, default in defaults.items(): if default not in params[key]: raise Exception('default {} ({}) not found in parameter values'.format(key, default)) idefs[key] = np.where(params[key] == default)[0][0] return idefs def xlslog(filename, sheetname, data): """ Append log data on a new row to specific sheet of excel workbook, using a lockfile to avoid read/write errors between concurrent processes. :param filename: absolute or relative path to the Excel workbook :param sheetname: name of the Excel spreadsheet to which data is appended :param data: data structure to be added to specific columns on a new row :return: boolean indicating success (1) or failure (0) of operation """ try: lock = lockfile.FileLock(filename) lock.acquire() wb = load_workbook(filename) ws = wb[sheetname] keys = data.keys() i = 1 row_data = {} for k in keys: row_data[k] = data[k] i += 1 ws.append(row_data) wb.save(filename) lock.release() return 1 except PermissionError: # If file cannot be accessed for writing because already opened logger.warning('Cannot write to "%s". Close the file and type "Y"', filename) user_str = input() if user_str in ['y', 'Y']: return xlslog(filename, sheetname, data) else: return 0 def checkBatchLog(batch_dir, batch_type): ''' Check for appropriate log file in batch directory, and create one if it is absent. :param batch_dir: full path to batch directory :param batch_type: type of simulation batch :return: 2 tuple with full path to log file and boolean stating if log file was created ''' # Check for directory existence if not os.path.isdir(batch_dir): raise IOError('"{}" output directory does not exist'.format(batch_dir)) # Determine log template from batch type if batch_type == 'MECH': logfile = 'log_MECH.xlsx' elif batch_type == 'A-STIM': logfile = 'log_ASTIM.xlsx' elif batch_type == 'E-STIM': logfile = 'log_ESTIM.xlsx' else: - raise InputError('Unknown batch type', batch_type) + raise ValueError('Unknown batch type', batch_type) # Get template in package subdirectory this_dir, _ = os.path.split(__file__) # parent_dir = os.path.abspath(os.path.join(this_dir, os.pardir)) logsrc = this_dir + '/templates/' + logfile assert os.path.isfile(logsrc), 'template log file "{}" not found'.format(logsrc) # Copy template in batch directory if no appropriate log file logdst = batch_dir + '/' + logfile is_log = os.path.isfile(logdst) if not is_log: shutil.copy2(logsrc, logdst) return (logdst, not is_log) diff --git a/notebooks/BLS model - intermolecular pressure.ipynb b/notebooks/BLS model - intermolecular pressure.ipynb new file mode 100644 index 0000000..c6cd40d --- /dev/null +++ b/notebooks/BLS model - intermolecular pressure.ipynb @@ -0,0 +1,209 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Bilayer Sonophore model: computation of intermolecular pressure" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Imports" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import logging\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "from PySONIC.utils import logger, rmse, rsquared\n", + "from PySONIC.neurons import CorticalRS\n", + "from PySONIC.core import BilayerSonophore, PmCompMethod\n", + "from PySONIC.constants import *\n", + "\n", + "# Set logging level\n", + "logger.setLevel(logging.INFO)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Functions" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def plotPmavg(bls, Z, fs=15):\n", + " fig, ax = plt.subplots(figsize=(5, 3))\n", + " for skey in ['right', 'top']:\n", + " ax.spines[skey].set_visible(False)\n", + " ax.set_xlabel('Z (nm)', fontsize=fs)\n", + " ax.set_ylabel('Pressure (kPa)', fontsize=fs)\n", + " ax.set_xticks([0, bls.a * 1e9])\n", + " ax.set_xticklabels(['0', 'a'])\n", + " ax.set_yticks([-10, 0, 40])\n", + " ax.set_ylim([-10, 50])\n", + " for item in ax.get_xticklabels() + ax.get_yticklabels():\n", + " item.set_fontsize(fs)\n", + " ax.plot(Z * 1e9, bls.v_PMavg(Z, bls.v_curvrad(Z), bls.surface(Z)) * 1e-3, label='$P_m$')\n", + " ax.plot(Z * 1e9, bls.PMavgpred(Z) * 1e-3, label='$P_{m,approx}$')\n", + " ax.axhline(y=0, color='k')\n", + " ax.legend(fontsize=fs, frameon=False)\n", + " fig.tight_layout()\n", + "\n", + "def plotZprofiles(bls, f, A, Q, fs=15):\n", + " # Run simulation with integrated intermolecular pressure\n", + " _, y, _ = bls.simulate(f, A, Qm, Pm_comp_method=PmCompMethod.direct)\n", + " Z1, _ = y[:, -NPC_FULL:] * 1e9 # nm\n", + "\n", + " # Run simulation with predicted intermolecular pressure\n", + " _, y, _ = bls.simulate(f, A, Qm, Pm_comp_method=PmCompMethod.predict)\n", + " Z2, _ = y[:, -NPC_FULL:] * 1e9 # nm\n", + " \n", + " # Plot figure \n", + " t = np.linspace(0, 1 / f, NPC_FULL) * 1e6 # us\n", + " fig, ax = plt.subplots(figsize=(5, 3))\n", + " for skey in ['right', 'top']:\n", + " ax.spines[skey].set_visible(False)\n", + " ax.set_xlabel('time (us)', fontsize=fs)\n", + " ax.set_ylabel('Deflection (nm)', fontsize=fs)\n", + " ax.set_xticks([t[0], t[-1]])\n", + " for item in ax.get_xticklabels() + ax.get_yticklabels():\n", + " item.set_fontsize(fs)\n", + " \n", + " ax.plot(t, Z1, label='$P_m$')\n", + " ax.plot(t, Z2, label='$P_{m,approx}$')\n", + " ax.axhline(y=0, color='k')\n", + " ax.legend(fontsize=fs, frameon=False)\n", + " fig.tight_layout()\n", + " \n", + " return fig, Z1, Z2 " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "neuron = CorticalRS()\n", + "Qm0 = neuron.Cm0 * neuron.Vm0 * 1e-3 # C/m2\n", + "bls = BilayerSonophore(32e-9, neuron.Cm0, Qm0)\n", + "f = 500e3\n", + "A = 100e3\n", + "Qm = Qm0" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Profiles comparison over deflection range " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "Z = np.linspace(-0.4 * bls.Delta_, bls.a, 1000)\n", + "fig = plotPmavg(bls, Z)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Error quantification over a typical acoustic cycle" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Z-error: R2 = 0.9996, RMSE = 0.0454 nm (0.8212% dZ)\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "fig, Z1, Z2 = plotZprofiles(bls, f, A, Qm)\n", + "error_Z = rmse(Z1, Z2)\n", + "r2_Z = rsquared(Z1, Z2)\n", + "print('Z-error: R2 = {:.4f}, RMSE = {:.4f} nm ({:.4f}% dZ)'.format(\n", + " r2_Z, error_Z, error_Z / (Z1.max() - Z1.min()) * 1e2))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/BLS model - static forces.ipynb b/notebooks/BLS model - static forces.ipynb index 9e88e53..9504909 100644 --- a/notebooks/BLS model - static forces.ipynb +++ b/notebooks/BLS model - static forces.ipynb @@ -1,439 +1,438 @@ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Bilayer Sonophore model: analysis of static pressure forces\n", "\n", "### Imports" ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import time\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "from matplotlib import ticker\n", "\n", - "from PySONIC.core import BilayerSonophore\n", - "from PySONIC.utils import PmCompMethod" + "from PySONIC.core import BilayerSonophore, PmCompMethod" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Parameters" ] }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "a = 32e-9 # in-plane radius (m)\n", "Cm0 = 1e-2 # membrane resting capacitance (F/m2)\n", "Qm0 = -71.9e-5 # membrane resting charge density (C/m2)\n", "bls = BilayerSonophore(a, Cm0, Qm0)\n", "Z = np.linspace(-0.45 * bls.Delta, 2 * bls.a, 3000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Functions" ] }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "def prepareAxis(ax, xlabel, fs):\n", " if xlabel == 'Leaflet deflection (nm)':\n", " ax.set_xticks([-0.5 * bls.Delta * 1e9, bls.a * 1e9])\n", " ax.set_xticklabels(['$-\\Delta / 2$', 'a'])\n", " ax.axvline(-0.5 * bls.Delta * 1e9, linestyle='--', color='dimgrey')\n", " ax.axvline(bls.a * 1e9, linestyle='--', color='dimgrey')\n", " ax.yaxis.set_major_locator(ticker.MaxNLocator(2))\n", " ax.axhline(0.0, color='k', linewidth=1)\n", " else:\n", " ax.set_yticks([-0.5 * bls.Delta * 1e9, 0])\n", " ax.set_yticklabels(['$-\\Delta / 2$', '0'])\n", " ax.axhline(-0.5 * bls.Delta * 1e9, linestyle='--', color='dimgrey')\n", " ax.axhline(0, linestyle='--', color='dimgrey')\n", " ax.xaxis.set_major_locator(ticker.MaxNLocator(2))\n", " ax.axvline(0.0, color='k', linewidth=1)\n", " ax.set_xlabel(xlabel, fontsize=fs)\n", " for key in ['top', 'right']:\n", " ax.spines[key].set_visible(False)\n", " \n", "\n", "def plotVars(yvars, labels, limits=None, sharex=False, unit=None, xvar=Z * 1e9, xlabel='Leaflet deflection (nm)', fs=12, lw=2):\n", " if not sharex:\n", " naxes = len(yvars)\n", " fig, axes = plt.subplots(1, naxes, figsize=(5 * naxes, 3))\n", " if naxes == 1:\n", " axes = [axes]\n", " for i, ax, in enumerate(axes):\n", " prepareAxis(ax, xlabel, fs)\n", " ax.set_ylabel(labels[i], fontsize=fs)\n", " ax.plot(xvar, yvars[i], linewidth=lw)\n", " if limits is not None:\n", " ax.set_ylim(limits[i])\n", " else:\n", " naxes = len(yvars)\n", " fig, ax = plt.subplots(1, 1, figsize=(5, 3))\n", " prepareAxis(ax, xlabel, fs)\n", " ax.set_ylabel(unit, fontsize=fs)\n", " for yvar, label in zip(yvars, labels):\n", " ax.plot(xvar, yvar, linewidth=lw, label=label)\n", " if limits is not None:\n", " ax.set_ylim(limits)\n", " ax.legend(fontsize=fs, loc='center right', bbox_to_anchor=(1.5, 0.5), frameon=False)\n", " fig.tight_layout()\n", " return fig" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Geometrical variables" ] }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ - "" + "" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "R = bls.v_curvrad(Z)\n", "S = bls.surface(Z)\n", "V = bls.volume(Z)\n", "\n", "fig = plotVars(\n", " [1 / R * 1e-9, S * 1e18, V * 1e27],\n", " ['Curvature ($nm^{-1}$)', 'Surface ($nm^2$)', 'Volume ($nm^3$)']\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- The **leaflet curvature** is negative for a compressed sonophore (Z < 0) and positive for a expanded sonophore (Z > 0). It reaches a maximum when the deflection equals the in-plane diameter of the sonophore\n", "- The **leaflet surface** and **sonophore volume** both increase with the deflection magnitude. Note that the relative surface increases is less pronounced than the relative volume increase" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Intermolecular pressure" ] }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ - "" + "" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "Pm_apex = np.array([bls.PMlocal(0.0, z, r) for z, r in zip(Z, R)])\n", "Pm_avg = bls.v_PMavg(Z, R, S)\n", "Pm_avg_predict = bls.PMavgpred(Z)\n", "\n", "fig = plotVars(\n", " [Pm_apex * 1e-3, Pm_avg * 1e-3, Pm_avg_predict * 1e-3],\n", " ['$P_{M, apex}$', '$\\overline{P_{M}}$', 'approximated $\\overline{P_{M}}$'],\n", " limits=(-20, 20),\n", " sharex=True,\n", " unit='Pressure (kPa)'\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- The **intermolecular pressure at the leaflet apex** follows a marked *Lennard-Jones* profile\n", "- The **average leaflet intermolecular pressure** profile is similar in nature but less marked than that of the apex pressure\n", "- The ***Lennard-Jones* approximation of the average leaflet intermolecular pressure** is quite accurate" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Electrical pressure" ] }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ - "" + "" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "PQ_Qm0 = bls.Pelec(Z, bls.Qm0)\n", "PQ_0 = bls.Pelec(Z, 0)\n", "PQ_AP = bls.Pelec(Z, 30.0e-5)\n", "\n", "fig = plotVars(\n", " [PQ_Qm0 * 1e-3, PQ_0 * 1e-3, PQ_AP * 1e-3],\n", " ['$P_{Q, -71.9\\ nC/cm^2}$', '$P_{Q, 0\\ nC/cm^2}$', '$P_{Q, +30\\ nC/cm^2}$'],\n", " sharex=True,\n", " unit='Pressure (kPa)'\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Electrical pressure** is null when the membrane is not charged, and then increases in magnitude with the square of charge density and decreases as the inter-leaflet distance increases" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Gas pressure" ] }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ - "" + "" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "Pg_ng0 = bls.gasmol2Pa(bls.ng0, V)\n", "Pg_sparse = bls.gasmol2Pa(0.1 * bls.ng0, V)\n", "Pg_dense = bls.gasmol2Pa(10 * bls.ng0, V)\n", "\n", "fig = plotVars(\n", " np.array([Pg_ng0, Pg_sparse, Pg_dense]) * 1e-3,\n", " ['$P_{G, ng0}$', '$P_{G, 0.1 \\cdot ng0}$', '$P_{G, 10 \\cdot ng0}$'],\n", " sharex=True,\n", " unit='Pressure (kPa)',\n", " limits=(-10, 150)\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In th absence of gas transport, **gas pressure** increases here when the sonophore is compressed. However, in reality gas transport keeps the pressure around $P_0$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Elastic tension pressure" ] }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ - "" + "" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "Pel = bls.PEtot(Z, R)\n", "fig = plotVars(\n", " np.array([Pel]) * 1e-3,\n", " ['$P_{elastic}$ (kPa)']\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Elastic tension pressure** increases with the relative stretch of the sonpophore leaflets. Therefore, it is null at Z = 0 and increases during both sonophore compressions and expansions. Its effect is very limited during compressions, however it becomes quite significant during expansions phases." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Pressure balance" ] }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ - "" + "" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "P0 = np.ones(len(Z)) * bls.P0\n", "\n", "fig = plotVars(\n", " np.array([Pg_ng0, Pm_avg, Pel, PQ_Qm0, -P0]) * 1e-3,\n", " ['$P_{G, ng0}$', '$\\overline{P_{M}}$', '$P_{elastic}$', '$P_{Q, -71.9\\ nC/cm^2}$', '$P_0$'],\n", " limits=(-150, 150),\n", " sharex=True,\n", " unit='Pressure (kPa)'\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As expected, the **intermolecular pressure dominates during compressions**, while the **elastic tension pressure dominates for siginficant expansions**. **In between, the static and gas pressure dominate** together." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Balance quasi-steady deflection" ] }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ - "" + "" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "Pac = np.linspace(-1e5, 1e5, 100)\n", "Zqs = np.array([bls.balancedefQS(bls.ng0, bls.Qm0, pac, PmCompMethod.direct) for pac in Pac])\n", "\n", "fig = plotVars(\n", " [Zqs * 1e9],\n", " ['Balance deflection (nm)'],\n", " xvar=Pac * 1e-3,\n", " xlabel='Acoustic pressure (kPa)'\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As expected, a negative acoustic pressure drives the quasi-steady system towards a positive deflection, whereas a positive acoustic pressure drives the system towards comprression." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.6.4" } }, "nbformat": 4, "nbformat_minor": 2 } diff --git a/scripts/plot_batch.py b/scripts/plot_batch.py index ddd4e88..9e6fe10 100644 --- a/scripts/plot_batch.py +++ b/scripts/plot_batch.py @@ -1,41 +1,41 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # @Author: Theo Lemaire # @Date: 2017-03-20 12:19:55 # @Email: theo.lemaire@epfl.ch # @Last Modified by: Theo Lemaire -# @Last Modified time: 2018-09-24 21:05:28 +# @Last Modified time: 2018-09-24 23:52:19 """ Batch plot profiles of several specific output variables of NICE simulations. """ import logging -from PySONIC.utils import logger, OpenFilesDialog, InputError +from PySONIC.utils import logger, OpenFilesDialog from PySONIC.plt import plotBatch # Set logging level logger.setLevel(logging.INFO) defaults = dict( V_m=['Vm'], Q_m=['Qm'] ) def main(): # Select data files pkl_filepaths, pkl_dir = OpenFilesDialog('pkl') if not pkl_filepaths: logger.error('No input file') return # Plot profiles try: plotBatch(pkl_dir, pkl_filepaths, title=True, vars_dict=defaults) - except InputError as err: + except Exception as err: logger.error(err) if __name__ == '__main__': main() diff --git a/scripts/plot_comp.py b/scripts/plot_comp.py index 6665d82..c9406fa 100644 --- a/scripts/plot_comp.py +++ b/scripts/plot_comp.py @@ -1,39 +1,39 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # @Author: Theo Lemaire # @Date: 2017-02-13 12:41:26 # @Email: theo.lemaire@epfl.ch # @Last Modified by: Theo Lemaire -# @Last Modified time: 2018-09-24 21:07:21 +# @Last Modified time: 2018-09-24 23:52:32 """ Compare profiles of several specific output variables of NICE simulations. """ import logging -from PySONIC.utils import logger, OpenFilesDialog, InputError +from PySONIC.utils import logger, OpenFilesDialog from PySONIC.plt import plotComp # Set logging level logger.setLevel(logging.INFO) default = 'Qm' def main(): # Select data files pkl_filepaths, _ = OpenFilesDialog('pkl') if not pkl_filepaths: logger.error('No input file') return # Comparative plot try: plotComp(default, pkl_filepaths) - except InputError as err: + except Exception as err: logger.error(err) if __name__ == '__main__': main() diff --git a/tests/test_intermolecular_pressure.py b/tests/test_intermolecular_pressure.py deleted file mode 100644 index b58604b..0000000 --- a/tests/test_intermolecular_pressure.py +++ /dev/null @@ -1,76 +0,0 @@ -import logging -import numpy as np -import matplotlib -import matplotlib.pyplot as plt - -from PySONIC.utils import logger, si_format, PmCompMethod, rmse, rsquared -from PySONIC.plt import cm2inch -from PySONIC.neurons import CorticalRS -from PySONIC.core import BilayerSonophore -from PySONIC.constants import * - -# Set logging level -logger.setLevel(logging.INFO) - -# Plot parameters -matplotlib.rcParams['pdf.fonttype'] = 42 -matplotlib.rcParams['ps.fonttype'] = 42 -matplotlib.rcParams['font.family'] = 'arial' - -fs = 8 # font size -lw = 2 # linewidth -ps = 15 # scatter point size - -# Create standard bls object -neuron = CorticalRS() -A = 100e3 -f = 500e3 -a = 32e-9 -Cm0 = neuron.Cm0 -Qm0 = neuron.Vm0 * Cm0 * 1e-3 -Qm = Qm0 - -# Create sonophore object -bls = BilayerSonophore(a, Cm0, Qm0) - -# Compare profiles of direct and approximated intermolecular pressures along Z -Z = np.linspace(-0.4 * bls.Delta_, bls.a, 1000) -Pm_direct = bls.v_PMavg(Z, bls.v_curvrad(Z), bls.surface(Z)) -Pm_approx = bls.PMavgpred(Z) -fig, ax = plt.subplots(figsize=cm2inch(6, 5)) -for skey in ['right', 'top']: - ax.spines[skey].set_visible(False) -ax.set_xlabel('Z (nm)', fontsize=fs) -ax.set_ylabel('Pressure (kPa)', fontsize=fs) -ax.set_xticks([0, bls.a * 1e9]) -ax.set_xticklabels(['0', 'a']) -ax.set_yticks([-10, 0, 40]) -ax.set_ylim([-10, 50]) -for item in ax.get_xticklabels() + ax.get_yticklabels(): - item.set_fontsize(fs) -ax.plot(Z * 1e9, Pm_direct * 1e-3, label='$\mathregular{P_m}$') -ax.plot(Z * 1e9, Pm_approx * 1e-3, label='$\mathregular{P_{m,approx}}$') -ax.axhline(y=0, color='k') -ax.legend(fontsize=fs, frameon=False) -fig.tight_layout() - -# Run simulation with integrated intermolecular pressure -_, y, _ = bls.simulate(f, A, Qm, Pm_comp_method=PmCompMethod.direct) -Z1, _ = y[:, -NPC_FULL:] -deltaZ1 = Z1.max() - Z1.min() -logger.info('simulation with standard Pm: Zmin = %.2f nm, Zmax = %.2f nm, dZ = %.2f nm', - Z1.min() * 1e9, Z1.max() * 1e9, deltaZ1 * 1e9) - -# Run simulation with predicted intermolecular pressure -_, y, _ = bls.simulate(f, A, Qm, Pm_comp_method=PmCompMethod.predict) -Z2, _ = y[:, -NPC_FULL:] -deltaZ2 = Z2.max() - Z2.min() -logger.info('simulation with predicted Pm: Zmin = %.2f nm, Zmax = %.2f nm, dZ = %.2f nm', - Z2.min() * 1e9, Z2.max() * 1e9, deltaZ2 * 1e9) - -error_Z = rmse(Z1, Z2) -r2_Z = rsquared(Z1, Z2) -logger.info('Z-error: R2 = %.4f, RMSE = %.4f nm (%.4f%% dZ)', - r2_Z, error_Z * 1e9, error_Z / deltaZ1 * 1e2) - -plt.show()