|
18 | 18 | # |
19 | 19 | # You should have received a copy of the GNU General Public License |
20 | 20 | # along with NEST. If not, see <http://www.gnu.org/licenses/>. |
| 21 | + |
21 | 22 | import importlib |
| 23 | +import multiprocessing |
22 | 24 | import os |
23 | 25 | import sys |
| 26 | +import tempfile |
24 | 27 |
|
25 | 28 | from pynestml.frontend.frontend_configuration import FrontendConfiguration |
26 | 29 | from pynestml.frontend.pynestml_frontend import generate_python_standalone_target |
| 30 | +from pynestml.meta_model.ast_model import ASTModel |
27 | 31 | from pynestml.utils.logger import LoggingLevel, Logger |
| 32 | +from pynestml.utils.model_parser import ModelParser |
28 | 33 |
|
29 | 34 |
|
30 | 35 | class PythonStandaloneTargetTools: |
31 | | - """ |
32 | | - Helper functions for Python standalone target. |
| 36 | + r""" |
| 37 | + Helper functions for the Python standalone target. |
33 | 38 | """ |
34 | 39 | @classmethod |
35 | | - def _get_model_parameters_and_state(cls, model_name: str): |
36 | | - input_path = os.path.join(os.path.realpath(os.path.join(os.path.dirname(__file__), os.path.join( |
37 | | - os.pardir, os.pardir, "models", "neurons", model_name + ".nestml")))) |
38 | | - suffix = "_nestml" |
| 40 | + def _get_model_parameters_and_state(cls, nestml_file_name: str): |
| 41 | + suffix = "" |
39 | 42 | module_name = FrontendConfiguration.get_module_name() |
40 | | - target_path = FrontendConfiguration.get_module_name() |
41 | | - generate_python_standalone_target(input_path=input_path, |
42 | | - target_path=target_path, |
43 | | - suffix=suffix, |
44 | | - module_name=module_name, |
45 | | - logging_level="INFO") |
| 43 | + target_path = tempfile.mkdtemp(prefix="nestml_python_target_", suffix="", dir=".") # dir = "." is necessary for Python import |
| 44 | + # this has to run in a different process, because otherwise the frontend configuration gets overwritten |
| 45 | + process = multiprocessing.Process(target=generate_python_standalone_target, kwargs={"input_path": nestml_file_name, |
| 46 | + "target_path": target_path, |
| 47 | + "suffix": suffix, |
| 48 | + "module_name": module_name, |
| 49 | + "logging_level": "ERROR"}) |
| 50 | + process.start() |
| 51 | + process.join() # wait for code generation to complete |
| 52 | + |
| 53 | + ast_compilation_unit = ModelParser.parse_file(nestml_file_name) |
| 54 | + if ast_compilation_unit is None or len(ast_compilation_unit.get_model_list()) == 0: |
| 55 | + raise Exception("Error(s) occurred during code generation; please check error messages") |
46 | 56 |
|
47 | | - py_module_name = module_name + "." + model_name + suffix |
| 57 | + model: ASTModel = ast_compilation_unit.get_model_list()[0] |
| 58 | + model_name = model.get_name() |
| 59 | + |
| 60 | + py_module_name = os.path.basename(target_path) + "." + model_name |
48 | 61 | module = importlib.import_module(py_module_name) |
49 | | - neuron_name = "Neuron_" + model_name + suffix + "(1.0)" |
| 62 | + neuron_name = "Neuron_" + model_name + "(1.0)" # 1.0 is a dummy value for the timestep |
50 | 63 | neuron = eval("module." + neuron_name) |
| 64 | + |
51 | 65 | parameters_list = [p for p in dir(neuron.Parameters_) if not "__" in p] |
52 | 66 | parameters = {p: getattr(neuron, "get_" + p)() for p in parameters_list} |
53 | 67 |
|
54 | | - state_list = [p for p in dir(neuron.State_) if not "__" in p] |
| 68 | + if "ode_state_variable_name_to_index" in dir(neuron.State_): |
| 69 | + state_list = neuron.State_.ode_state_variable_name_to_index.keys() |
| 70 | + else: |
| 71 | + state_list = [p for p in dir(neuron.State_) if not "__" in p] |
55 | 72 | state_vars = {p: getattr(neuron, "get_" + p)() for p in state_list} |
56 | 73 |
|
57 | 74 | return parameters, state_vars |
58 | 75 |
|
59 | 76 | @classmethod |
60 | | - def get_neuron_parameters_and_state(cls, neuron_model_name: str) -> tuple[dict, dict]: |
| 77 | + def get_neuron_parameters_and_state(cls, nestml_file_name: str) -> tuple[dict, dict]: |
61 | 78 | r""" |
62 | 79 | Get the parameters for the given neuron model. The code is generated for the model for Python standalone target |
63 | 80 | The parameters and state variables are then queried by creating the neuron in Python standalone simulator. |
64 | | - :param neuron_model_name: Name of the neuron model |
| 81 | + :param nestml_file_name: File name of the neuron model |
65 | 82 | :return: A dictionary of parameters and state variables |
66 | 83 | """ |
67 | | - parameters, state = cls._get_model_parameters_and_state(neuron_model_name) |
| 84 | + parameters, state = cls._get_model_parameters_and_state(nestml_file_name) |
68 | 85 |
|
69 | 86 | if not parameters or not state: |
70 | 87 | Logger.log_message(None, -1, |
71 | | - "An error occurred while creating the neuron for python standalone target: " + neuron_model_name, |
| 88 | + "An error occurred while creating the neuron for Python standalone target: " + nestml_file_name, |
72 | 89 | None, LoggingLevel.ERROR) |
73 | 90 | sys.exit(1) |
74 | 91 | else: |
75 | | - Logger.log_message(None, -1, "The model parameters were successfully queried from python standalone target.", |
| 92 | + Logger.log_message(None, -1, "The model parameters were successfully queried from Python standalone target.", |
76 | 93 | None, LoggingLevel.INFO) |
77 | 94 |
|
78 | 95 | return parameters, state |
0 commit comments