"""
Neuron simulator export for:

Components:
    spiker (Type: spikeGenerator:  period=0.012 (SI time))
    spikes2 (Type: spikeArray)
    spike100 (Type: spikeArray)
    if1 (Type: intFire1Cell:  tau=0.024 (SI time) C=1.0000000000000001E-11 (SI capacitance) thresh=0.001 (SI voltage) reset=0.0 (SI voltage) MVOLT=0.001 (SI voltage) PAMPERE=1.0E-12 (SI current))
    iaf1 (Type: iafCell:  leakConductance=5.0E-10 (SI conductance) leakReversal=-0.06 (SI voltage) thresh=-0.035 (SI voltage) reset=-0.065 (SI voltage) C=1.0000000000000001E-11 (SI capacitance))
    iaf2 (Type: iafCell:  leakConductance=5.2E-10 (SI conductance) leakReversal=-0.06 (SI voltage) thresh=-0.035 (SI voltage) reset=-0.07 (SI voltage) C=1.0000000000000001E-11 (SI capacitance))
    syn0 (Type: impulseSynapse:  PAMPERE=1.0E-12 (SI current) TSMALL=2.5E-6 (SI time))
    syn1 (Type: expOneSynapse:  tauDecay=0.002 (SI time) gbase=1.0000000000000002E-10 (SI conductance) erev=0.01 (SI voltage))
    syn2 (Type: expTwoSynapse:  tauRise=5.0E-5 (SI time) tauDecay=0.002 (SI time) peakTime=1.8917330533917622E-4 (SI time) waveformFactor=1.1273891364130213 (dimensionless) gbase=1.0000000000000002E-10 (SI conductance) erev=0.01 (SI voltage))
    net2 (Type: network)
    sim1 (Type: Simulation:  length=0.3 (SI time) step=5.0E-6 (SI time))


    This NEURON file has been generated by org.neuroml.export (see https://github.com/NeuroML/org.neuroml.export)
         org.neuroml.export  v1.5.4
         org.neuroml.model   v1.5.4
         jLEMS               v0.9.9.1

"""

import neuron

import time
import sys

import hashlib

h = neuron.h
h.load_file("nrngui.hoc")

h("objref p")
h("p = new PythonObject()")


