def locate_pads_on_nerve(self): """ Find the locations in the nerve corresponding to the pads """ # Iterate over the ring's pads self.contact_points = [] for i_pad in range(self.npads): # The pad's angular position determines which cable cell(s) # is (are) stimulated # Select which of the targets is in contact # with a pad 'i_pad' according to its angular position angdiff = np.abs(ws.contour_angles - self.thts[i_pad]) which_cables = np.where(angdiff == angdiff.min())[0] # Iterate over possible cables ncables_here = len(which_cables) for i_cable in which_cables: # For cable_cell i, see where this is try: i_sec, zpos = anatomy.locate(ws.seclens[i_cable], self.z) except TypeError: msg = 'ERROR: A ring of an electrode could not be placed on the nerve' ws.log(msg) ws.terminate() # Add the point point = {'cable': i_cable, 'section': i_sec, 'z': zpos} try: self.contact_points[i_pad].append(point) except IndexError: self.contact_points.append([point])
def prepare_workspace(remove_previous=True): """ Prepare all the necessary information in the workspace to be able to do the necessary work, including running a simulation """ # Data management # Prepare the folders that the code will use get_paths() # Remove results from previous simulation(s) if remove_previous: als.remove_previous_results() # Create the log file create_log_file() # Pass a couple of functions to ws ws.log = log ws.terminate = terminate # Read all the settings from the json files read_settings() now = datetime.now() ws.log("Execution started on: %s" % now.strftime("%d %h %Y at %H:%M:%S")) # Set up some physical properties of the system from the settings physics() # Set up a simulation setup_simcontrol() # Miscellaneous variables miscellanea()
def square_pulses(injections_, point, protocol): """ Assign current injections to the pads """ # Injection segment in hoc seg = ws.sections[point['cable']][point['section']](point['z']) # IClamp object(s) for i, amp in enumerate(protocol['currents']): # Gather the data dur = protocol['pulse durations'][i] delay = protocol['pulse onset times'][i] # Create IClamp object and add its properties injection = h.IClamp(seg) # Intensity from uA to nA injection.amp = amp * 1.e3 injection.dur = dur injection.delay = delay # Add this information as an attribute injections_.append({ 'injection id': ws.counters['current injections'], 'point': point, 'amp': amp, 'dur': dur, 'delay': delay }) # Update the counter ws.counters['current injections'] += 1 # Add the duration and delay to ws.totaldur ws.totaldur = max(ws.totaldur, delay + dur) # Append it to ws ws.injections.append(injection) ws.injections_info = ws.injections_info + injections_ ws.log('Injection added to: %s, %s, %s, %s' % (str(point), str(amp), str(dur), str(delay)))
def __init__(self, name): self.name = name settings = ws.electrodes_settings[name] self.type = settings['type'] self.role = settings['role'] self.z = settings['z-position'] self.nrings = settings['number of rings'] self.rng_sp = settings['inter-ring distance'] self.nppr = settings['pads per ring'] self.length = settings['length'] self.thickness = settings['thickness'] self.rho = settings['insulator resistivity'] self.center = ws.nvt.circumcenter self.d_in = ws.nvt.circumdiameter self.d_out = self.d_in + 2. * self.thickness if self.role == 'stimulation': self.stimulation_protocol = \ settings['stimulation protocol'] # Necessary by-products # Ends of the cuff z = self.z l = self.length nrings = self.nrings left_end = z - 0.5 * l rght_end = z + 0.5 * l # Positions of the rings rings_halfspan = 0.5 * (nrings - 1) * self.rng_sp z_ring_left = z - rings_halfspan z_ring_rght = z + rings_halfspan self.rings_poss = np.linspace(z_ring_left, z_ring_rght, nrings) # Angular positions of the pads on the rings self.thts = settings['angular positions'] # Consistency check if len(self.thts) != self.nppr: msg = 'ERROR: Different number of pads and angles for them' ws.log(msg) ws.terminate() # Warning if size mismatch if z_ring_left < left_end or z_ring_rght > rght_end: msg = 'ERROR: Cuff electrode rings outside the cuff.' ws.log(msg) ws.terminate() # Store variables into attributes self.left_end = left_end self.rght_end = rght_end self.z_ring_left = z_ring_left self.z_ring_rght = z_ring_rght # Create rings self.create_rings()
def parameter_regressions(filename): """ Open and read a file with parameters over which to make regressions and obtain new expressions and values """ # Read data with open(filename, 'r') as f: frl = list(csv.reader(f)) keys = frl[0] data = np.array(frl[1:], dtype=np.float64) n_fields = len(keys) n_entries = len(data) data_dict = OrderedDict() for i in range(n_fields): data_dict[keys[i]] = data[:, i] # Independent variable indepvar = keys[0] # Dictionary containing the regressions regressions = OrderedDict() # Of course, indicate which one is the independent variable regressions['independent variable'] = indepvar for i, (k, v) in enumerate(list(data_dict.items())[1:]): x = data_dict[indepvar] # Linear regression slope, intercept, r_value, p_value, std_err = linregress(x, v) # Check if it's valid if intercept < 0: # Negative values, unacceptable. Use a polynomial fit # Quadratic fit parabolic_coeffs = np.polyfit(x, v, 2) if parabolic_coeffs[-1] < 0: # Not even a quadratic fit worked msg = 'ERROR: Could not obtain positive values for ' + k + ' for small ' + indepvar ws.log(msg) ws.terminate() # Give it a x-array that covers the whole domain x_ = np.linspace(0, x.max(), 100) ypol = polynomial(x_, parabolic_coeffs) # return polynomial regression a, b, c = parabolic_coeffs regressions[k] = lambda x: a * x**2 + b * x + c else: # Plot linear regression # Create straight line object and draw it sl = geo.StraightLine((0, intercept), slope) regressions[k] = sl.equation return regressions
def go_by_steps(): """ Main driver for the simulation """ initialize() # Loop over time steps for i_t, t in enumerate(ws.tarray): h.fadvance() # Rest for 500 ms to allow the computer to cool down if False: time.sleep(0.5) # Time control t1 = datetime.now() msg = "Time step %i of %i. Total elapsed time: %i seconds" % ( i_t, ws.nt, (t1 - t0).total_seconds()) # Log the time step into the simulation log file ws.log(msg)
def physics(): """ Set up some physical properties of the system from the settings """ # Length (cm) ws.length = ws.anatomy_settings['length'] # Internodal length to fiberD ratio # This will rarely be used in some models ws.ilD = ws.anatomy_settings['axons']['myelinated'][ 'internodal length to fiberD ratio'] # Temperature if False: h.celsius = ws.settings['temperature'] # Resistivities and thicknesses # Units: # Resistivity: Ohm*cm # Distance units: um # Resistivities ws.rho = {} # Thicknesses ws.th = {} # Nominal thickness of the perineurium (um) ws.th['perineurium'] = ws.tissues_settings['thickness']['perineurium'] # Resistivities of the tissues. They must be in MOhm*um # (convert from Ohm*cm) ws.rho['perineurium'] = ws.tissues_settings['resistivities'][ 'perineurium'] * 1.e-6 * 1.e4 ws.rho['epi_trans'] = ws.tissues_settings['resistivities']['epineurium'][ 'transverse'] * 1.e-6 * 1.e4 ws.rho['epi_longt'] = ws.tissues_settings['resistivities']['epineurium'][ 'longitudinal'] * 1.e-6 * 1.e4 ws.rho['endo_trans'] = ws.tissues_settings['resistivities']['endoneurium'][ 'transverse'] * 1.e-6 * 1.e4 ws.rho['endo_longt'] = ws.tissues_settings['resistivities']['endoneurium'][ 'longitudinal'] * 1.e-6 * 1.e4 # Perineurium resistance (MOhm*um2) ws.resist_perineurium = ws.rho['perineurium'] * ws.th['perineurium'] ws.log("Perineurium resistance %f MOhm*um2:" % ws.resist_perineurium) # INFINITE RESISTIVITY OF AN IDEALLY TOTALLY RESISTIVE MEDIUM ws.rho['SuperResistive'] = 1.e9
def save_electrode_recordings(): """ Save the recordings from all electrodes into files """ # Time tvarr = ws.tvec.as_numpy() # Iterate over electrodes: for name, electrode in ws.electrodes.items(): if electrode.role == 'recording': # Iterate over rings on the electrode for ring in electrode.rings: ring_number = ring.ring_number # Iterate over pads on the ring for pad, rec in ring.recordings.items(): # Recording ID rec_id = rec['recording id'] # Fetch the vector with the data from ws v = ws.recordings[rec_id] # Save with artefacts with open(os.path.join(ws.folders["data/results"], 'recordings_E_%s_R%iP%i_withartefacts.csv'\ %(name.replace(' ', '_'), ring_number, pad)), 'w') as f: fw = csv.writer(f) fw.writerow(['Time (ms)', 'V (mV)']) for tt, vv in zip(tvarr, v): fw.writerow([tt, vv]) # Remove the artefacts try: v = remove_artefacts(tvarr, v.as_numpy()) except Exception as e: message = "ERROR in %s.save_electrode_recordings:\n" % __name__ + type( e).__name__ + ':\n' + str(e) ws.log(message) # Don't terminate, just go on # ws.terminate() # Save with open(os.path.join(ws.folders["data/results"], 'recordings_E_%s_R%iP%i_noartefacts.csv'\ %(name.replace(' ', '_'), ring_number, pad)), 'w') as f: fw = csv.writer(f) fw.writerow(['Time (ms)', 'V (mV)']) for tt, vv in zip(tvarr, v): fw.writerow([tt, vv])
def __init__(self, points): # Validation if len(points) != 2: msg = 'WARNING: Defined a segment with %i points:\n%s\nSegment not created. Returned None' % ( len(points), str(points)) try: ws.log(msg) except AttributeError: print(msg) return None # Clean the points. Give them a nice and clean format points = (tuple([tls.list_to_number(x) for x in points[0]]), tuple([tls.list_to_number(x) for x in points[1]])) self.points_list = list(points) # Add attributes self.update(points)
def list_to_number(l): """ Given a list or a list of lists with ONLY ONE NUMBER INSIDE, retrieve that number. If there's more than one number, yield an error and quit """ # Check if it's a list or a number try: ll = len(l) except TypeError: # It's probably a number, so just return it as it is return float(l) l = np.array(l).flatten() if len(l) != 1: msg = 'ERROR: tools.list_to_number got a list with more than one number' ws.log(msg) ws.terminate() return float(l[0])
def __init__(self, name, index=0): PointElectrode.__init__(self, name, 0) settings = self.settings try: self.z = settings['z-position'] except KeyError: self.axon = settings['axon'] try: self.node = settings['node of Ranvier'] except KeyError: self.node = settings['internode'] self.position = settings['position'] else: # For a node of Ranvier, inject current in the middle self.position = 0.5 else: # Warning if position mismatch if self.z < 0. or self.z > ws.length: ws.log('ERROR: Electrode is outside the limits of the nerve.') ws.terminate() # Note: the warnings or errors for targetting a non-existing # node will be automatically raised by NEURON # Setting up this point is necessary to set up stimulation and # recording # Besides, it gives the process uniformity across # different types of electrodes cable_i = ws.nNAELC + self.axon k = 0 if "node of Ranvier" in settings.keys(): for j, sec in enumerate(ws.sections[cable_i]): secname = sec.name() if "NODE" in secname or "node" in secname: if k == self.node: break k += 1 self.point = {'cable': cable_i, 'section': j, 'z': self.position} self.set_stim_info()
def set_stimulation(self): """ Set up the current injections according to the assigned protocol """ # Dictionary containing the information about all # the current injections self.injections = {} # Injections list # There's only one 'pad' for an intracellular electrode self.injections[0] = [] # Fetch protocol protocol = self.stimulation_protocol # Create IClamp object(s) # Only square pulses are supported for now if protocol['type'] == 'square pulses': ws.log('Setting up injection on %s' % str(self.point)) square_pulses(self.injections[0], self.point, protocol) if protocol['type'] == 'ground': ws.log('Setting up ground on %s' % str(self.point)) ground_segment(self.point)
def sanity_checks(): """ Perform some checks to make sure that everything is defined correctly and do whatever is necessary if not """ # Using the Resistor Network model implies... if ws.settings["nerve model"] == "resistor network": # that there is ephaptic coupling if not ws.EC["presence"]: msg = "WARNING: Using the Resistor Network Model implies "\ + "the presence of ephaptic coupling\nEphaptic "\ + "coupling was activated" ws.log(msg) ws.EC["presence"] = True ws.EC["use"] = "resistor network" # that the stimulation forcing can't be "pre-computed" if ws.settings["stimulation"]["method"] == "pre-computed": msg = "ERROR: Using the Resistor Network Model implies "\ + "that the stimulation method can't be pre-computed\n"\ + "Please check your settings" ws.log(msg) ws.terminate()
def run(): """ Run a simulation """ if h.tstop <= ws.totaldur: msg = 'WARNING: The simulation tstop is shorter than the total '\ + 'stimulation time. This will cause trouble when processing '\ + 'the recordings.' ws.log(msg) def initialize(): """ Initialize the simulation for the first time step """ h.finitialize(h.v_init) h.fcurrent() def go_by_steps(): """ Main driver for the simulation """ initialize() # Loop over time steps for i_t, t in enumerate(ws.tarray): h.fadvance() # Rest for 500 ms to allow the computer to cool down if False: time.sleep(0.5) # Time control t1 = datetime.now() msg = "Time step %i of %i. Total elapsed time: %i seconds" % ( i_t, ws.nt, (t1 - t0).total_seconds()) # Log the time step into the simulation log file ws.log(msg) def go_full(): """ Run the simulation in one go with h.run() """ initialize() h.run() # Run t0 = datetime.now() # log('Running the simulation in NEURON') now = datetime.now() ws.log("Starting the simulation in NEURON on: %s" % now.strftime("%d %h %Y at %H:%M:%S")) go_by_steps() now = datetime.now() ws.log("NEURON simulation finished on: %s" % now.strftime("%d %h %Y at %H:%M:%S")) log('Whole simulation time: %i seconds' % (now - t0).total_seconds())
def __new__(cls, verts, *args, **kwargs): lv = len(verts) # Validation if lv == 0: msg = 'ERROR: Point list or array is empty.' try: ws.log(msg) except AttributeError: print(msg) else: ws.terminate() elif lv == 1: msg = 'WARNING: Defined polygon with only 1 point:\n%s. Returing point' % str( verts) try: ws.log(msg) except AttributeError: print(msg) return Point(*verts[0]) elif lv == 2: msg = 'WARNING: Defined polygon with only 2 points:\n%s. Returing Segment' % str( verts) try: ws.log(msg) except AttributeError: print(msg) return Segment(verts) # elif lv == 3: # This is a triangle # return super(Triangle, cls).__new__(cls) # return super(Triangle, cls).__new__(cls) # I didn't get this to work... pass elif lv >= 3: # Now, this is a proper polygon. So, proceed. return super(Polygon, cls).__new__(cls)
def build(self): """ Build it """ ws.log("Building the power diagram") x, y, r = self.x, self.y, self.r # Generating circles gc = GCircles(x, y, r) gc.triangulate() # First iteration pairs, trios, segments, pairs_trios, pairs_oprs, pairs_opts, \ pairs_allpts, pvpts, lines = build_laguerre(gc, gc.trios) # First correction new_trios, new_pairs, rmv_trios, rmv_pairs, finished = \ one_correction(pairs_oprs, pairs_opts, pairs_trios, segments) trios = update_trios(new_trios, rmv_trios, trios) # Iterate until there's no more bad crossings # Iteration counter corrcount = 0 while not finished: pairs, trios, segments, pairs_trios, pairs_oprs, pairs_opts, \ pairs_allpts, pvpts, lines = build_laguerre(gc, trios) new_trios, new_pairs, rmv_trios, rmv_pairs, finished = \ one_correction(pairs_oprs, pairs_opts, pairs_trios, segments) trios = update_trios(new_trios, rmv_trios, trios) # Go fix the only-two-connections issue poss_lost = [] poss_lost, rmv_trios, new_trios = fix_biconnections( poss_lost, pairs, gc, pairs_trios) if len(poss_lost) > 0: trios = update_trios(new_trios, rmv_trios, trios) pairs, trios, segments, pairs_trios, pairs_oprs, pairs_opts, \ pairs_allpts, pvpts, lines = build_laguerre(gc, trios) # 'This should be fixed now' whoslost = poss_lost[0] corrcount += 1 enough = corrcount == gc.nc / 4 # Check if this is finished finished = finished or enough ws.log('Number of corrections: %i' % corrcount) if enough: error_msg = 'TESSELLATION ERROR: Something went ' \ + 'wrong with this set of circles\n'\ + 'This is probably due to a bug in the algorithm\n' \ + 'Please try a different set of circles\n' ws.log(error_msg) self.any_errors = True print('TESSELLATION ERROR') return ws.log('Correction iterations: %i' % corrcount) # Check if two connections cross for ip, pair1 in enumerate(pairs): a = segments[pair1] for pair2 in pairs[ip + 1:]: b = segments[pair2] if isinstance(a, geo.Segment) and isinstance(b, geo.Segment): crossing, _ = geo.crossing_segments(a, b) overlapping = geo.overlapping_segments(a, b) if crossing or overlapping: ws.log( 'TESSELLATION ERROR: An error was found for pairs: %s, %s' % (str(pair1), str(pair2))) ws.log('This is due to a bug in the algorithm') ws.log('Please try a different set of circles') self.any_errors = True print('TESSELLATION ERROR') return else: pass # If finished, crop the diagram using the contour if self.contour is not None: # print('') # print('segments:') # for k, v in segments.items(): # print(k, v) # Crop it segments = crop_diagram(gc, pairs, segments, pvpts, lines, self.contour) # Finally, remove the segments which are not geo.Segment # instances for k, seg in segments.items(): if not isinstance(seg, geo.Segment): if len(seg) != 2: segments[k] = None if len(seg) == 2: segments[k] = geo.Segment(seg) self.pairs = pairs self.segments = segments self.trios = trios self.get_polygons()
def print_properties(self): """ Print properties of the nerve, same as with the fascicles""" ws.log('--------------------------------------') ws.log('Nerve') ws.log('Circumcircle\'s Diameter: %0.2f' % self.circumdiameter) ws.log('Circumcircle\'s Center: (%f, %f)' % (self.circumcenter[0], self.circumcenter[1])) ws.log('Area: %0.3f um2' % self.crss_area) ws.log('Number of Axons: %i' % self.naxons_total) ws.log('Number of NAELC: %i' % self.nNAELC) ws.log('Total number of cables: %i' % self.nc) ws.log('--------------------------------------')
def print_properties(self): """ Print the main properties of the fascicle: size, packing ratios, number of axons, etc.""" try: ws.log('--------------------------------------') ws.log('Fascicle %s' % self.id) ws.log('Circumcircle\'s Diameter: %0.2f' % self.circumdiameter) ws.log('Circumcircle\'s Center: (%f, %f)' % (self.circumcenter[0], self.circumcenter[1])) # ws.log('Centroid: (%f, %f)'%(self.polygon.centroid[0], self.polygon.centroid[1])) ws.log('Area: %0.3f um2' % self.area) ws.log('Number of Axons: %i' % self.naxons) ws.log('Fiber Area: %0.3f um2' % self.fiber_area) ws.log('Extracellular Area: %0.3f um2' % self.free_area) ws.log('Fiber Packing Ratio: %0.3f' % self.packing_ratio) ws.log('Intracellular Area: %0.3f um2' % self.intracellular_area) ws.log('Intracellular to Extracellular Areas Ratio: %0.3f' % self.intra_extra_ratio) ws.log('--------------------------------------') except AttributeError: ws.log( 'ERROR: Fascicle %s is missing certain attributes that were intended to print. Skipping the rest.' % self.id)
def prepare_simulation(prep_workspace=True): """ Do the whole process of preparing a simulation """ # Prepare the necessary variables from the configuration files if prep_workspace: prepare_workspace() # Shortenings stimmethod = ws.settings["stimulation"]["method"] nervemodel = ws.settings["nerve model"] # Create the NERVE TOPOLOGY. Cross-section only anatomy.create_nerve() # BIOPHYSICS # LOAD NEURON HOC CODE AND DEFINE FUNCTIONS # Load NEURON code h.load_file(os.path.join(ws.folders["src/hoc"], "ephap.hoc")) h.load_file(os.path.join(ws.folders["src/hoc"], "wire.hoc")) for m in ws.axonmodel_settings.values(): h.load_file(m['hoc file']) # BUILD ALL THE WIRES with all their length bio.build_cables() # Finish setting up information for the fascicles if nervemodel == "resistor network": bio.finish_fascicles() # Create electrodes # This needs to be after the creation of the cables and before # connecting the ephapses if stimmethod == "from electrodes": ws.log("Creating the electrodes") electrodes.create_electrodes() # elif stimmethod == "pre-computed": # pass # EPHAPSES if ws.EC["presence"]: if nervemodel == "resistor network": # Create an instance of the Resistor Network ws.log("Creating the Resistor Network") ws.rnet = bio.ResistorNetwork() # Get the resistances of the connections for each pair ws.log("\tGetting resistor values") ws.rnet.get_resistances() # Connect resistors with ephapses in NEURON ws.log("\tConnecting resistors") ws.rnet.connect_resistors() # OTHER CONNECTIONS ws.log("Preparing connections to ground on the boundaries and xraxial") # Prepare connections to ground in the system if stimmethod == "from electrodes": electrodes.prepare_ground_paths() # Set xraxial and make the connections to ground if nervemodel == "resistor network": bio.electrics() # STIMULATION ws.log("Setting up stimulation") # Inject currents from the cuff electrode # Add injected currents, delays and durations if stimmethod == "from electrodes": # if nervemodel == "resistor network": electrodes.set_stimulation() elif stimmethod == "pre-computed": bio.set_direct_extracellular_stimulation() # RECORDINGS ws.log("Setting up recordings") # Time als.set_time_recording() # From electrodes # Flag: presence of electrode recordings # Set it to False by default; it will become True if a recording # electrode is created ws.settings["electrode recordings"] = False if stimmethod == "from electrodes": if nervemodel == "resistor network": electrodes.set_recordings() # Directly on the cables/axons als.record_cables()
def create_nerve(): """ Create the nerve using the available information Also create the necessary variables for the model to work and to be able to perform further actions """ ws.log("Creating Nerve") # Build a nerve from the specifications in the anatomy settings # Contours # Generation if ws.anatomy_settings["cross-section"]["use contours"] == "generation": generate_contours() # Change the settings so we load the just generated file ws.anatomy_settings["cross-section"]["contours file"] = os.path.join( ws.folders["data/saved"], ws.anatomy_settings["cross-section"] ["contours generation"]["filename"]) # Load from a file elif ws.anatomy_settings["cross-section"]["use contours"] == "load file": pass # Create the tessellated nerve ws.nvt = NerveTess() nvt = ws.nvt # Filling # Generation if ws.anatomy_settings["cross-section"][ "use internal topology"] == "generation": # Create the cables and tessellation print('generation of internal topology') fill_contours() ws.anatomy_settings["cross-section"]["internal topology file"] = \ os.path.join(ws.folders["data/saved"], ws.anatomy_settings["cross-section"]["fibers distribution"]["filename"]) # Load from file and build if ws.anatomy_settings["cross-section"][ "use internal topology"] == "load file": itpath = ws.anatomy_settings["cross-section"]["internal topology file"] if '.csv' in itpath: ws.nvt.build_preexisting() elif '.json' in itpath: ws.nvt.build_from_json() # Allocate cables to fascicles ws.nvt.allocate_cables_in_fascicles() # Save the generated topology if ws.anatomy_settings["cross-section"][ "use internal topology"] == "generation": savepath = ws.anatomy_settings["cross-section"]["fibers distribution"][ "filename"] # To a csv file spath = os.path.join(ws.folders["data/saved"], savepath.replace('.json', '.csv')) ws.nvt.save_to_file(spath) # To a json file if ws.anatomy_settings["cross-section"][ "use internal topology"] == "generation": jsonpath = os.path.join(ws.folders["data/saved"], savepath.replace('.csv', '.json')) ws.nvt.save_to_json(jsonpath) # Get fascicles' free areas for k in ws.nvt.fascicles: ws.nvt.fascicles[k].get_free_area() # Get the necessary stuff from the nerve ws.nNAELC = nvt.nNAELC ws.naxons_total = nvt.naxons_total ws.nc = nvt.nc r = nvt.r ws.pairs = nvt.pairs contour_nerve = nvt.contour_nerve if ws.settings["graphics"]: fig, ax = plt.subplots() diams = 2. * r[np.where(r > 0)] count, bins, ignored = ax.hist(diams, 50, normed=True) binswidth = bins[1:] - bins[:-1] import scipy.special as sps if ws.anatomy_settings["cross-section"]["use internal topology"] == "generation" \ and ws.anatomy_settings["cross-section"]["fibers distribution"]["axon placements"]["packing"]["type"] == "gamma": ws.log("Axon diameters:") ws.log("\tThis should be close to 1: %f:" % (count * binswidth).sum()) ws.log("\tThis should be close to %f: %f" % (2 * ws.nvt.avg_r, diams.mean())) shape = ws.nvt.gamma_shape mean = 2. * ws.nvt.avg_r scale = mean / shape else: shape = 2.5 mean = 2. * 3.65 scale = mean / shape y = bins**(shape - 1) * (np.exp(-bins / scale) / (sps.gamma(shape) * scale**shape)) ax.plot(bins, y, linewidth=2, color='r') ax.set_xlabel("Axon Diameters (um)") plt.show() # Find the axon with the lowest radius try: ws.raxmin = r[np.where(r != 0)].min() except ValueError: # No axons ws.raxmin = 0. # Number of points in the nerve's contour ws.npc = len(contour_nerve['Nerve']) # An array with the angular positions of the points on the nerve's contour # I will need this for the stimulation with cuff electrodes ws.contour_angles = np.zeros(ws.npc) for i, (xc, yc) in enumerate(contour_nerve['Nerve']): # ws.contour_angles[i] = geo.pts_angle(nvt.centroid, (xc, yc)) ws.contour_angles[i] = geo.pts_angle(nvt.circumcenter, (xc, yc)) # Save in the workspace ws.r = nvt.r ws.contour_nerve = contour_nerve # z-axis ws.z = np.linspace(0., ws.length, ws.nseg) # Create figure to visualise the tessellation process if ws.settings["graphics"]: fig, ax = plt.subplots() ws.ax = ax ws.nvt.pd.draw(ax) # ws.nvt.draw_axons_and_points(ax, facecolor='grey') ws.nvt.draw_axons_and_points(ax, facecolor='grey', lw=0) ws.nvt.draw_contours(ax, c='r') if False: for i, (x, y) in enumerate(zip(ws.nvt.x, ws.nvt.y)): ax.text(x, y, i) ax.text(x, y, ws.nvt.models[i][0], color='r') if ws.nvt.models[i] == 'gaines_sensory': ax.scatter(x, y, c='b', s=np.pi * ws.nvt.r[i]**2, zorder=1e9) elif ws.nvt.models[i] == 'MRG': ax.scatter(x, y, c='r', s=np.pi * ws.nvt.r[i]**2, zorder=1e9) # Draw the contour from the PD # ws.nvt.pd.draw(ax, colour='g', linestyle='-', linewidth=3) if False: ws.nvt.pd.draw(ax, colour='k', linestyle='-', linewidth=0, values='precomputed', precompv=ws.nvt.free_areas, alpha=0.2) ax.set_aspect('equal') ax.legend() plt.show() ws.log("Nerve has been created")
def crop_diagram(gc, pairs, segments, pvpts, lines, contour): """ Crop the diagram using a contour """ segments = get_far_points(gc, segments, pvpts, lines) # Calculate limits cont_xy = contour['vertices'] # cont_sg = cont_xy[contour['segments']] contour_polygon = geo.Polygon(cont_xy) x, y = np.array(cont_xy).T xmin, xmax = x.min(), x.max() ymin, ymax = y.min(), y.max() dx = xmax - xmin dy = ymax - ymin # Now get the intersection between a pair's segment and the contour for pair in pairs: seg = segments[pair] if isinstance(seg, geo.Segment): # First off, if the segment is completely outside the contour, # remove it a, b = seg.a, seg.b discard = (not contour_polygon.contains_point(a)) and ( not contour_polygon.contains_point(b)) if not discard: for cs in contour_polygon.segments.values(): # xyint = geo.intersection_segments(seg, geo.Segment(cs)) xyint = geo.intersection_segments(seg, cs) if xyint != (np.nan, np.nan): # We found an intersection between the segment and the # contour # Now the point to be replaced is the one falling outside if contour_polygon.contains_point(a): change = 1 elif contour_polygon.contains_point(b): change = 0 # the contour seg.change_point(change, xyint) # We're done with this loop break # Sanity Check after cutting it # If any segment is even partially outside the contour, remove it # I've seen this happen when a segment has one point # outside and one right on the contour, for which no # intersection is found and hence it's left as is # Check if any point is detected to be outside the contour (being # on it may yield True, unfortunately), so in that case, check = (not contour_polygon.contains_point(a)) or ( not contour_polygon.contains_point(b)) if check: # roughly check if it's really outside. In that # case, it may be really really far (thus be a # long segment) discard = seg.length > 10. * np.hypot(dx, dy) if discard: ws.log('Discarding %s' % str(pair)) ws.log(str(contour_polygon.contains_point(seg.a))) ws.log(str(seg.points.flatten())) ws.log(str(seg)) ws.log('length = %s, dx = %s, dy = %s' % (str(seg.length), str(dx), str(dy))) ws.log("") segments[pair] = [None] else: # Add the segment segments[pair] = seg else: # Add the segment segments[pair] = seg else: # This is not good doing anymore # segments[pair] = [None] pass # List pairs again pairs = list(segments.keys()) return segments
def set_recordings(self): """ Prepare the recording vectors for the desired variables """ # ws.log("Setting recordings for cable %i"%self.cable_number) # Update the flag indicating if this cable has recordings self.has_recordings = True # Variables to record from the settings rvars = ws.settings["data records"]["variables"] # Dictionary with all the recordings: # One entry per variable. Each entry contains a list of # recording identifiers (one for each variable and segment of # the cable) self.recordings = {} # Which sections to record where_to_record = ws.settings["data records"]["record sections"] # If this cable is a NAELC, record only vext[0] if self.cabletype == 'NAELC': rvars = ['vext[0]'] where_to_record = ["all"] # Record the variables timings = [] for var in rvars: # Create the list self.recordings[var] = [] # Create the recording vectors for i, seg in enumerate(ws.segments[self.cable_number]): record = False t0 = datetime.now() if where_to_record[0] == "all": record = True else: # for sectypename in where_to_record: # if self.properties["sctyps"][i] == sectypename: # record = True if self.properties["sctyps"][i] in where_to_record: record = True if record: self.recordings[var].append({ "number": ws.counters['recordings'], "segment": str(seg) }) als.set_recording(seg, var) t1 = datetime.now() timings.append((t1 - t0).total_seconds()) ws.log( "\tAverage time needed by als.set_recording for cable %i: %f ms. Max: %f ms" % (self.cable_number, 1e3 * np.array(timings).mean(), 1e3 * np.array(timings).max()))
def build_cables(): """ Build all the cables, NAELC and fibers """ ws.log("Building the cables in NEURON") t0 = datetime.now() # Dictionaries where to store information in the workspace ws.zprofs_RN = {} ws.sections = {} ws.sectypes = {} ws.secsegs = {} ws.seclens = {} ws.zprofs = {} ws.segments = {} cables = [] ws.cables_hoc = h.List() # New building method # First, axon model properties # Axon general model(s) ws.ax_gral_models = {} # ws.ax_gral_models[ws.settings['axon model']] = AxonGeneralModel(ws.settings['axon model']) for m in ws.nvt.models_set: if 'NAELC' not in m: ws.ax_gral_models[m] = AxonGeneralModel(m) ws.ax_gral_models[m].build_regressions() # Classes of cables ws.cable_classes = {"NAELC": NAELC, "Axon": Axon} # First, instantiate all the cables. # Then, build axons. From them, obtain the desired nseg for NAELC. # Finally, build the NAELC using this value # Instantiate everything nvt = ws.nvt for i in range(nvt.nc): # Create the cable(s) if ws.settings["nerve model"] == "resistor network": # Use NAELC # cable = ws.cable_classes[nvt.cables[i]](ws.settings['axon model']) cable = ws.cable_classes[nvt.cables[i]](nvt.models[i]) # Build it only if it is an axon # The NAELC will be built later on if nvt.cables[i] == 'Axon': # print('building', i, nvt.cables[i]) cable.build() # Append it cables.append(cable) elif ws.settings["nerve model"] == "simple": # Don't use NAELC if nvt.cables[i] == 'Axon': # cable = ws.cable_classes[nvt.cables[i]](ws.settings['axon model']) cable = ws.cable_classes[nvt.cables[i]](nvt.models[i]) # Build the axon cable.build() # Append it cables.append(cable) # Obtain nseg for the NAELC if there are axons if ws.counters['axons'] > 0: # There's axons daxmin = 2. * ws.raxmin if ws.EC["resistor network"][ "transverse resistor locations"] == "only nodes of Ranvier": nseg = int(ws.length / ws.deltax_min) + 1 elif ws.EC["resistor network"][ "transverse resistor locations"] == "regular": nseg = int(ws.length / float( ws.EC["resistor network"]["resistors separation"])) + 1 else: # nseg for an internode nseg = anatomy.nseg_dlambda_rule( ws.deltax_min, ws.g_min * daxmin, cables[ws.counters['NAELC']].properties['rhoa'], cm=cables[ws.counters['NAELC']].properties['cm']) # nseg for the whole nerve length nseg = int(ws.length / (ws.deltax_min / nseg)) + 1 # Make sure it's an odd number # This adds 2 if it's odd, but it doesn't matter; it's even better nseg += nseg % 2 + 1 else: # If we're here, there are no axons nseg = ws.nseg ws.NAELC_nseg = nseg ws.log("NAELC segment length = %f" % (ws.length / nseg)) ws.log("nseg: %i" % nseg) if ws.counters['axons'] > 0: ws.log("Min. deltax: %f" % ws.deltax_min) # Now build the NAELC according to the value of nseg # obtained from the axons for i in range(ws.counters['NAELC']): # print('building', i, cables[i]) cables[i].build() # print(ws.nvt.cables) # Store the cables in the workspace ws.cables = cables # Maximum number of segments. This is a number to be found ws.maxnseg = 0 t1 = datetime.now() ws.log("Created %i cables in %i seconds" % (ws.nvt.nc, (t1 - t0).total_seconds()))