"""
A collection of functions to help writing simualtion scripts.
:copyright: Copyright 2006-2024 by the PyNN team, see AUTHORS.
:license: CeCILL, see LICENSE for details.
"""
from datetime import datetime
import os
import sys
from importlib import import_module
import logging
# If there is a settings.py file on the path, defaults will be
# taken from there.
try:
from settings import SMTPHOST, EMAIL
except ImportError:
SMTPHOST = None
EMAIL = None
def get_script_args(n_args, usage=''):
"""
Get command line arguments.
This works by finding the name of the main script and assuming any
arguments after this in sys.argv are arguments to the script.
It would be nicer to use optparse, but this doesn't seem to work too well
with nrniv or mpirun.
"""
calling_frame = sys._getframe(1)
if '__file__' in calling_frame.f_locals:
script = calling_frame.f_locals['__file__']
try:
script_index = sys.argv.index(script)
except ValueError:
try:
script_index = sys.argv.index(os.path.abspath(script))
except ValueError:
script_index = 0
else:
script_index = 0
args = sys.argv[script_index + 1:script_index + 1 + n_args]
if len(args) != n_args:
usage = usage or "Script requires %d arguments, you supplied %d" % (n_args, len(args))
raise Exception(usage)
return args
[docs]
def get_simulator(*arguments):
"""
Import and return a PyNN simulator backend module based on command-line
arguments.
The simulator name should be the first positional argument. If your script
needs additional arguments, you can specify them as (name, help_text) tuples.
If you need more complex argument handling, you should use argparse
directly.
Returns (simulator, command-line arguments)
"""
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("simulator",
help="neuron, nest, brian or another backend simulator")
for argument in arguments:
arg_name, help_text = argument[:2]
extra_args = {}
if len(argument) > 2:
extra_args = argument[2]
parser.add_argument(arg_name, help=help_text, **extra_args)
args = parser.parse_args()
sim = import_module("pyNN.%s" % args.simulator)
return sim, args
[docs]
def init_logging(logfile, debug=False, num_processes=1, rank=0, level=None):
"""
Simple configuration of logging.
"""
# allow logfile == None
# which implies output to stderr
# num_processes and rank should be obtained using mpi4py, rather than having them as arguments
if logfile:
if num_processes > 1:
logfile += '.%d' % rank
logfile = os.path.abspath(logfile)
# prefix log messages with mpi rank
mpi_prefix = ""
if num_processes > 1:
mpi_prefix = 'Rank %d of %d: ' % (rank, num_processes)
if debug:
log_level = logging.DEBUG
else:
log_level = logging.INFO
# allow user to override exact log_level
if level:
log_level = level
logging.basicConfig(
level=log_level,
format=mpi_prefix + '%(asctime)s %(levelname)-8s [%(name)s] %(message)s (%(pathname)s[%(lineno)d]:%(funcName)s)', # noqa: E501
filename=logfile,
filemode='w')
return logging.getLogger("PyNN")
def normalized_filename(root, basename, extension, simulator,
num_processes=None, use_iso8601=False):
"""
Generate a file path containing a timestamp and information about the
simulator used and the number of MPI processes.
The date is used as a sub-directory name, the date & time are included in the
filename.
If use_iso8601 is True, follow https://en.wikipedia.org/wiki/ISO_8601
"""
timestamp = datetime.now()
if use_iso8601:
date = timestamp.strftime("%Y-%m-%d")
date_time = timestamp.strftime("%Y-%m-%dT%H:%M:%S")
else:
date = timestamp.strftime("%Y%m%d")
date_time = timestamp.strftime("%Y%m%d-%H%M%S")
if num_processes:
np = "_np%d" % num_processes
else:
np = ""
return os.path.join(root,
date,
"%s_%s%s_%s.%s" % (basename,
simulator,
np,
date_time,
extension))
[docs]
def notify(
msg="Simulation finished.",
subject="Simulation finished.",
smtphost=SMTPHOST,
address=EMAIL,
):
"""Send an e-mail stating that the simulation has finished."""
if not (smtphost and address):
print(
"SMTP host and/or e-mail address not specified.\n"
"Unable to send notification message."
)
else:
import smtplib
msg = ("From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n") % (
address,
address,
subject,
) + msg
msg += "\nTimestamp: %s" % datetime.now().strftime("%H:%M:%S, %F")
server = smtplib.SMTP(smtphost)
server.sendmail(address, address, msg)
server.quit()