class NeuronSimulation:
    def __init__(self, tstop, dt, seed=123456789):

        print(
            "\n    Starting simulation in NEURON of %sms generated from NeuroML2 model...\n"
            % tstop
        )

        self.seed = seed
        self.randoms = []
        self.next_global_id = (
            0  # Used in Random123 classes for elements using random(), etc.
        )

        self.next_spiking_input_id = (
            0  # Used in Random123 classes for elements using random(), etc.
        )

        """
        Adding simulation Component(id=sim1 type=Simulation) of network/component: net2 (Type: network)
        
        """
        # ######################   Population: spikersPop
        print(
            "Population spikersPop contains 1 instance(s) of component: spiker of type: spikeGenerator"
        )

        h(" {n_spikersPop = 1} ")
        """
        Population spikersPop contains instances of Component(id=spiker type=spikeGenerator)
        whose dynamics will be implemented as a mechanism (spiker) in a mod file
        """
        h(" create spikersPop[1]")
        h(" objectvar m_spiker_spikersPop[1] ")

        for i in range(int(h.n_spikersPop)):
            h.spikersPop[i].L = 10.0
            h.spikersPop[i](0.5).diam = 10.0
            h.spikersPop[i](0.5).cm = 318.31
            h.spikersPop[i].push()
            h(
                " spikersPop[%i]  { m_spiker_spikersPop[%i] = new spiker(0.5) } "
                % (i, i)
            )

            h.m_spiker_spikersPop[i].period = 12.0
            h.pop_section()

            self.next_global_id += 1

        # ######################   Population: spikeArrayPop
        print(
            "Population spikeArrayPop contains 1 instance(s) of component: spikes2 of type: spikeArray"
        )

        h(" {n_spikeArrayPop = 1} ")
        """
        Population spikeArrayPop contains instances of Component(id=spikes2 type=spikeArray)
        whose dynamics will be implemented as a mechanism (spikes2) in a mod file
        """
        h(" create spikeArrayPop[1]")
        h(" objectvar m_spikes2_spikeArrayPop[1] ")

        for i in range(int(h.n_spikeArrayPop)):
            h.spikeArrayPop[i].L = 10.0
            h.spikeArrayPop[i](0.5).diam = 10.0
            h.spikeArrayPop[i](0.5).cm = 318.31
            h.spikeArrayPop[i].push()
            h(
                " spikeArrayPop[%i]  { m_spikes2_spikeArrayPop[%i] = new spikes2(0.5) } "
                % (i, i)
            )

            h.pop_section()

            self.next_global_id += 1

        # ######################   Population: spike100Pop
        print(
            "Population spike100Pop contains 1 instance(s) of component: spike100 of type: spikeArray"
        )

        h(" {n_spike100Pop = 1} ")
        """
        Population spike100Pop contains instances of Component(id=spike100 type=spikeArray)
        whose dynamics will be implemented as a mechanism (spike100) in a mod file
        """
        h(" create spike100Pop[1]")
        h(" objectvar m_spike100_spike100Pop[1] ")

        for i in range(int(h.n_spike100Pop)):
            h.spike100Pop[i].L = 10.0
            h.spike100Pop[i](0.5).diam = 10.0
            h.spike100Pop[i](0.5).cm = 318.31
            h.spike100Pop[i].push()
            h(
                " spike100Pop[%i]  { m_spike100_spike100Pop[%i] = new spike100(0.5) } "
                % (i, i)
            )

            h.pop_section()

            self.next_global_id += 1

        # ######################   Population: iafPop
        print(
            "Population iafPop contains 7 instance(s) of component: iaf1 of type: iafCell"
        )

        h(" {n_iafPop = 7} ")
        """
        Population iafPop contains instances of Component(id=iaf1 type=iafCell)
        whose dynamics will be implemented as a mechanism (iaf1) in a mod file
        """
        h(" create iafPop[7]")
        h(" objectvar m_iaf1_iafPop[7] ")

        for i in range(int(h.n_iafPop)):
            h.iafPop[i].L = 10.0
            h.iafPop[i](0.5).diam = 10.0
            h.iafPop[i](0.5).cm = 3.183098861837907
            h.iafPop[i].push()
            h(" iafPop[%i]  { m_iaf1_iafPop[%i] = new iaf1(0.5) } " % (i, i))

            h.m_iaf1_iafPop[i].leakConductance = 5.0e-4
            h.m_iaf1_iafPop[i].leakReversal = -60.0
            h.m_iaf1_iafPop[i].thresh = -35.0
            h.m_iaf1_iafPop[i].reset = -65.0
            h.m_iaf1_iafPop[i].C = 1.0e-5
            h.pop_section()

            self.next_global_id += 1

        # ######################   Population: if1Pop
        print(
            "Population if1Pop contains 7 instance(s) of component: if1 of type: intFire1Cell"
        )

        h(" {n_if1Pop = 7} ")
        """
        Population if1Pop contains instances of Component(id=if1 type=intFire1Cell)
        whose dynamics will be implemented as a mechanism (if1) in a mod file
        """
        h(" create if1Pop[7]")
        h(" objectvar m_if1_if1Pop[7] ")

        for i in range(int(h.n_if1Pop)):
            h.if1Pop[i].L = 10.0
            h.if1Pop[i](0.5).diam = 10.0
            h.if1Pop[i](0.5).cm = 318.31
            h.if1Pop[i].push()
            h(" if1Pop[%i]  { m_if1_if1Pop[%i] = new if1(0.5) } " % (i, i))

            h.m_if1_if1Pop[i].tau = 24.0
            h.m_if1_if1Pop[i].C = 1.0e-5
            h.m_if1_if1Pop[i].thresh = 1.0
            h.m_if1_if1Pop[i].reset = 0.0
            h.m_if1_if1Pop[i].MVOLT = 1.0
            h.m_if1_if1Pop[i].PAMPERE = 0.001
            h.pop_section()

            self.next_global_id += 1

        # ######################   Projection: projection1
        print(
            "Adding projection: projection1, from spike100Pop to iafPop with synapse syn1, 1 connection(s)"
        )

        # Connection null: cell 0, seg 0 (0.5) [0.5 on spike100Pop[0]] -> cell 0, seg 0 (0.5) [0.5 on iafPop[0]], weight: 1.0, delay 0.0
        h("objectvar syn_iafPop_0_0_syn1_0")
        h("iafPop[0] syn_iafPop_0_0_syn1_0 = new syn1(0.5)")
        h("objectvar nc_syn_iafPop_0_0_syn1_0_0")
        h(
            "spike100Pop[0] nc_syn_iafPop_0_0_syn1_0_0 = new NetCon(m_spike100_spike100Pop[0], syn_iafPop_0_0_syn1_0, 0.0, 0.0, 1.0)"
        )

        # ######################   Projection: projection2
        print(
            "Adding projection: projection2, from spike100Pop to iafPop with synapse syn1, 2 connection(s)"
        )

        # Connection null: cell 0, seg 0 (0.5) [0.5 on spike100Pop[0]] -> cell 1, seg 0 (0.5) [0.5 on iafPop[1]], weight: 0.5, delay 10.0
        h("objectvar syn_iafPop_1_0_syn1_0")
        h("iafPop[1] syn_iafPop_1_0_syn1_0 = new syn1(0.5)")
        h("objectvar nc_syn_iafPop_1_0_syn1_0_0")
        h(
            "spike100Pop[0] nc_syn_iafPop_1_0_syn1_0_0 = new NetCon(m_spike100_spike100Pop[0], syn_iafPop_1_0_syn1_0, 0.0, 10.0, 0.5)"
        )

        # Connection null: cell 0, seg 0 (0.5) [0.5 on spike100Pop[0]] -> cell 2, seg 0 (0.5) [0.5 on iafPop[2]], weight: 0.25, delay 15.0
        h("objectvar syn_iafPop_2_0_syn1_0")
        h("iafPop[2] syn_iafPop_2_0_syn1_0 = new syn1(0.5)")
        h("objectvar nc_syn_iafPop_2_0_syn1_0_1")
        h(
            "spike100Pop[0] nc_syn_iafPop_2_0_syn1_0_1 = new NetCon(m_spike100_spike100Pop[0], syn_iafPop_2_0_syn1_0, 0.0, 15.0, 0.25)"
        )

        # ######################   Projection: projection3
        print(
            "Adding projection: projection3, from spikeArrayPop to iafPop with synapse syn2, 1 connection(s)"
        )

        # Connection null: cell 0, seg 0 (0.5) [0.5 on spikeArrayPop[0]] -> cell 3, seg 0 (0.5) [0.5 on iafPop[3]], weight: 1.0, delay 0.0
        h("objectvar syn_iafPop_3_0_syn2_0")
        h("iafPop[3] syn_iafPop_3_0_syn2_0 = new syn2(0.5)")
        h("objectvar nc_syn_iafPop_3_0_syn2_0_0")
        h(
            "spikeArrayPop[0] nc_syn_iafPop_3_0_syn2_0_0 = new NetCon(m_spikes2_spikeArrayPop[0], syn_iafPop_3_0_syn2_0, 0.0, 0.0, 1.0)"
        )

        # ######################   Projection: projection4
        print(
            "Adding projection: projection4, from spikeArrayPop to iafPop with synapse syn2, 2 connection(s)"
        )

        # Connection null: cell 0, seg 0 (0.5) [0.5 on spikeArrayPop[0]] -> cell 4, seg 0 (0.5) [0.5 on iafPop[4]], weight: 0.5, delay 10.0
        h("objectvar syn_iafPop_4_0_syn2_0")
        h("iafPop[4] syn_iafPop_4_0_syn2_0 = new syn2(0.5)")
        h("objectvar nc_syn_iafPop_4_0_syn2_0_0")
        h(
            "spikeArrayPop[0] nc_syn_iafPop_4_0_syn2_0_0 = new NetCon(m_spikes2_spikeArrayPop[0], syn_iafPop_4_0_syn2_0, 0.0, 10.0, 0.5)"
        )

        # Connection null: cell 0, seg 0 (0.5) [0.5 on spikeArrayPop[0]] -> cell 5, seg 0 (0.5) [0.5 on iafPop[5]], weight: 0.25, delay 15.0
        h("objectvar syn_iafPop_5_0_syn2_0")
        h("iafPop[5] syn_iafPop_5_0_syn2_0 = new syn2(0.5)")
        h("objectvar nc_syn_iafPop_5_0_syn2_0_1")
        h(
            "spikeArrayPop[0] nc_syn_iafPop_5_0_syn2_0_1 = new NetCon(m_spikes2_spikeArrayPop[0], syn_iafPop_5_0_syn2_0, 0.0, 15.0, 0.25)"
        )

        # ######################   Projection: projection1a
        print(
            "Adding projection: projection1a, from spike100Pop to if1Pop with synapse syn0, 3 connection(s)"
        )

        # Connection null: cell 0, seg 0 (0.5) [0.5 on spike100Pop[0]] -> cell 0, seg 0 (0.5) [0.5 on if1Pop[0]], weight: 0.1, delay 0.0
        h("objectvar syn_if1Pop_0_0_syn0_0")
        h("if1Pop[0] syn_if1Pop_0_0_syn0_0 = new syn0(0.5)")
        h("objectvar nc_syn_if1Pop_0_0_syn0_0_0")
        h(
            "spike100Pop[0] nc_syn_if1Pop_0_0_syn0_0_0 = new NetCon(m_spike100_spike100Pop[0], syn_if1Pop_0_0_syn0_0, 0.0, 0.0, 0.1)"
        )

        # Connection null: cell 0, seg 0 (0.5) [0.5 on spike100Pop[0]] -> cell 1, seg 0 (0.5) [0.5 on if1Pop[1]], weight: 0.05, delay 10.0
        h("objectvar syn_if1Pop_1_0_syn0_0")
        h("if1Pop[1] syn_if1Pop_1_0_syn0_0 = new syn0(0.5)")
        h("objectvar nc_syn_if1Pop_1_0_syn0_0_1")
        h(
            "spike100Pop[0] nc_syn_if1Pop_1_0_syn0_0_1 = new NetCon(m_spike100_spike100Pop[0], syn_if1Pop_1_0_syn0_0, 0.0, 10.0, 0.05)"
        )

        # Connection null: cell 0, seg 0 (0.5) [0.5 on spike100Pop[0]] -> cell 2, seg 0 (0.5) [0.5 on if1Pop[2]], weight: 0.2, delay 15.0
        h("objectvar syn_if1Pop_2_0_syn0_0")
        h("if1Pop[2] syn_if1Pop_2_0_syn0_0 = new syn0(0.5)")
        h("objectvar nc_syn_if1Pop_2_0_syn0_0_2")
        h(
            "spike100Pop[0] nc_syn_if1Pop_2_0_syn0_0_2 = new NetCon(m_spike100_spike100Pop[0], syn_if1Pop_2_0_syn0_0, 0.0, 15.0, 0.2)"
        )

        # ######################   Projection: projection1b
        print(
            "Adding projection: projection1b, from spikersPop to if1Pop with synapse syn0, 1 connection(s)"
        )

        # Connection null: cell 0, seg 0 (0.5) [0.5 on spikersPop[0]] -> cell 3, seg 0 (0.5) [0.5 on if1Pop[3]], weight: 0.4, delay 0.0
        h("objectvar syn_if1Pop_3_0_syn0_0")
        h("if1Pop[3] syn_if1Pop_3_0_syn0_0 = new syn0(0.5)")
        h("objectvar nc_syn_if1Pop_3_0_syn0_0_0")
        h(
            "spikersPop[0] nc_syn_if1Pop_3_0_syn0_0_0 = new NetCon(m_spiker_spikersPop[0], syn_if1Pop_3_0_syn0_0, 0.0, 0.0, 0.4)"
        )

        trec = h.Vector()
        trec.record(h._ref_t)

        h.tstop = tstop

        h.dt = dt

        h.steps_per_ms = 1 / h.dt

        # ######################   Display: self.display_d1
        self.display_d1 = h.Graph(0)
        self.display_d1.size(0, h.tstop, -80.0, 50.0)
        self.display_d1.view(0, -80.0, h.tstop, 130.0, 80, 330, 330, 250)
        h.graphList[0].append(self.display_d1)
        # Line, plotting: spikersPop[0]/tsince
        self.display_d1.addexpr(
            "m_spiker_spikersPop[0].tsince",
            "m_spiker_spikersPop[0].tsince",
            1,
            1,
            0.8,
            0.9,
            2,
        )
        # Line, plotting: spike100Pop[0]/tsince
        self.display_d1.addexpr(
            "m_spike100_spike100Pop[0].tsince",
            "m_spike100_spike100Pop[0].tsince",
            2,
            1,
            0.8,
            0.9,
            2,
        )
        # Line, plotting: spikeArrayPop[0]/tsince
        self.display_d1.addexpr(
            "m_spikes2_spikeArrayPop[0].tsince",
            "m_spikes2_spikeArrayPop[0].tsince",
            3,
            1,
            0.8,
            0.9,
            2,
        )

        # ######################   Display: self.display_d2
        self.display_d2 = h.Graph(0)
        self.display_d2.size(0, h.tstop, -80.0, 50.0)
        self.display_d2.view(0, -80.0, h.tstop, 130.0, 80, 330, 330, 250)
        h.graphList[0].append(self.display_d2)
        # Line, plotting: iafPop[0]/v
        self.display_d2.addexpr(
            "iafPop[0].v(0.5)", "iafPop[0].v(0.5)", 1, 1, 0.8, 0.9, 2
        )
        # Line, plotting: iafPop[1]/v
        self.display_d2.addexpr(
            "iafPop[1].v(0.5)", "iafPop[1].v(0.5)", 2, 1, 0.8, 0.9, 2
        )
        # Line, plotting: iafPop[2]/v
        self.display_d2.addexpr(
            "iafPop[2].v(0.5)", "iafPop[2].v(0.5)", 3, 1, 0.8, 0.9, 2
        )

        # ######################   Display: self.display_d3
        self.display_d3 = h.Graph(0)
        self.display_d3.size(0, h.tstop, -80.0, 50.0)
        self.display_d3.view(0, -80.0, h.tstop, 130.0, 80, 330, 330, 250)
        h.graphList[0].append(self.display_d3)
        # Line, plotting: iafPop[3]/v
        self.display_d3.addexpr(
            "iafPop[3].v(0.5)", "iafPop[3].v(0.5)", 1, 1, 0.8, 0.9, 2
        )
        # Line, plotting: iafPop[4]/v
        self.display_d3.addexpr(
            "iafPop[4].v(0.5)", "iafPop[4].v(0.5)", 2, 1, 0.8, 0.9, 2
        )
        # Line, plotting: iafPop[5]/v
        self.display_d3.addexpr(
            "iafPop[5].v(0.5)", "iafPop[5].v(0.5)", 3, 1, 0.8, 0.9, 2
        )

        # ######################   Display: self.display_d20
        self.display_d20 = h.Graph(0)
        self.display_d20.size(0, h.tstop, -80.0, 50.0)
        self.display_d20.view(0, -80.0, h.tstop, 130.0, 80, 330, 330, 250)
        h.graphList[0].append(self.display_d20)
        # Line, plotting: if1Pop[0]/v
        self.display_d20.addexpr(
            "if1Pop[0].v(0.5)", "if1Pop[0].v(0.5)", 1, 1, 0.8, 0.9, 2
        )
        # Line, plotting: if1Pop[1]/v
        self.display_d20.addexpr(
            "if1Pop[1].v(0.5)", "if1Pop[1].v(0.5)", 2, 1, 0.8, 0.9, 2
        )
        # Line, plotting: if1Pop[2]/v
        self.display_d20.addexpr(
            "if1Pop[2].v(0.5)", "if1Pop[2].v(0.5)", 3, 1, 0.8, 0.9, 2
        )

        # ######################   Display: self.display_d20b
        self.display_d20b = h.Graph(0)
        self.display_d20b.size(0, h.tstop, -80.0, 50.0)
        self.display_d20b.view(0, -80.0, h.tstop, 130.0, 80, 330, 330, 250)
        h.graphList[0].append(self.display_d20b)
        # Line, plotting: if1Pop[3]/v
        self.display_d20b.addexpr(
            "if1Pop[3].v(0.5)", "if1Pop[3].v(0.5)", 1, 1, 0.8, 0.9, 2
        )

        # ######################   Display: self.display_d20a
        self.display_d20a = h.Graph(0)
        self.display_d20a.size(0, h.tstop, -80.0, 50.0)
        self.display_d20a.view(0, -80.0, h.tstop, 130.0, 80, 330, 330, 250)
        h.graphList[0].append(self.display_d20a)
        # Line, plotting: if1Pop[0]/syn0/i
        self.display_d20a.addexpr(
            "m_if1_if1Pop[0].syn0_i", "m_if1_if1Pop[0].syn0_i", 1, 1, 0.8, 0.9, 2
        )
        # Line, plotting: if1Pop[1]/syn0/i
        self.display_d20a.addexpr(
            "m_if1_if1Pop[1].syn0_i", "m_if1_if1Pop[1].syn0_i", 2, 1, 0.8, 0.9, 2
        )
        # Line, plotting: if1Pop[2]/syn0/i
        self.display_d20a.addexpr(
            "m_if1_if1Pop[2].syn0_i", "m_if1_if1Pop[2].syn0_i", 3, 1, 0.8, 0.9, 2
        )

        # ######################   File to save: ex0.dat (of0)
        # Column: iafPop[0]/v
        h(" objectvar v_0_of0 ")
        h(" { v_0_of0 = new Vector() } ")
        h(" { v_0_of0.record(&iafPop[0].v(0.5)) } ")
        h.v_0_of0.resize((h.tstop * h.steps_per_ms) + 1)
        # Column: iafPop[1]/v
        h(" objectvar v_1_of0 ")
        h(" { v_1_of0 = new Vector() } ")
        h(" { v_1_of0.record(&iafPop[1].v(0.5)) } ")
        h.v_1_of0.resize((h.tstop * h.steps_per_ms) + 1)
        # Column: iafPop[2]/v
        h(" objectvar v_2_of0 ")
        h(" { v_2_of0 = new Vector() } ")
        h(" { v_2_of0.record(&iafPop[2].v(0.5)) } ")
        h.v_2_of0.resize((h.tstop * h.steps_per_ms) + 1)
        # Column: iafPop[3]/v
        h(" objectvar v_3_of0 ")
        h(" { v_3_of0 = new Vector() } ")
        h(" { v_3_of0.record(&iafPop[3].v(0.5)) } ")
        h.v_3_of0.resize((h.tstop * h.steps_per_ms) + 1)
        # Column: iafPop[4]/v
        h(" objectvar v_4_of0 ")
        h(" { v_4_of0 = new Vector() } ")
        h(" { v_4_of0.record(&iafPop[4].v(0.5)) } ")
        h.v_4_of0.resize((h.tstop * h.steps_per_ms) + 1)
        # Column: iafPop[5]/v
        h(" objectvar v_5_of0 ")
        h(" { v_5_of0 = new Vector() } ")
        h(" { v_5_of0.record(&iafPop[5].v(0.5)) } ")
        h.v_5_of0.resize((h.tstop * h.steps_per_ms) + 1)
        # Column: iafPop[6]/v
        h(" objectvar v_6_of0 ")
        h(" { v_6_of0 = new Vector() } ")
        h(" { v_6_of0.record(&iafPop[6].v(0.5)) } ")
        h.v_6_of0.resize((h.tstop * h.steps_per_ms) + 1)

        # ######################   File to save: time.dat (time)
        # Column: time
        h(" objectvar v_time ")
        h(" { v_time = new Vector() } ")
        h(" { v_time.record(&t) } ")
        h.v_time.resize((h.tstop * h.steps_per_ms) + 1)

        self.initialized = False

        self.sim_end = -1  # will be overwritten

        h.nrncontrolmenu()

    def run(self):

        self.initialized = True
        sim_start = time.time()
        print(
            "Running a simulation of %sms (dt = %sms; seed=%s)"
            % (h.tstop, h.dt, self.seed)
        )

        h.run()

        self.sim_end = time.time()
        self.sim_time = self.sim_end - sim_start
        print(
            "Finished NEURON simulation in %f seconds (%f mins)..."
            % (self.sim_time, self.sim_time / 60.0)
        )

        self.save_results()

    def advance(self):

        if not self.initialized:
            h.finitialize()
            self.initialized = True

        h.fadvance()

    ###############################################################################
    # Hash function to use in generation of random value
    # This is copied from NetPyNE: https://github.com/Neurosim-lab/netpyne/blob/master/netpyne/simFuncs.py
    ###############################################################################
    def _id32(self, obj):
        return int(
            hashlib.md5(obj).hexdigest()[0:8], 16
        )  # convert 8 first chars of md5 hash in base 16 to int

    ###############################################################################
    # Initialize the stim randomizer
    # This is copied from NetPyNE: https://github.com/Neurosim-lab/netpyne/blob/master/netpyne/simFuncs.py
    ###############################################################################
    def _init_stim_randomizer(self, rand, stimType, gid, seed):
        # print("INIT STIM  %s; %s; %s; %s"%(rand, stimType, gid, seed))
        rand.Random123(self._id32(stimType), gid, seed)

    def save_results(self):

        print("Saving results at t=%s..." % h.t)

        if self.sim_end < 0:
            self.sim_end = time.time()

        self.display_d1.exec_menu("View = plot")
        self.display_d2.exec_menu("View = plot")
        self.display_d3.exec_menu("View = plot")
        self.display_d20.exec_menu("View = plot")
        self.display_d20b.exec_menu("View = plot")
        self.display_d20a.exec_menu("View = plot")

        # ######################   File to save: time.dat (time)
        py_v_time = [
            t / 1000 for t in h.v_time.to_python()
        ]  # Convert to Python list for speed...

        f_time_f2 = open("time.dat", "w")
        num_points = len(py_v_time)  # Simulation may have been stopped before tstop...

        for i in range(num_points):
            f_time_f2.write("%f" % py_v_time[i])  # Save in SI units...
        f_time_f2.close()
        print("Saved data to: time.dat")

        # ######################   File to save: ex0.dat (of0)
        py_v_0_of0 = [
            float(x / 1000.0) for x in h.v_0_of0.to_python()
        ]  # Convert to Python list for speed, variable has dim: voltage
        py_v_1_of0 = [
            float(x / 1000.0) for x in h.v_1_of0.to_python()
        ]  # Convert to Python list for speed, variable has dim: voltage
        py_v_2_of0 = [
            float(x / 1000.0) for x in h.v_2_of0.to_python()
        ]  # Convert to Python list for speed, variable has dim: voltage
        py_v_3_of0 = [
            float(x / 1000.0) for x in h.v_3_of0.to_python()
        ]  # Convert to Python list for speed, variable has dim: voltage
        py_v_4_of0 = [
            float(x / 1000.0) for x in h.v_4_of0.to_python()
        ]  # Convert to Python list for speed, variable has dim: voltage
        py_v_5_of0 = [
            float(x / 1000.0) for x in h.v_5_of0.to_python()
        ]  # Convert to Python list for speed, variable has dim: voltage
        py_v_6_of0 = [
            float(x / 1000.0) for x in h.v_6_of0.to_python()
        ]  # Convert to Python list for speed, variable has dim: voltage

        f_of0_f2 = open("ex0.dat", "w")
        num_points = len(py_v_time)  # Simulation may have been stopped before tstop...

        for i in range(num_points):
            f_of0_f2.write(
                "%e\t%e\t%e\t%e\t%e\t%e\t%e\t%e\t\n"
                % (
                    py_v_time[i],
                    py_v_0_of0[i],
                    py_v_1_of0[i],
                    py_v_2_of0[i],
                    py_v_3_of0[i],
                    py_v_4_of0[i],
                    py_v_5_of0[i],
                    py_v_6_of0[i],
                )
            )
        f_of0_f2.close()
        print("Saved data to: ex0.dat")

        save_end = time.time()
        save_time = save_end - self.sim_end
        print("Finished saving results in %f seconds" % (save_time))

        print("Done")


if __name__ == "__main__":

    ns = NeuronSimulation(tstop=300, dt=0.005, seed=123456789)

    ns.run()
