Correcting poor code design

When Python is the interpreter, what is a good
design for the interface to the basic NEURON
concepts.

Moderator: hines

Post Reply
aliaped
Posts: 13
Joined: Sun Aug 09, 2020 12:10 pm

Correcting poor code design

Post 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,

Alia

Code: Select all

#imports
    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('cell.ses')): 
                h.load_file(filename)
                p = h.PWManager()
                count = p.count()
                index = 0
                while index < count:
                    if p.name(index).startswith('Point') :
                        p.close(index)
                        count = count-1
                        index = index - 1
                    elif p.name(index).startswith('I/V') :
                        p.close(index)
                        count = count-1
                        index = index - 1
                    elif p.name(index).startswith('Gr') :
                        p.close(index)
                        count = count-1
                        index = index - 1
                    else :
                        index = index + 1
                h.save_session(filename.replace('.ses', '_cell.ses'))
            else :
                continue

#creating the rig
    def rig_spec(h, traces):
        soma = h.soma
        if hasattr(soma, 'eh'):
            for sec in h.allsec():
                sec.eh = -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
    file_clean()


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


ted
Site Admin
Posts: 6287
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Re: Correcting poor code design

Post 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
and
modular code organization.

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

Code: Select all

REPEAT
  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
test.py

Executing test.py 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 cell.ses all by itself, then exit NEURON.

My next step is to update test.py to

h.load_file("cell.ses") # 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 cell.ses file.

If all is well, I toggle the CellBuilder's Continuous Create button. If everything is still good, I save the CellBuilder to cell.ses 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 files--stim.ses for the IClamp, graphs.ses for the two graphs, and run.ses for the RunControl panel.

Now I can update test.py to

from neuron import h,gui
h.load_file("cell.ses")
h.load_file("stim.ses")
h.load_file("graphs.ses")
h.load_file("run.ses")

If I need to take a break, fine. When I return, I just use python to execute test.py 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 files--stim.ses, graphs.ses, and run.ses.
aliaped
Posts: 13
Joined: Sun Aug 09, 2020 12:10 pm

Re: Correcting poor code design

Post 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,

Alia
ted
Site Admin
Posts: 6287
Joined: Wed May 18, 2005 4:50 pm
Location: Yale University School of Medicine
Contact:

Re: Correcting poor code design

Post 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.
Post Reply