Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pynestml/codegeneration/nest_desktop_code_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,6 @@ def _get_neuron_model_namespace(self, neuron: ASTModel) -> Dict:
namespace["nestml_version"] = pynestml.__version__
namespace["neuronName"] = neuron.get_name()
namespace["neuron"] = neuron
namespace["parameters"], namespace["state"] = PythonStandaloneTargetTools.get_neuron_parameters_and_state(neuron.get_name())
namespace["parameters"], namespace["state"] = PythonStandaloneTargetTools.get_neuron_parameters_and_state(neuron.file_path)

return namespace
57 changes: 37 additions & 20 deletions pynestml/codegeneration/python_standalone_target_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,61 +18,78 @@
#
# You should have received a copy of the GNU General Public License
# along with NEST. If not, see <http://www.gnu.org/licenses/>.

import importlib
import multiprocessing
import os
import sys
import tempfile

from pynestml.frontend.frontend_configuration import FrontendConfiguration
from pynestml.frontend.pynestml_frontend import generate_python_standalone_target
from pynestml.meta_model.ast_model import ASTModel
from pynestml.utils.logger import LoggingLevel, Logger
from pynestml.utils.model_parser import ModelParser


class PythonStandaloneTargetTools:
"""
Helper functions for Python standalone target.
r"""
Helper functions for the Python standalone target.
"""
@classmethod
def _get_model_parameters_and_state(cls, model_name: str):
input_path = os.path.join(os.path.realpath(os.path.join(os.path.dirname(__file__), os.path.join(
os.pardir, os.pardir, "models", "neurons", model_name + ".nestml"))))
suffix = "_nestml"
def _get_model_parameters_and_state(cls, nestml_file_name: str):
suffix = ""
module_name = FrontendConfiguration.get_module_name()
target_path = FrontendConfiguration.get_module_name()
generate_python_standalone_target(input_path=input_path,
target_path=target_path,
suffix=suffix,
module_name=module_name,
logging_level="INFO")
target_path = tempfile.mkdtemp(prefix="nestml_python_target_", suffix="", dir=".") # dir = "." is necessary for Python import
# this has to run in a different process, because otherwise the frontend configuration gets overwritten
process = multiprocessing.Process(target=generate_python_standalone_target, kwargs={"input_path": nestml_file_name,
"target_path": target_path,
"suffix": suffix,
"module_name": module_name,
"logging_level": "ERROR"})
process.start()
process.join() # wait for code generation to complete

ast_compilation_unit = ModelParser.parse_file(nestml_file_name)
if ast_compilation_unit is None or len(ast_compilation_unit.get_model_list()) == 0:
raise Exception("Error(s) occurred during code generation; please check error messages")

py_module_name = module_name + "." + model_name + suffix
model: ASTModel = ast_compilation_unit.get_model_list()[0]
model_name = model.get_name()

py_module_name = os.path.basename(target_path) + "." + model_name
module = importlib.import_module(py_module_name)
neuron_name = "Neuron_" + model_name + suffix + "(1.0)"
neuron_name = "Neuron_" + model_name + "(1.0)" # 1.0 is a dummy value for the timestep
neuron = eval("module." + neuron_name)

parameters_list = [p for p in dir(neuron.Parameters_) if not "__" in p]
parameters = {p: getattr(neuron, "get_" + p)() for p in parameters_list}

state_list = [p for p in dir(neuron.State_) if not "__" in p]
if "ode_state_variable_name_to_index" in dir(neuron.State_):
state_list = neuron.State_.ode_state_variable_name_to_index.keys()
else:
state_list = [p for p in dir(neuron.State_) if not "__" in p]
state_vars = {p: getattr(neuron, "get_" + p)() for p in state_list}

return parameters, state_vars

@classmethod
def get_neuron_parameters_and_state(cls, neuron_model_name: str) -> tuple[dict, dict]:
def get_neuron_parameters_and_state(cls, nestml_file_name: str) -> tuple[dict, dict]:
r"""
Get the parameters for the given neuron model. The code is generated for the model for Python standalone target
The parameters and state variables are then queried by creating the neuron in Python standalone simulator.
:param neuron_model_name: Name of the neuron model
:param nestml_file_name: File name of the neuron model
:return: A dictionary of parameters and state variables
"""
parameters, state = cls._get_model_parameters_and_state(neuron_model_name)
parameters, state = cls._get_model_parameters_and_state(nestml_file_name)

if not parameters or not state:
Logger.log_message(None, -1,
"An error occurred while creating the neuron for python standalone target: " + neuron_model_name,
"An error occurred while creating the neuron for Python standalone target: " + nestml_file_name,
None, LoggingLevel.ERROR)
sys.exit(1)
else:
Logger.log_message(None, -1, "The model parameters were successfully queried from python standalone target.",
Logger.log_message(None, -1, "The model parameters were successfully queried from Python standalone target.",
None, LoggingLevel.INFO)

return parameters, state
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def test_nest_desktop_code_generator(self):
target_path=target_path,
target_platform=target_platform,
module_name=target_path,
logging_level="INFO")
logging_level="DEBUG")

# Read the parameters from the generated json file and match them with the actual values
with open(os.path.join(target_path, "iaf_psc_exp_neuron.json")) as f:
Expand Down