1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
|
"""
Brian 2
"""
import logging
import signal
def _check_dependencies():
"""Check basic dependencies"""
import sys
missing = []
try:
import numpy
except ImportError as ex:
sys.stderr.write(f"Importing numpy failed: '{ex}'\n")
missing.append("numpy")
try:
import sympy
except ImportError as ex:
sys.stderr.write(f"Importing sympy failed: '{ex}'\n")
missing.append("sympy")
try:
import pyparsing
except ImportError as ex:
sys.stderr.write(f"Importing pyparsing failed: '{ex}'\n")
missing.append("pyparsing")
try:
import jinja2
except ImportError as ex:
sys.stderr.write(f"Importing Jinja2 failed: '{ex}'\n")
missing.append("jinja2")
if len(missing):
raise ImportError(
f"Some required dependencies are missing:\n{', '.join(missing)}"
)
_check_dependencies()
try:
from pylab import *
except ImportError:
# Do the non-matplotlib pylab imports manually
# don't let numpy's datetime hide stdlib
import datetime
import numpy.ma as ma
from numpy import *
from numpy.fft import *
from numpy.linalg import *
from numpy.random import *
# Make sure that Brian's unit-aware functions are used, even when directly
# using names prefixed with numpy or np
import brian2.numpy_ as numpy
import brian2.numpy_ as np
try:
from ._version import __version__, __version_tuple__
except ImportError:
try:
from setuptools_scm import get_version
__version__ = get_version(
root="..",
relative_to=__file__,
version_scheme="post-release",
local_scheme="no-local-version",
)
__version_tuple__ = tuple(int(x) for x in __version__.split(".")[:3])
except ImportError:
logging.getLogger("brian2").warning(
"Cannot determine Brian version, running from source and "
"setuptools_scm is not installed."
)
__version__ = "unknown"
__version_tuple__ = (0, 0, 0)
# delete some annoying names from the namespace
if "x" in globals():
del x
if "f" in globals():
del f
if "rate" in globals():
del rate
__docformat__ = "restructuredtext en"
from brian2.only import *
from brian2.only import test
# Check for outdated dependency versions
def _check_dependency_version(name, version):
import sys
from packaging.version import Version
from .core.preferences import prefs
from .utils.logger import get_logger
logger = get_logger(__name__)
module = sys.modules[name]
if not isinstance(module.__version__, str): # mocked module
return
if not Version(module.__version__) >= Version(version):
message = (
f"{name} is outdated (got version {module.__version__}, need version"
f" {version})"
)
if prefs.core.outdated_dependency_error:
raise ImportError(message)
else:
logger.warn(message, "outdated_dependency")
def _check_dependency_versions():
for name, version in [("numpy", "1.10"), ("sympy", "1.2"), ("jinja2", "2.7")]:
_check_dependency_version(name, version)
_check_dependency_versions()
# Initialize the logging system
BrianLogger.initialize()
logger = get_logger(__name__)
# Check the caches
def _get_size_recursively(dirname):
import os
total_size = 0
for dirpath, _, filenames in os.walk(dirname):
for fname in filenames:
# When other simulations are running, files may disappear while
# we walk through the directory (in particular with Cython, where
# we delete the source files after compilation by default)
try:
size = os.path.getsize(os.path.join(dirpath, fname))
total_size += size
except OSError:
pass # ignore the file
return total_size
#: Stores the cache directory for code generation targets
_cache_dirs_and_extensions = {}
def check_cache(target):
cache_dir, _ = _cache_dirs_and_extensions.get(target, (None, None))
if cache_dir is None:
return
size = _get_size_recursively(cache_dir)
size_in_mb = int(round(size / 1024.0 / 1024.0))
if size_in_mb > prefs.codegen.max_cache_dir_size:
logger.info(
f"Cache size for target '{target}': {size_in_mb} MB.\n"
f"You can call clear_cache('{target}') to delete all "
"files from the cache or manually delete files in the "
f"'{cache_dir}' directory."
)
else:
logger.debug(f"Cache size for target '{target}': {size_in_mb} MB")
def clear_cache(target):
"""
Clears the on-disk cache with the compiled files for a given code generation
target.
Parameters
----------
target : str
The code generation target (e.g. ``'cython'``)
Raises
------
ValueError
If the given code generation target does not have an on-disk cache
IOError
If the cache directory contains unexpected files, suggesting that
deleting it would also delete files unrelated to the cache.
"""
import os
import shutil
cache_dir, extensions = _cache_dirs_and_extensions.get(target, (None, None))
if cache_dir is None:
raise ValueError(f'No cache directory registered for target "{target}".')
cache_dir = os.path.abspath(cache_dir) # just to make sure...
for folder, _, files in os.walk(cache_dir):
for f in files:
for ext in extensions:
if f.endswith(ext):
break
else:
raise OSError(
f"The cache directory for target '{target}' contains "
f"the file '{os.path.join(folder, f)}' of an unexpected type and "
"will therefore not be removed. Delete files in "
f"'{cache_dir}' manually"
)
logger.debug(f"Clearing cache for target '{target}' (directory '{cache_dir}').")
shutil.rmtree(cache_dir)
def _check_caches():
from brian2.codegen.runtime.cython_rt.extension_manager import (
get_cython_cache_dir,
get_cython_extensions,
)
for target, (dirname, extensions) in [
("cython", (get_cython_cache_dir(), get_cython_extensions()))
]:
_cache_dirs_and_extensions[target] = (dirname, extensions)
if prefs.codegen.max_cache_dir_size > 0:
check_cache(target)
_check_caches()
class _InterruptHandler:
"""
Class to turn a Ctrl+C interruption (SIGINT signal) into a `stop` signal for
a running simulation (i.e., finish simulating the current time step and then
stop). This handler is activated by default, but can be switched off by
setting the `core.stop_on_keyboard_interrupt` preference to ``False``.
Note that this will only handle interruptions during a `Network.run`,
interrupting at any other time will raise a `KeyboardInterrupt` in the
usual way. In case that finishing the current time step takes a long time
(or hangs for some reason), interrupting with Ctrl+C a second time will
force the usual interrupt, regardless of the preference setting.
"""
def __init__(self, previous_handler):
self.previous_handler = previous_handler
def __call__(self, signalnum, stack_frame):
if (
not prefs.core.stop_on_keyboard_interrupt
or not Network._globally_running
or Network._globally_stopped
):
self.previous_handler(signalnum, stack_frame)
else:
logging.getLogger("brian2").warning(
"Simulation stop requested. Press Ctrl+C again to interrupt."
)
Network._globally_stopped = True
_int_handler = _InterruptHandler(signal.getsignal(signal.SIGINT))
signal.signal(signal.SIGINT, _int_handler)
|