Beispiel #1
0
    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])
Beispiel #2
0
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()
Beispiel #3
0
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)))
Beispiel #4
0
    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()
Beispiel #5
0
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
Beispiel #6
0
 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)
Beispiel #7
0
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
Beispiel #8
0
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])
Beispiel #9
0
    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)
Beispiel #10
0
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])
Beispiel #11
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()
Beispiel #12
0
    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)
Beispiel #13
0
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()
Beispiel #14
0
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())
Beispiel #15
0
    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()
Beispiel #17
0
 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('--------------------------------------')
Beispiel #18
0
    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)
Beispiel #19
0
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()
Beispiel #20
0
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
Beispiel #22
0
    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()))
Beispiel #23
0
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()))