# populate namespace with pylab functions and stuff
%pylab inline

import nest # import NEST module

"""
Explanation: PyNEST - First Steps
Modeling networks of spiking neurons using NEST
Python Module of the Week, 03.05.2019
Alexander van Meegen
This notebook guides through your first steps using NEST. It shows
* how to get help
* how to create and simulate a single neuron
* how to visualize the output
Essentially, this is a reproduction of the 'Hello World!' notebook with added explanations. 
For more details see part 1 of the official PyNEST tutorial.
End of explanation
"""


# information about functions with Python's help() ...
help(nest.Models)

# ... or IPython's question mark
nest.Models?

# list neuron models
nest.Models()

# choose LIF neuron with exponential synaptic currents: 'iaf_psc_exp'
# look in documentation for model description
# or (if not compiled with MPI)
nest.help('iaf_psc_exp')

"""
Explanation: Getting help
End of explanation
"""


# before creating a new network,
# reset the simulation kernel / remove all nodes
nest.ResetKernel()

# create the neuron
neuron = nest.Create('iaf_psc_exp')

# investigate the neuron

# Create() just returns a list (tuple) with handles to the new nodes
# (handles = integer numbers called ids)
neuron

# current dynamical state/parameters of the neuron
# note that the membrane voltage is at -70 mV
nest.GetStatus(neuron)

"""
Explanation: Creating a neuron
End of explanation
"""


# create a spike generator
spikegenerator = nest.Create('spike_generator')

# check out 'spike_times' in its parameters
nest.GetStatus(spikegenerator)

# set the spike times at 10 and 50 ms
nest.SetStatus(spikegenerator, {'spike_times': [10., 50.]})

"""
Explanation: Creating a spikegenerator
End of explanation
"""


# create a voltmeter for recording
voltmeter = nest.Create('voltmeter')

# investigate the voltmeter
voltmeter

# see that it records membrane voltage, senders, times
nest.GetStatus(voltmeter)

"""
Explanation: Creating a voltmeter
End of explanation
"""


# investigate Connect() function
nest.Connect?

# connect spike generator and voltmeter to the neuron
nest.Connect(spikegenerator, neuron, syn_spec={'weight': 1e3})

nest.Connect(voltmeter, neuron)

"""
Explanation: Connecting
End of explanation
"""


# run simulation for 100 ms
nest.Simulate(100.)

# look at nest's KernelStatus:
# network_size (root node, neuron, spike generator, voltmeter)
# num_connections
# time (simulation duration)
nest.GetKernelStatus()

# note that voltmeter has recorded 99 events
nest.GetStatus(voltmeter)

# read out recording time and voltage from voltmeter
times = nest.GetStatus(voltmeter)[0]['events']['times']
voltages = nest.GetStatus(voltmeter)[0]['events']['V_m']

"""
Explanation: Simulating
End of explanation
"""


# plot results
# units can be found in documentation
pylab.plot(times, voltages, label='Neuron 1')
pylab.xlabel('Time (ms)')
pylab.ylabel('Membrane potential (mV)')
pylab.title('Membrane potential')
pylab.legend()

# create the same plot with NEST's build-in plotting function
import nest.voltage_trace

nest.voltage_trace.from_device(voltmeter)

"""
Explanation: Plotting
End of explanation
"""


