Page 1 of 1

Correcting poor code design

Posted: Mon May 03, 2021 10:39 am
by aliaped
Hi there!

First off, thank you for taking time to look into my question.

I started my project working mostly with the gui, and am now transitioning to working largely with python. When I was building my gui projects, I saved all my items (rig, UI, CellBuilder, etc) in one session file. I saved a new file multiple times a day, so I have a 100+ .ses files. I am now trying to correct that mistake. I wrote a quick python script to isolate the cell builder files, but when I run it through my jupyter notebook, my kernel consistently dies. This is a "load dependent" effect - if the file list is short or if I have less if statements, the kernel is fine. I'm not sure if this is a question you could answer, as it might be completely unrelated to neuron. However, I suspect there is a better way to clean up messy combined gui files, and I would appreciate any direction you might be able to provide. I have attached my code below. Please let me know if you need to see more for proper troubleshooting.

Many thanks again,


Code: Select all

    import os
    import efel
    import numpy as np
    from neuron import h, nrn, gui
    from neuron.units import ms, mV
    import csv
    directory_in_str ='/Users/pedersonam/ModelMOC/Alia/Today/AMP_model'

#Sim run protocol
    def make_trace(h, stim, hold):
        trace = {}
        v = h.Vector().record(h.soma(0.5)._ref_v)# Membrane potential vector
        t = h.Vector().record(h._ref_t)# Time stamp vector
        h.finitialize(-60 * mV)
        h.continuerun(1000 * ms)
        # Set the 'V' (=voltage) key of the trace
        trace['V'] = v
        trace['T'] = t
        # Set the 'stim_start' (time at which a stimulus starts, in ms)
        # key of the trace
        # Warning: this need to be a list (with one element)
        trace['stim_start'] = [300]
        # Set the 'stim_end' (time at which a stimulus end) key of the trace
        # Warning: this need to be a list (with one element)
        trace['stim_end'] = [stim.dur + stim.delay]
        return trace

# globals
    traces = []
    filenames = []
    directory = os.fsencode(directory_in_str)

#cleaning files - this is the problem area
    def file_clean():
        for file in os.listdir(directory):
            filename = os.fsdecode(file)
            if filename.endswith(".ses") and (not filename.endswith('')): 
                p = h.PWManager()
                count = p.count()
                index = 0
                while index < count:
                    if'Point') :
                        count = count-1
                        index = index - 1
                    elif'I/V') :
                        count = count-1
                        index = index - 1
                    elif'Gr') :
                        count = count-1
                        index = index - 1
                    else :
                        index = index + 1
                h.save_session(filename.replace('.ses', ''))
            else :

#creating the rig
    def rig_spec(h, traces):
        soma = h.soma
        if hasattr(soma, 'eh'):
            for sec in h.allsec():
       = -38
        # # Current clamp 
        stim = h.IClamp(h.soma(0.5))
        stim.delay = 300 * ms
        stim.dur = 500 * ms
        stim.amp = 0.07
        tstop = stim.delay+stim.dur+250
        hold = h.IClamp(soma(0.5))
        hold.delay = 0
        hold.dur = tstop
        hold.amp = 0.0121695
        traces.append(make_trace(h, stim, hold))
        return h

 # kernel dies here

#actually running the simulation
    for file in os.listdir(directory):
        filename = os.fsdecode(file)
        if filename.endswith("") and filename.startswith('2'): 
            rig_spec(h, traces)

Re: Correcting poor code design

Posted: Mon May 03, 2021 12:10 pm
by ted
The GUI is a great way to get a lot done quickly, but it can become cumbersome to deal with all of those graphs and panels that are so easy to spawn.

A good way to maintain sanity is to apply the principles of
incremental revision and testing
modular code organization.

Incremental revision and testing is easy enough--start with something small and simple that works, then

Code: Select all

  change something
  try it
UNTIL you have what you want
But how do you impose modularity if you've been saving everything into a single .ses file? It's not easy. Yes, you can try to dissect the .ses file with a text editor, but that's hard to do without breaking something.

My programs tend to follow this sequence:

1. specify the model itself (i.e. the anatomical and biophysical properties of the model cell)
2. specify the instrumentation (stimuli, Vector record, graphs)
3. specify simulation flow control (running a single simulation, running a series of simulations in which one or more parameters are swept over a range of values, running one or more simulations followed by postprocessing of results to extract key measures of performance e.g. spike frequency)
4. write results to one or more files

If I use the GUI, I am careful to save session files selectively. Then I can use load_file() to retrieve and recreate just those GUI tools that I want.

Example: suppose I have a Neurolucida morphology file. I start by executing a Python file that contains these statements
from neuron import h,gui
I think I'll call this file

Executing gives me a NEURON Main Menu toolbar that I use to bring up an Import3d tool, so I can import the morphology into a CellBuilder.

Next I save the CellBuilder to a ses file called all by itself, then exit NEURON.

My next step is to update to

h.load_file("") # CellBuilder that specifies model cell's anatomy and biophysics

which I execute. Now I have a NEURON Main Menu toolbar and a CellBuilder. I use the CellBuilder to specify the model cell's discretization strategy (d_lambda, of course), and specify the model's biophysical parameters (Ra, cm, ion channels and their parameters). Now, before I break anything, I save just the updated CellBuilder on top of the previous file.

If all is well, I toggle the CellBuilder's Continuous Create button. If everything is still good, I save the CellBuilder to again.

Next I bring up a RunControl panel, attach an IClamp to soma(0.5), and create two Graphs--one that shows v at the middle of the soma--just v(0.5) or soma.v(0.5) in hoc, but you and I know that its Python name is h.soma(0.5).v.

Time to save some new session for the IClamp, for the two graphs, and for the RunControl panel.

Now I can update to

from neuron import h,gui

If I need to take a break, fine. When I return, I just use python to execute and everything is restored to where it was before my break.

Next I run some simulations, make sure that the IClamp's current doesn't start until after h.soma(0.5).v has settled down (just in case initialization didn't put the cell into steady state), maybe change tstop in the RunControl, and adjust duration and amplitude to get a spike. Maybe I have to rescale one or both of the Graphs ("View = plot" or "Set view").

Time to save the updated GUI tools to their .ses,, and

Re: Correcting poor code design

Posted: Mon May 03, 2021 12:43 pm
by aliaped
Hi Ted,

Thank you for your thorough response. I fear I left something out of my initial question: while i love the gui tools, I would like to use as few of them as possible. I am trying to fully control and modify my model with python code to eliminate the clicking of buttons. I don't know how to convert the cell builder spec to python (only how to modify it once it is loaded), but everything else can be done without the gui, and I would prefer that. How to I implement modularity with that goal in mind?

Many thanks again,


Re: Correcting poor code design

Posted: Mon May 03, 2021 4:05 pm
by ted
One writes code the way one writes anything else. In this particular case, start by printing on a small sheet of paper the incremental revision and testing algorithm, and the four step sequence I mentioned, and tape it to your desktop or your monitor. Get a sheet of paper and a pencil and start writing code, referring to the Programmer's Reference as necessary. After you have a small block of code you want to test--even just a single line of code, if you have the least doubt about what you read in the Programmer's Reference or simply haven't executed such a statement before--use a text editor to enter it into a file, and use your computer to test and revise the file.