Skip to content

Commit 4ab51bd

Browse files
author
Lucas Rebscher
committed
Clean up experiments backend code
1 parent 1c62c73 commit 4ab51bd

4 files changed

Lines changed: 54 additions & 106 deletions

File tree

netpyne_ui/experiments.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,20 @@
2020
NET_PARAMS_FILE = "netParams.json"
2121
EXPERIMENT_FILE = "experiment.json"
2222

23+
# Id for the base model trial created for an Experiment without parameters.
2324
BASE_TRIAL_ID = "model_output"
2425

26+
2527
class ExperimentsError(Exception):
28+
""" Base Exception for any specific Experiment errors. """
2629
pass
2730

2831

2932
def get_experiments() -> List[dict]:
33+
"""Returns the current list of experiments.
34+
35+
Scans the experiments folder to read experiments from disk and includes experiment in design.
36+
"""
3037
# Only update Experiments stored on filesystem
3138
stored_experiments = _scan_experiments_directory()
3239
model.experiments = [
@@ -77,6 +84,11 @@ def edit_experiment(name: str, experiment: dict):
7784

7885

7986
def replace_current_with(name: str):
87+
"""Replaces the experiment in design with a new experiment based on experiment with `name`.
88+
89+
:param name: name of experiment to be cloned.
90+
:raises ExperimentsError: thrown if Experiment with `name` doesn't exist.
91+
"""
8092
exp = _get_by_name(name)
8193
if not exp:
8294
raise ExperimentsError(f"Experiment with name {name} does not exist")
@@ -116,6 +128,7 @@ def any_in_state(states: List[model.ExperimentState]) -> model.Experiment:
116128

117129

118130
def set_to_error(experiment: model.Experiment):
131+
"""Sets the state of `experiment` to ERROR. """
119132
path = os.path.join(
120133
constants.NETPYNE_WORKDIR_PATH, constants.EXPERIMENTS_FOLDER, experiment.name
121134
)
@@ -216,7 +229,7 @@ def _parse_experiment(directory: str) -> model.Experiment:
216229
* netParams.json
217230
* simConfig.json
218231
* json file for each trial in case of batch
219-
* output files for each trial (if available)
232+
* data files for each trial (if available)
220233
221234
:raises ExperimentsError
222235
"""
@@ -230,13 +243,6 @@ def _parse_experiment(directory: str) -> model.Experiment:
230243
except IOError:
231244
raise ExperimentsError(f"Could not find {EXPERIMENT_FILE}")
232245

233-
with open(os.path.join(path, NET_PARAMS_FILE), "r") as f:
234-
net_params = json.load(f)
235-
236-
with open(os.path.join(path, SIM_CONFIG_FILE), "r") as f:
237-
sim_config = json.load(f)
238-
239-
run_cfg = experiment_config["runCfg"]
240246
del experiment_config["runCfg"]
241247

242248
# Convert timestamp to datetime
@@ -251,7 +257,7 @@ def _parse_experiment(directory: str) -> model.Experiment:
251257

252258

253259
def _delete_experiment_folder(experiment: model.Experiment):
254-
"""Recursively deletes the associated experiment folder."""
260+
"""Recursively deletes the associated `experiment` folder."""
255261

256262
def onerror(func, path, exc_info):
257263
# TODO: error handling
@@ -271,6 +277,11 @@ def _create_base_model_trial() -> model.Trial:
271277

272278

273279
def _create_trials(experiment: model.Experiment) -> List[model.Trial]:
280+
"""Gerates based on `experiment.params` all possible trial combinations.
281+
282+
:param experiment: given experiment.
283+
:return: list of generated trials.
284+
"""
274285
params = copy.deepcopy(experiment.params)
275286
params = process_params(params)
276287

@@ -314,6 +325,12 @@ def _create_trials(experiment: model.Experiment) -> List[model.Trial]:
314325

315326

316327
def process_params(params: List[model.ExplorationParameter]) -> List[model.ExplorationParameter]:
328+
"""Cleaning, filtering and converting of experiment `params`.
329+
330+
:param params: given parameters.
331+
:raises ExperimentsError: thrown for invalid parameter configuration.
332+
:return: processed list of params.
333+
"""
317334
params = [p for p in params if p.mapsTo != '']
318335

319336
for param in params:

netpyne_ui/model.py

Lines changed: 2 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -5,87 +5,6 @@
55

66

77
def register(metadata):
8-
metadata['batch_config'] = {
9-
"label": "Batch configuration",
10-
"help": "",
11-
"suggestions": "",
12-
"hintText": "",
13-
'children': {
14-
'enabled': {
15-
"label": "Run as batch",
16-
"help": "Activates batch",
17-
"suggestions": "",
18-
"hintText": "",
19-
"type": "bool"
20-
},
21-
'name': {
22-
"label": "Name of the batch",
23-
"help": "",
24-
"suggestions": "",
25-
"hintText": "",
26-
"type": "str"
27-
},
28-
'saveFolder': {
29-
"label": "Save folder",
30-
"help": "",
31-
"suggestions": "",
32-
"hintText": "",
33-
"type": "str"
34-
},
35-
'seed': {
36-
"label": "Seed",
37-
"help": "",
38-
"suggestions": "",
39-
"hintText": "",
40-
"type": "int"
41-
},
42-
'method': {
43-
"label": "Exploration method",
44-
"help": "",
45-
"suggestions": "",
46-
"hintText": "",
47-
"type": "str"
48-
}
49-
}
50-
}
51-
52-
metadata["run_config"] = {
53-
"label": "Run configuration",
54-
"help": "",
55-
"suggestions": "",
56-
"hintText": "",
57-
'children': {
58-
'parallel': {
59-
"label": "Parallel",
60-
"help": "",
61-
"suggestions": "",
62-
"hintText": "",
63-
"type": "bool"
64-
},
65-
'asynchronous': {
66-
"label": "Run in background",
67-
"help": "",
68-
"suggestions": "",
69-
"hintText": "",
70-
"type": "bool"
71-
},
72-
'type': {
73-
"label": "Execution type",
74-
"help": "",
75-
"suggestions": "",
76-
"hintText": "",
77-
"type": "str"
78-
},
79-
'cores': {
80-
"label": "Number of cores",
81-
"help": "",
82-
"suggestions": "",
83-
"hintText": "",
84-
"type": "int"
85-
}
86-
}
87-
}
88-
898
# Modification of metadata to enable validation of experiment parameters.
909
metadata['netParams']['children']['popParams']['container'] = True
9110
metadata['netParams']['children']['cellParams']['container'] = True
@@ -96,6 +15,7 @@ def register(metadata):
9615
metadata['netParams']['children']['stimSourceParams']['container'] = True
9716
metadata['netParams']['children']['stimTargetParams']['container'] = True
9817

18+
9919
class ExperimentState:
10020
DESIGN = "DESIGN"
10121
PENDING = "PENDING"
@@ -136,7 +56,7 @@ class Trial:
13656
# [{ "paramX": 0.2, "paramY": "0.4"}]
13757
params: list = field(default_factory=list)
13858
indices: list = field(default_factory=list)
139-
59+
14060

14161
@dataclass
14262
class Experiment:

netpyne_ui/netpyne_geppetto.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ def simulateNetPyNEModelInGeppetto(self, args):
302302
return utils.getJSONError(message, sys.exc_info())
303303

304304
def _prepare_simulation_files(self, experiment: model.Experiment = None, use_prev_inst: bool = False) -> str:
305+
"""Prepares template files and netpyne model files for a single simulation """
305306
exp = copy.deepcopy(experiment)
306307
# Remove parameter & trials for single run
307308
exp.params = []
@@ -358,6 +359,13 @@ def _prepare_simulation_files(self, experiment: model.Experiment = None, use_pre
358359
return save_folder_path
359360

360361
def _prepare_batch_files(self, experiment: model.Experiment) -> str:
362+
"""Creates template files and netpyne model files in the experiment folder.
363+
364+
Only for an experiment consisting of many trials.
365+
366+
:param experiment: given experiment
367+
:return: working directory path
368+
"""
361369
exp = copy.deepcopy(experiment)
362370
exp.params = self.experiments.process_params(exp.params)
363371

netpyne_ui/simulations.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
import os
44
import multiprocessing
55
import platform
6-
from dataclasses import dataclass
76

87
from netpyne import sim
98
from netpyne_ui import constants
109

10+
# Available netpyne.batch.Batch methods.
1111
MPI_DIRECT = "mpi_direct"
1212
MPI_BULLETIN = "mpi_bulletin"
1313

@@ -27,15 +27,6 @@ class InvalidConfigError(Exception):
2727
""" Thrown if invalid number of CPUs were specified. """
2828

2929

30-
@dataclass
31-
class Simulation:
32-
cores: int
33-
name: str
34-
asynchronous: bool
35-
batch: bool
36-
subprocess = None
37-
38-
3930
class LocalSimulationPool:
4031
""" Pool that manages simulation running on the same machine as Netpyne-UI. """
4132

@@ -47,18 +38,25 @@ def __init__(self):
4738

4839
def run(self, parallel, cores, method="", batch=False, asynchronous=False, working_directory=None):
4940
if int(cores) > self.cpus:
50-
raise InvalidConfigError(f"Specified {cores} cores, but only {self.cpus} are available")
41+
raise InvalidConfigError(
42+
f"Specified {cores} cores, but only {self.cpus} are available")
5143

5244
logging.info(f"Scheduling simulation on {cores} cores ...")
5345

5446
if batch or asynchronous or parallel:
5547
if method == MPI_DIRECT:
56-
self._run_in_subprocess(_python_command(working_directory), asynchronous=asynchronous, working_directory=working_directory)
48+
self._run_in_subprocess(
49+
_python_command(working_directory), asynchronous=asynchronous, working_directory=working_directory
50+
)
5751
elif method == MPI_BULLETIN:
5852
if parallel:
59-
self._run_in_subprocess(_bulletin_board_cmd(cores, working_directory), asynchronous=asynchronous, working_directory=working_directory)
53+
self._run_in_subprocess(
54+
_bulletin_board_cmd(cores, working_directory), asynchronous=asynchronous, working_directory=working_directory
55+
)
6056
else:
61-
self._run_in_subprocess(_python_command(working_directory), asynchronous=asynchronous, working_directory=working_directory)
57+
self._run_in_subprocess(
58+
_python_command(working_directory), asynchronous=asynchronous, working_directory=working_directory
59+
)
6260
else:
6361
raise InvalidConfigError(f"Unsupported method {method}")
6462
else:
@@ -89,7 +87,7 @@ def _run_in_same_process(self):
8987
sim.simulate()
9088
sim.saveData()
9189

92-
def _run_in_subprocess(self, cmds, asynchronous=False, working_directory: str=None):
90+
def _run_in_subprocess(self, cmds, asynchronous=False, working_directory: str = None):
9391
if self.is_running():
9492
logging.info("Another simulation is still running")
9593
return False
@@ -110,6 +108,11 @@ def _run_in_subprocess(self, cmds, asynchronous=False, working_directory: str=No
110108

111109

112110
def _bulletin_board_cmd(cores, working_directory=None):
111+
""" Creates command to run batch or single simulations in parallel.
112+
113+
Uses mpiexec that implements the OpenMPI protocol to schedule simulations.
114+
Uses the master/worker pattern.
115+
"""
113116
return ["mpiexec", "-n", str(cores), "--use-hwthread-cpus", "nrniv", "-python", "-mpi", _script_path(working_directory)]
114117

115118

0 commit comments

Comments
 (0)