From 440f5ba3726690e84bbab689864026d08ff23ebd Mon Sep 17 00:00:00 2001 From: Gianluca Ficarelli Date: Mon, 26 Feb 2024 11:54:24 +0100 Subject: [PATCH 1/6] Allow LocalAccessPoint to load EModels from Nexus staged data --- bluepyemodel/access_point/local.py | 135 ++++++++++++------ .../model/neuron_model_configuration.py | 12 +- 2 files changed, 100 insertions(+), 47 deletions(-) diff --git a/bluepyemodel/access_point/local.py b/bluepyemodel/access_point/local.py index 3f2407f4..2137717c 100644 --- a/bluepyemodel/access_point/local.py +++ b/bluepyemodel/access_point/local.py @@ -19,6 +19,7 @@ import glob import json import logging +from functools import cached_property from itertools import chain from pathlib import Path @@ -50,6 +51,8 @@ "myelinated": "myelin", } +SUPPORTED_MORPHOLOGY_EXTENSIONS = (".asc", ".swc") + class LocalAccessPoint(DataAccessPoint): """Access point to access configuration files and e-models when stored locally.""" @@ -124,8 +127,6 @@ def __init__( self.legacy_dir_structure = legacy_dir_structure self.with_seeds = with_seeds - self.morph_path = None - if final_path is None: self.final_path = self.emodel_dir / "final.json" else: @@ -141,6 +142,43 @@ def __init__( self.pipeline_settings = self.load_pipeline_settings() self.unfrozen_params = None + @cached_property + def morph_dir(self): + """Return the morphology directory as read from the recipes, or fallback to 'morphology'.""" + recipes = self.get_recipes() + return Path(self.emodel_dir, recipes.get("morph_path", "morphology")) + + @cached_property + def morph_path(self): + """Return the path to the morphology file as read from the recipes.""" + recipes = self.get_recipes() + + morph_file = None + if isinstance(recipes["morphology"], str): + morph_file = recipes["morphology"] + else: + for _, morph_file in recipes["morphology"]: + if morph_file.endswith(SUPPORTED_MORPHOLOGY_EXTENSIONS): + break + + if not morph_file or not morph_file.endswith(SUPPORTED_MORPHOLOGY_EXTENSIONS): + raise FileNotFoundError(f"Morphology file not defined or not supported: {morph_file}") + + morph_path = self.morph_dir / morph_file + + if not morph_path.is_file(): + raise FileNotFoundError(f"Morphology file not found: {morph_path}") + if str(Path.cwd()) not in str(morph_path.resolve()) and self.emodel_metadata.iteration: + raise FileNotFoundError( + "When using a githash or iteration tag, the path to the morphology must be local" + " otherwise it cannot be archived during the creation of the githash. To solve" + " this issue, you can copy the morphology from " + f"{morph_path.resolve()} to {Path.cwd() / 'morphologies'} and update your " + "recipes." + ) + + return morph_path + def set_emodel(self, emodel): """Setter for the name of the emodel, check it exists (with or without seed) in recipe.""" _emodel = "_".join(emodel.split("_")[:2]) if self.with_seeds else emodel @@ -154,14 +192,48 @@ def set_emodel(self, emodel): def load_pipeline_settings(self): """ """ - - settings = self.get_recipes().get("pipeline_settings", {}) - + recipes = self.get_recipes() + settings = recipes.get("pipeline_settings", {}) + if isinstance(settings, str): + # read the pipeline settings from file + settings = self.get_json("pipeline_settings") if "morph_modifiers" not in settings: - settings["morph_modifiers"] = self.get_recipes().get("morph_modifiers", None) - + settings["morph_modifiers"] = recipes.get("morph_modifiers", None) return EModelPipelineSettings(**settings) + def _config_to_final(self, config): + """Convert the configuration stored in EM_*.json to the format used for final.json.""" + metadata = self.emodel_metadata + return { + metadata.emodel: { + "iteration": metadata.iteration, + "emodel": metadata.emodel, + "etype": metadata.etype, + "ttype": metadata.ttype, + "mtype": metadata.mtype, + "species": metadata.species, + "brain_region": metadata.brain_region, + "synapse_class": metadata.synapse_class, + "score": config["fitness"], # float + "parameters": config["parameter"], # list[dict] + "fitness": config["score"], # list[dict] + "features": config["features"], # list[dict] + "validation_fitness": config["scoreValidation"], # list[dict] + "validated": config["passedValidation"], # bool + "seed": config["seed"], # int + } + } + + def get_final_content(self, lock_file=True): + """Return the final content from recipes if available, or fallback to final.json""" + recipes = self.get_recipes() + if "final" in recipes: + if self.final_path and self.final_path.is_file(): + logger.warning("Ignored %s, using file from recipes", self.final_path) + data = self.get_json("final") + return self._config_to_final(data) + return self.get_final(lock_file=lock_file) + def get_final(self, lock_file=True): """Get emodel dictionary from final.json.""" if self.final_path is None: @@ -380,18 +452,13 @@ def get_available_mechanisms(self): def get_available_morphologies(self): """Get the list of names of available morphologies""" - - names = [] - - morph_dir = self.emodel_dir / "morphology" + morph_dir = self.morph_dir if not morph_dir.is_dir(): return None - for morph_file in glob.glob(str(morph_dir / "*.asc")) + glob.glob(str(morph_dir / "*.swc")): - names.append(Path(morph_file).stem) - - return set(names) + patterns = ["*" + ext for ext in SUPPORTED_MORPHOLOGY_EXTENSIONS] + return {morph_file.stem for pattern in patterns for morph_file in morph_dir.glob(pattern)} def get_model_configuration(self): """Get the configuration of the model, including parameters, mechanisms and distributions""" @@ -415,7 +482,7 @@ def get_model_configuration(self): if isinstance(parameters["mechanisms"], dict): configuration.init_from_legacy_dict(parameters, self.get_morphologies()) else: - configuration.init_from_dict(parameters) + configuration.init_from_dict(parameters, self.get_morphologies()) configuration.mapping_multilocation = self.get_recipes().get("multiloc_map", None) @@ -555,28 +622,6 @@ def get_morphologies(self): """ recipes = self.get_recipes() - - if isinstance(recipes["morphology"], str): - morph_file = recipes["morphology"] - else: - morph_file = recipes["morphology"][0][1] - - if self.morph_path is None: - self.morph_path = Path(recipes["morph_path"]) / morph_file - if not self.morph_path.is_absolute(): - self.morph_path = Path(self.emodel_dir) / self.morph_path - else: - self.morph_path = Path(self.morph_path) - - if str(Path.cwd()) not in str(self.morph_path.resolve()) and self.emodel_metadata.iteration: - raise FileNotFoundError( - "When using a githash or iteration tag, the path to the morphology must be local" - " otherwise it cannot be archived during the creation of the githash. To solve" - " this issue, you can copy the morphology from " - f"{self.morph_path.resolve()} to {Path.cwd() / 'morphologies'} and update your " - "recipes." - ) - morphology_definition = { "name": self.morph_path.stem, "path": str(self.morph_path), @@ -628,7 +673,7 @@ def format_emodel_data(self, model_data): def get_emodel(self, lock_file=True): """Get dict with parameter of single emodel (including seed if any)""" - final = self.get_final(lock_file=lock_file) + final = self.get_final_content(lock_file=lock_file) if self.emodel_metadata.emodel in final: return self.format_emodel_data(final[self.emodel_metadata.emodel]) @@ -648,7 +693,7 @@ def get_emodels(self, emodels=None): emodels = [self.emodel_metadata.emodel] models = [] - for mod_data in self.get_final().values(): + for mod_data in self.get_final_content().values(): if mod_data["emodel"] in emodels: models.append(self.format_emodel_data(mod_data)) @@ -692,7 +737,7 @@ def has_model_configuration(self): return Path(recipes["params"]).is_file() def get_emodel_etype_map(self): - final = self.get_final() + final = self.get_final_content() return {emodel: emodel.split("_")[0] for emodel in final} def get_emodel_names(self): @@ -702,14 +747,14 @@ def get_emodel_names(self): dict: keys are emodel names with seed, values are names without seed. """ - final = self.get_final() + final = self.get_final_content() return {mod_name: mod.get("emodel", mod_name) for mod_name, mod in final.items()} def has_best_model(self, seed): """Check if the best model has been stored.""" - final = self.get_final() + final = self.get_final_content() model_name = self.get_model_name_for_final(seed) @@ -718,7 +763,7 @@ def has_best_model(self, seed): def is_checked_by_validation(self, seed): """Check if the emodel with a given seed has been checked by Validation task.""" - final = self.get_final() + final = self.get_final_content() model_name = self.get_model_name_for_final(seed) @@ -732,7 +777,7 @@ def is_validated(self): """Check if enough models have been validated.""" n_validated = 0 - final = self.get_final() + final = self.get_final_content() for _, entry in final.items(): if ( diff --git a/bluepyemodel/model/neuron_model_configuration.py b/bluepyemodel/model/neuron_model_configuration.py index a6999881..e74fc5fb 100644 --- a/bluepyemodel/model/neuron_model_configuration.py +++ b/bluepyemodel/model/neuron_model_configuration.py @@ -172,10 +172,17 @@ def _format_locations(locations): return locations - def init_from_dict(self, configuration_dict, auto_mechanism=False): + def init_from_dict(self, configuration_dict, morphology, auto_mechanism=False): """Instantiate the object from its dictionary form""" if "distributions" in configuration_dict: + # empty the list of distributions + if self.distributions and configuration_dict["distributions"]: + # remove default uniform distribution if added at + # https://github.com/BlueBrain/BluePyEModel/blob/ + # 15b8dd2824453a8bf097b2ede13dd7ecf5d07d05/bluepyemodel/access_point/local.py#L399 + logger.warning("Removing %s pre-existing distribution(s)", len(self.distributions)) + self.distributions = [] for distribution in configuration_dict["distributions"]: self.add_distribution( distribution["name"], @@ -206,7 +213,8 @@ def init_from_dict(self, configuration_dict, auto_mechanism=False): mechanism.get("ljp_corrected", None), ) - self.morphology = MorphologyConfiguration(**configuration_dict["morphology"]) + morphology_params = {**configuration_dict["morphology"], **morphology} + self.morphology = MorphologyConfiguration(**morphology_params) def init_from_legacy_dict(self, parameters, morphology): """Instantiate the object from its legacy dictionary form""" From 214f214e4a6fbe9f272189f8d8f412b300334006 Mon Sep 17 00:00:00 2001 From: Gianluca Ficarelli Date: Tue, 27 Feb 2024 14:14:40 +0100 Subject: [PATCH 2/6] Use vars(self.emodel_metadata) in _config_to_final --- bluepyemodel/access_point/local.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/bluepyemodel/access_point/local.py b/bluepyemodel/access_point/local.py index 2137717c..c1048a7e 100644 --- a/bluepyemodel/access_point/local.py +++ b/bluepyemodel/access_point/local.py @@ -203,17 +203,9 @@ def load_pipeline_settings(self): def _config_to_final(self, config): """Convert the configuration stored in EM_*.json to the format used for final.json.""" - metadata = self.emodel_metadata return { - metadata.emodel: { - "iteration": metadata.iteration, - "emodel": metadata.emodel, - "etype": metadata.etype, - "ttype": metadata.ttype, - "mtype": metadata.mtype, - "species": metadata.species, - "brain_region": metadata.brain_region, - "synapse_class": metadata.synapse_class, + self.emodel_metadata.emodel: { + **vars(self.emodel_metadata), "score": config["fitness"], # float "parameters": config["parameter"], # list[dict] "fitness": config["score"], # list[dict] From d0496ecc1c9c50deabae43ab3f3a538335866e8b Mon Sep 17 00:00:00 2001 From: Gianluca Ficarelli Date: Wed, 28 Feb 2024 17:28:52 +0100 Subject: [PATCH 3/6] Add tests --- tests/conftest.py | 23 +- tests/functional_tests/test_protocols.py | 4 +- .../test_protocols_from_nexus.py | 58 ++ tests/functional_tests/test_validation.py | 2 +- .../test_validation_from_nexus.py | 70 ++ .../config/nexus/EMC__emodel=L5PC.json | 554 ++++++++++++ .../config/nexus/EMPS__emodel=L5PC.json | 64 ++ .../config/nexus/EM__emodel=L5PC.json | 588 ++++++++++++ .../config/nexus/FCC__emodel=L5PC.json | 840 ++++++++++++++++++ tests/test_data/config/recipes_nexus.json | 15 + .../test_local_access_point_from_nexus.py | 114 +++ 11 files changed, 2325 insertions(+), 7 deletions(-) create mode 100644 tests/functional_tests/test_protocols_from_nexus.py create mode 100644 tests/functional_tests/test_validation_from_nexus.py create mode 100644 tests/test_data/config/nexus/EMC__emodel=L5PC.json create mode 100644 tests/test_data/config/nexus/EMPS__emodel=L5PC.json create mode 100644 tests/test_data/config/nexus/EM__emodel=L5PC.json create mode 100644 tests/test_data/config/nexus/FCC__emodel=L5PC.json create mode 100644 tests/test_data/config/recipes_nexus.json create mode 100644 tests/unit_tests/test_local_access_point_from_nexus.py diff --git a/tests/conftest.py b/tests/conftest.py index 7ba60384..e1250cb4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,6 +24,15 @@ from tests.utils import DATA, cwd +@pytest.fixture(scope="session") +def nrnivmodl(tmp_path_factory): + """Compile the mechanisms only once per session.""" + path = tmp_path_factory.mktemp("nrnivmodl_dir") + with cwd(path): + os.popen(f"nrnivmodl {DATA}/mechanisms").read() + return path + + @pytest.fixture def workspace(tmp_path): """Change the working directory to tmp_path. @@ -35,7 +44,7 @@ def workspace(tmp_path): @pytest.fixture -def emodel_dir(workspace): +def emodel_dir(workspace, nrnivmodl): """Copy the required files to workspace/emodel.""" dirs = ["config", "mechanisms", "morphology", "ephys_data"] files = ["final.json"] @@ -44,6 +53,7 @@ def emodel_dir(workspace): shutil.copytree(DATA / name, dst / name) for name in files: shutil.copyfile(DATA / name, dst / name) + shutil.copytree(nrnivmodl / "x86_64", workspace / "x86_64") yield dst @@ -72,7 +82,10 @@ def db_restart(emodel_dir): @pytest.fixture -def evaluator(db, emodel_dir): - os.popen(f"nrnivmodl {emodel_dir}/mechanisms").read() - db.get_mechanisms_directory = lambda: None - return get_evaluator_from_access_point(access_point=db) +def db_from_nexus(emodel_dir): + return get_access_point( + "local", + emodel="L5PC", + emodel_dir=emodel_dir, + recipes_path=emodel_dir / "config/recipes_nexus.json", + ) diff --git a/tests/functional_tests/test_protocols.py b/tests/functional_tests/test_protocols.py index 02b213e3..3e6b6eef 100644 --- a/tests/functional_tests/test_protocols.py +++ b/tests/functional_tests/test_protocols.py @@ -20,13 +20,15 @@ from numpy.testing import assert_allclose from pandas.testing import assert_frame_equal +from bluepyemodel.evaluation.evaluation import get_evaluator_from_access_point -def test_protocols(db, evaluator, tmp_path): +def test_protocols(db, tmp_path): logging.basicConfig(level=logging.DEBUG) logging.getLogger().setLevel(logging.DEBUG) params = db.get_emodel().parameters + evaluator = get_evaluator_from_access_point(access_point=db) responses = evaluator.run_protocols( protocols=evaluator.fitness_protocols.values(), param_values=params diff --git a/tests/functional_tests/test_protocols_from_nexus.py b/tests/functional_tests/test_protocols_from_nexus.py new file mode 100644 index 00000000..0f27a665 --- /dev/null +++ b/tests/functional_tests/test_protocols_from_nexus.py @@ -0,0 +1,58 @@ +""" +Copyright 2024, EPFL/Blue Brain Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import logging + +import pandas as pd +from numpy.testing import assert_allclose +from pandas.testing import assert_frame_equal + +from bluepyemodel.evaluation.evaluation import get_evaluator_from_access_point + + +def test_protocols(db_from_nexus, tmp_path): + logging.basicConfig(level=logging.DEBUG) + logging.getLogger().setLevel(logging.DEBUG) + + params = db_from_nexus.get_emodel().parameters + evaluator = get_evaluator_from_access_point(access_point=db_from_nexus) + + responses = evaluator.run_protocols( + protocols=evaluator.fitness_protocols.values(), param_values=params + ) + + # FIXME: remove print after clarifying the different result when executing the test alone + print(responses) + assert_allclose(responses["bpo_rmp"], -82.61402706564716, rtol=1e-06) + assert_allclose(responses["bpo_holding_current"], -0.05, rtol=1e-06) + assert_allclose(responses["bpo_rin"], 41.13626022273351, rtol=1e-06) + assert_allclose(responses["bpo_threshold_current"], 0.3757011543411314, rtol=1e-06) + + for prot_name in [ + "APWaveform_280.soma.v", + "IDrest_150.soma.v", + "IDrest_250.soma.v", + "IV_-100.soma.v", + "RinProtocol.soma.v", + "RMPProtocol.soma.v", + "SearchHoldingCurrent.soma.v", + "SearchThresholdCurrent.soma.v", + ]: + output_path = f"{tmp_path}/test_{prot_name}.csv" + responses[prot_name].response.to_csv(output_path, index=False) + expected_df = pd.read_csv(output_path) + response = responses[prot_name].response + assert_frame_equal(response, expected_df) diff --git a/tests/functional_tests/test_validation.py b/tests/functional_tests/test_validation.py index 833e95d9..a489860f 100644 --- a/tests/functional_tests/test_validation.py +++ b/tests/functional_tests/test_validation.py @@ -57,4 +57,4 @@ def test_validation(db): ) assert len(emodels) == 1 - assert emodels[0].passed_validation + assert emodels[0].passed_validation is True diff --git a/tests/functional_tests/test_validation_from_nexus.py b/tests/functional_tests/test_validation_from_nexus.py new file mode 100644 index 00000000..6d806c8a --- /dev/null +++ b/tests/functional_tests/test_validation_from_nexus.py @@ -0,0 +1,70 @@ +""" +Copyright 2024, EPFL/Blue Brain Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import logging + +from bluepyemodel.validation.validation import define_validation_function, validate + + +def _always_true_validation(model, threshold=5.0, validation_protocols_only=False): + return True + + +def test_define_validation_function(db_from_nexus): + + model = { + "scores": {"a": 0.0, "b": 4.9, "c": 0.5, "d": 9.9}, + "scores_validation": {"c": 0.5, "d": 9.9}, + } + + db_from_nexus.pipeline_settings.validation_function = _always_true_validation + + validation_function = define_validation_function(db_from_nexus) + + validated = bool( + validation_function( + model, + db_from_nexus.pipeline_settings.validation_threshold, + False, + ) + ) + + assert validated + + +def test_validation(db_from_nexus): + + logging.basicConfig(level=logging.DEBUG) + logging.getLogger().setLevel(logging.DEBUG) + + def _get_final_content(lock_file=True): + # enforce validated to None to ensure that the validation is run + result = get_final_content(lock_file=lock_file) + for key, value in result.items(): + assert "validated" in value + value["validated"] = None + return result + + get_final_content = db_from_nexus.get_final_content + db_from_nexus.get_final_content = _get_final_content + db_from_nexus.get_mechanisms_directory = lambda: None + emodels = validate( + access_point=db_from_nexus, + mapper=map, + ) + + assert len(emodels) == 1 + assert emodels[0].passed_validation is True diff --git a/tests/test_data/config/nexus/EMC__emodel=L5PC.json b/tests/test_data/config/nexus/EMC__emodel=L5PC.json new file mode 100644 index 00000000..560705df --- /dev/null +++ b/tests/test_data/config/nexus/EMC__emodel=L5PC.json @@ -0,0 +1,554 @@ +{ + "mechanisms": [ + { + "name": "pas", + "stochastic": false, + "location": "all", + "version": null, + "temperature": null, + "ljp_corrected": null, + "id": null + }, + { + "name": "CaDynamics_DC0", + "stochastic": false, + "location": "allact", + "version": null, + "temperature": null, + "ljp_corrected": null, + "id": null + }, + { + "name": "Ca_HVA2", + "stochastic": false, + "location": "allact", + "version": null, + "temperature": null, + "ljp_corrected": null, + "id": null + }, + { + "name": "Ca_LVAst", + "stochastic": false, + "location": "allact", + "version": null, + "temperature": null, + "ljp_corrected": null, + "id": null + }, + { + "name": "SKv3_1", + "stochastic": false, + "location": "somaxon", + "version": null, + "temperature": null, + "ljp_corrected": null, + "id": null + }, + { + "name": "SK_E2", + "stochastic": false, + "location": "somaxon", + "version": null, + "temperature": null, + "ljp_corrected": null, + "id": null + }, + { + "name": "K_Pst", + "stochastic": false, + "location": "somaxon", + "version": null, + "temperature": null, + "ljp_corrected": null, + "id": null + }, + { + "name": "K_Tst", + "stochastic": false, + "location": "somaxon", + "version": null, + "temperature": null, + "ljp_corrected": null, + "id": null + }, + { + "name": "NaTg", + "stochastic": false, + "location": "axonal", + "version": null, + "temperature": null, + "ljp_corrected": null, + "id": null + }, + { + "name": "Nap_Et2", + "stochastic": false, + "location": "axonal", + "version": null, + "temperature": null, + "ljp_corrected": null, + "id": null + }, + { + "name": "NaTg", + "stochastic": false, + "location": "somatic", + "version": null, + "temperature": null, + "ljp_corrected": null, + "id": null + }, + { + "name": "NaTg", + "stochastic": false, + "location": "apical", + "version": null, + "temperature": null, + "ljp_corrected": null, + "id": null + }, + { + "name": "SKv3_1", + "stochastic": false, + "location": "apical", + "version": null, + "temperature": null, + "ljp_corrected": null, + "id": null + }, + { + "name": "Ih", + "stochastic": false, + "location": "somadend", + "version": null, + "temperature": null, + "ljp_corrected": null, + "id": null + } + ], + "distributions": [ + { + "name": "uniform", + "function": null, + "soma_ref_location": 0.5 + }, + { + "name": "exp", + "function": "(-0.8696 + 2.087*math.exp(({distance})*0.0031))*{value}", + "soma_ref_location": 0.5 + }, + { + "name": "decay", + "function": "math.exp({distance}*{constant})*{value}", + "soma_ref_location": 0.5, + "parameters": [ + "constant" + ] + } + ], + "parameters": [ + { + "name": "v_init", + "value": -80, + "location": "global" + }, + { + "name": "celsius", + "value": 34, + "location": "global" + }, + { + "name": "constant", + "value": [ + -0.1, + 0.0 + ], + "location": "distribution_decay" + }, + { + "name": "cm", + "value": 0.02, + "location": "myelinated" + }, + { + "name": "Ra", + "value": 100, + "location": "all" + }, + { + "name": "g_pas", + "value": [ + 1e-05, + 6e-05 + ], + "location": "all", + "mechanism": "pas" + }, + { + "name": "e_pas", + "value": [ + -95, + -60 + ], + "location": "all", + "mechanism": "pas" + }, + { + "name": "gIhbar_Ih", + "value": [ + 0, + 0.0002 + ], + "location": "somadend", + "distribution": "exp", + "mechanism": "Ih" + }, + { + "name": "cm", + "value": 1, + "location": "axonal" + }, + { + "name": "ena", + "value": 50, + "location": "axonal" + }, + { + "name": "ek", + "value": -90, + "location": "axonal" + }, + { + "name": "vshifth_NaTg", + "value": 10, + "location": "axonal", + "mechanism": "NaTg" + }, + { + "name": "slopem_NaTg", + "value": 9, + "location": "axonal", + "mechanism": "NaTg" + }, + { + "name": "gNaTgbar_NaTg", + "value": [ + 0, + 1.5 + ], + "location": "axonal", + "mechanism": "NaTg" + }, + { + "name": "gNap_Et2bar_Nap_Et2", + "value": [ + 0, + 0.02 + ], + "location": "axonal", + "mechanism": "Nap_Et2" + }, + { + "name": "gK_Pstbar_K_Pst", + "value": [ + 0, + 1 + ], + "location": "axonal", + "mechanism": "K_Pst" + }, + { + "name": "gK_Tstbar_K_Tst", + "value": [ + 0, + 0.2 + ], + "location": "axonal", + "mechanism": "K_Tst" + }, + { + "name": "gSKv3_1bar_SKv3_1", + "value": [ + 0, + 1 + ], + "location": "axonal", + "mechanism": "SKv3_1" + }, + { + "name": "gCa_HVAbar_Ca_HVA2", + "value": [ + 0, + 0.001 + ], + "location": "axonal", + "mechanism": "Ca_HVA2" + }, + { + "name": "gCa_LVAstbar_Ca_LVAst", + "value": [ + 0, + 0.01 + ], + "location": "axonal", + "mechanism": "Ca_LVAst" + }, + { + "name": "gSK_E2bar_SK_E2", + "value": [ + 0, + 0.1 + ], + "location": "axonal", + "mechanism": "SK_E2" + }, + { + "name": "decay_CaDynamics_DC0", + "value": [ + 20, + 300 + ], + "location": "axonal", + "mechanism": "CaDynamics_DC0" + }, + { + "name": "gamma_CaDynamics_DC0", + "value": [ + 0.005, + 0.05 + ], + "location": "axonal", + "mechanism": "CaDynamics_DC0" + }, + { + "name": "cm", + "value": 1, + "location": "somatic" + }, + { + "name": "ena", + "value": 50, + "location": "somatic" + }, + { + "name": "ek", + "value": -90, + "location": "somatic" + }, + { + "name": "vshiftm_NaTg", + "value": 13, + "location": "somatic", + "mechanism": "NaTg" + }, + { + "name": "vshifth_NaTg", + "value": 15, + "location": "somatic", + "mechanism": "NaTg" + }, + { + "name": "slopem_NaTg", + "value": 7, + "location": "somatic", + "mechanism": "NaTg" + }, + { + "name": "gNaTgbar_NaTg", + "value": [ + 0, + 0.3 + ], + "location": "somatic", + "mechanism": "NaTg" + }, + { + "name": "gK_Pstbar_K_Pst", + "value": [ + 0, + 0.2 + ], + "location": "somatic", + "mechanism": "K_Pst" + }, + { + "name": "gK_Tstbar_K_Tst", + "value": [ + 0, + 0.1 + ], + "location": "somatic", + "mechanism": "K_Tst" + }, + { + "name": "gSKv3_1bar_SKv3_1", + "value": [ + 0, + 1 + ], + "location": "somatic", + "mechanism": "SKv3_1" + }, + { + "name": "gCa_HVAbar_Ca_HVA2", + "value": [ + 0, + 0.001 + ], + "location": "somatic", + "mechanism": "Ca_HVA2" + }, + { + "name": "gCa_LVAstbar_Ca_LVAst", + "value": [ + 0, + 0.01 + ], + "location": "somatic", + "mechanism": "Ca_LVAst" + }, + { + "name": "gSK_E2bar_SK_E2", + "value": [ + 0, + 0.1 + ], + "location": "somatic", + "mechanism": "SK_E2" + }, + { + "name": "decay_CaDynamics_DC0", + "value": [ + 20, + 300 + ], + "location": "somatic", + "mechanism": "CaDynamics_DC0" + }, + { + "name": "gamma_CaDynamics_DC0", + "value": [ + 0.005, + 0.05 + ], + "location": "somatic", + "mechanism": "CaDynamics_DC0" + }, + { + "name": "cm", + "value": 2, + "location": "apical" + }, + { + "name": "ena", + "value": 50, + "location": "apical" + }, + { + "name": "ek", + "value": -90, + "location": "apical" + }, + { + "name": "gamma_CaDynamics_DC0", + "value": [ + 0.005, + 0.05 + ], + "location": "apical", + "mechanism": "CaDynamics_DC0" + }, + { + "name": "vshiftm_NaTg", + "value": 6, + "location": "apical", + "mechanism": "NaTg" + }, + { + "name": "vshifth_NaTg", + "value": 6, + "location": "apical", + "mechanism": "NaTg" + }, + { + "name": "gNaTgbar_NaTg", + "value": [ + 0, + 0.1 + ], + "location": "apical", + "distribution": "decay", + "mechanism": "NaTg" + }, + { + "name": "gSKv3_1bar_SKv3_1", + "value": [ + 0, + 0.003 + ], + "location": "apical", + "mechanism": "SKv3_1" + }, + { + "name": "gCa_HVAbar_Ca_HVA2", + "value": [ + 0, + 0.0001 + ], + "location": "apical", + "mechanism": "Ca_HVA2" + }, + { + "name": "gCa_LVAstbar_Ca_LVAst", + "value": [ + 0, + 0.001 + ], + "location": "apical", + "mechanism": "Ca_LVAst" + }, + { + "name": "cm", + "value": 2, + "location": "basal" + }, + { + "name": "gamma_CaDynamics_DC0", + "value": [ + 0.005, + 0.05 + ], + "location": "basal", + "mechanism": "CaDynamics_DC0" + }, + { + "name": "gCa_HVAbar_Ca_HVA2", + "value": [ + 0, + 0.0001 + ], + "location": "basal", + "mechanism": "Ca_HVA2" + }, + { + "name": "gCa_LVAstbar_Ca_LVAst", + "value": [ + 0, + 0.001 + ], + "location": "basal", + "mechanism": "Ca_LVAst" + } + ], + "morphology": { + "name": "C060114A5", + "format": "asc", + "path": null, + "seclist_names": null, + "secarray_names": null, + "section_index": null, + "id": null + }, + "morph_modifiers": null +} \ No newline at end of file diff --git a/tests/test_data/config/nexus/EMPS__emodel=L5PC.json b/tests/test_data/config/nexus/EMPS__emodel=L5PC.json new file mode 100644 index 00000000..3c2e4196 --- /dev/null +++ b/tests/test_data/config/nexus/EMPS__emodel=L5PC.json @@ -0,0 +1,64 @@ +{ + "extraction_reader": null, + "extraction_threshold_value_save": 1, + "plot_extraction": true, + "extract_absolute_amplitudes": false, + "pickle_cells_extraction": false, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + }, + "path_extract_config": "config/extract_config/L5PC_config.json", + "rheobase_strategy_extraction": "absolute", + "rheobase_settings_extraction": null, + "interpolate_RMP_extraction": false, + "default_std_value": 0.01, + "bound_max_std": false, + "minimum_protocol_delay": 0.0, + "max_threshold_voltage": -30, + "threshold_efeature_std": null, + "max_depth_holding_search": 7, + "max_depth_threshold_search": 10, + "spikecount_timeout": 50, + "stochasticity": false, + "neuron_dt": null, + "cvode_minstep": 0.0, + "start_from_emodel": null, + "optimisation_timeout": 600, + "optimiser": "MO-CMA", + "optimisation_params": { + "offspring_size": 20 + }, + "max_ngen": 10, + "optimisation_checkpoint_period": null, + "use_stagnation_criterion": true, + "plot_optimisation": true, + "compile_mechanisms": false, + "name_Rin_protocol": "IV_-20", + "name_rmp_protocol": "IV_0", + "strict_holding_bounds": true, + "validation_threshold": 1000, + "validation_function": "max_score", + "validation_protocols": [ + "sAHP_220" + ], + "n_model": 3, + "optimisation_batch_size": 5, + "max_n_batch": 3, + "name_gene_map": null, + "plot_currentscape": true, + "currentscape_config": { + "title": "L5PC" + }, + "plot_parameter_evolution": true, + "plot_bAP_EPSP": false, + "save_recordings": false, + "morph_modifiers": null, + "files_for_extraction": null, + "targets": null, + "protocols_rheobase": null, + "auto_targets": null, + "auto_targets_presets": null, + "use_ProbAMPANMDA_EMS": false +} \ No newline at end of file diff --git a/tests/test_data/config/nexus/EM__emodel=L5PC.json b/tests/test_data/config/nexus/EM__emodel=L5PC.json new file mode 100644 index 00000000..9130ca5f --- /dev/null +++ b/tests/test_data/config/nexus/EM__emodel=L5PC.json @@ -0,0 +1,588 @@ +{ + "fitness": 662.5893440041364, + "parameter": [ + { + "name": "constant.distribution_decay", + "value": -4.9999997198124646e-11, + "unitCode": "" + }, + { + "name": "g_pas.all", + "value": 5.632803471462532e-05, + "unitCode": "" + }, + { + "name": "e_pas.all", + "value": -82.49109163320246, + "unitCode": "" + }, + { + "name": "gIhbar_Ih.somadend", + "value": 9.999999146890343e-14, + "unitCode": "" + }, + { + "name": "gNaTgbar_NaTg.axonal", + "value": 1.49999999925, + "unitCode": "" + }, + { + "name": "gNap_Et2bar_Nap_Et2.axonal", + "value": 0.016429163366303387, + "unitCode": "" + }, + { + "name": "gK_Pstbar_K_Pst.axonal", + "value": 0.35623870084702924, + "unitCode": "" + }, + { + "name": "gK_Tstbar_K_Tst.axonal", + "value": 0.03406760550673209, + "unitCode": "" + }, + { + "name": "gSKv3_1bar_SKv3_1.axonal", + "value": 0.8993064462282256, + "unitCode": "" + }, + { + "name": "gCa_HVAbar_Ca_HVA2.axonal", + "value": 0.0006362293950500262, + "unitCode": "" + }, + { + "name": "gCa_LVAstbar_Ca_LVAst.axonal", + "value": 0.006825767406414945, + "unitCode": "" + }, + { + "name": "gSK_E2bar_SK_E2.axonal", + "value": 0.04045870284469083, + "unitCode": "" + }, + { + "name": "decay_CaDynamics_DC0.axonal", + "value": 153.66214448126124, + "unitCode": "" + }, + { + "name": "gamma_CaDynamics_DC0.axonal", + "value": 0.013892725368677946, + "unitCode": "" + }, + { + "name": "gNaTgbar_NaTg.somatic", + "value": 0.29999999985000003, + "unitCode": "" + }, + { + "name": "gK_Pstbar_K_Pst.somatic", + "value": 9.999999439624929e-11, + "unitCode": "" + }, + { + "name": "gK_Tstbar_K_Tst.somatic", + "value": 4.9999997198124646e-11, + "unitCode": "" + }, + { + "name": "gSKv3_1bar_SKv3_1.somatic", + "value": 0.5935428736858676, + "unitCode": "" + }, + { + "name": "gCa_HVAbar_Ca_HVA2.somatic", + "value": 0.000846375959810855, + "unitCode": "" + }, + { + "name": "gCa_LVAstbar_Ca_LVAst.somatic", + "value": 0.0021233722909938845, + "unitCode": "" + }, + { + "name": "gSK_E2bar_SK_E2.somatic", + "value": 0.06334366578969035, + "unitCode": "" + }, + { + "name": "decay_CaDynamics_DC0.somatic", + "value": 261.9526156612472, + "unitCode": "" + }, + { + "name": "gamma_CaDynamics_DC0.somatic", + "value": 0.010157216827939918, + "unitCode": "" + }, + { + "name": "gamma_CaDynamics_DC0.apical", + "value": 0.04442554039364624, + "unitCode": "" + }, + { + "name": "gNaTgbar_NaTg.apical", + "value": 0.02025744109577797, + "unitCode": "" + }, + { + "name": "gSKv3_1bar_SKv3_1.apical", + "value": 0.0025596419811034863, + "unitCode": "" + }, + { + "name": "gCa_HVAbar_Ca_HVA2.apical", + "value": 7.479775381367394e-05, + "unitCode": "" + }, + { + "name": "gCa_LVAstbar_Ca_LVAst.apical", + "value": 0.0007907937408173575, + "unitCode": "" + }, + { + "name": "gamma_CaDynamics_DC0.basal", + "value": 0.03874866815198076, + "unitCode": "" + }, + { + "name": "gCa_HVAbar_Ca_HVA2.basal", + "value": 3.958501967659612e-05, + "unitCode": "" + }, + { + "name": "gCa_LVAstbar_Ca_LVAst.basal", + "value": 0.0008951918042675094, + "unitCode": "" + } + ], + "score": [ + { + "name": "IDrest_150.soma.v.Spikecount", + "value": 2.359000952984802, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.mean_frequency", + "value": 2.5473067719252622, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.time_to_first_spike", + "value": 1.8975802980568641, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.time_to_last_spike", + "value": 1.1705463494754935, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.inv_time_to_first_spike", + "value": 1.601334069384769, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.inv_first_ISI", + "value": 2.0326329738450353, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.inv_second_ISI", + "value": 2.035267304174762, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.inv_third_ISI", + "value": 2.2014083807052236, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.inv_last_ISI", + "value": 2.733656584960495, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.AHP_depth", + "value": 2.436364479707141, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.AHP_time_from_peak", + "value": 0.4503077134383394, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.min_AHP_values", + "value": 250.0, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.depol_block_bool", + "value": 1.0, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.voltage_base", + "value": 2.100879750527612, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.Spikecount", + "value": 0.47140452079103173, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.mean_frequency", + "value": 0.6143754832551459, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.time_to_first_spike", + "value": 2.941136806455238, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.time_to_last_spike", + "value": 0.350571662983691, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.inv_time_to_first_spike", + "value": 2.497993498048805, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.inv_first_ISI", + "value": 6.358924783373354, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.inv_second_ISI", + "value": 1.9141500871662738, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.inv_third_ISI", + "value": 0.7164346277860275, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.inv_last_ISI", + "value": 0.015443610675384092, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.AHP_depth", + "value": 1.8334017595888072, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.AHP_time_from_peak", + "value": 6.39946913222683, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.min_AHP_values", + "value": 250.0, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.depol_block_bool", + "value": 0.0, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.voltage_base", + "value": 0.5560377791049563, + "unitCode": "" + }, + { + "name": "RMPProtocol.soma.v.voltage_base", + "value": 5.448651489346306, + "unitCode": "" + }, + { + "name": "SearchHoldingCurrent.soma.v.voltage_base", + "value": 0.36087048730893895, + "unitCode": "" + }, + { + "name": "RinProtocol.soma.v.ohmic_input_resistance_vb_ssse", + "value": 0.8510803936986094, + "unitCode": "" + }, + { + "name": "IV_-100.soma.v.voltage_base", + "value": 6.198237308955552, + "unitCode": "" + }, + { + "name": "IV_-100.soma.v.ohmic_input_resistance_vb_ssse", + "value": 0.6602622310691594, + "unitCode": "" + }, + { + "name": "APWaveform_280.soma.v.AP_amplitude", + "value": 7.7505654597590725, + "unitCode": "" + }, + { + "name": "APWaveform_280.soma.v.AP1_amp", + "value": 1.4751518976963902, + "unitCode": "" + }, + { + "name": "APWaveform_280.soma.v.AP_duration_half_width", + "value": 35.51760127035546, + "unitCode": "" + }, + { + "name": "APWaveform_280.soma.v.AHP_depth", + "value": 32.777629535734484, + "unitCode": "" + }, + { + "name": "SearchHoldingCurrent.soma.v.bpo_holding_current", + "value": 2.6593749724667184, + "unitCode": "" + }, + { + "name": "SearchThresholdCurrent.soma.v.bpo_threshold_current", + "value": 19.654289577104343, + "unitCode": "" + } + ], + "features": [ + { + "name": "IDrest_150.soma.v.Spikecount", + "value": 17.0, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.mean_frequency", + "value": 8.596169647912996, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.time_to_first_spike", + "value": 39.89999999960082, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.time_to_last_spike", + "value": 1977.6250000053583, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.inv_time_to_first_spike", + "value": 25.062656641854748, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.inv_first_ISI", + "value": 8.982708286556564, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.inv_second_ISI", + "value": 7.7534405892685365, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.inv_third_ISI", + "value": 7.863180656559301, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.inv_last_ISI", + "value": 8.406893652764708, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.AHP_depth", + "value": 11.605082746978722, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.AHP_time_from_peak", + "value": 29.476562500081798, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.min_AHP_values", + "value": null, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.depol_block_bool", + "value": 0.0, + "unitCode": "" + }, + { + "name": "IDrest_150.soma.v.voltage_base", + "value": -84.6661128887082, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.Spikecount", + "value": 22.0, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.mean_frequency", + "value": 11.173751841099335, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.time_to_first_spike", + "value": 16.574999999622037, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.time_to_last_spike", + "value": 1968.9000000053265, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.inv_time_to_first_spike", + "value": 60.33182503908315, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.inv_first_ISI", + "value": 9.528346831833344, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.inv_second_ISI", + "value": 9.962640099635461, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.inv_third_ISI", + "value": 10.214504596536358, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.inv_last_ISI", + "value": 11.16694584027205, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.AHP_depth", + "value": 13.909282929964116, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.AHP_time_from_peak", + "value": 24.839772727345572, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.min_AHP_values", + "value": null, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.depol_block_bool", + "value": 0.0, + "unitCode": "" + }, + { + "name": "IDrest_250.soma.v.voltage_base", + "value": -84.6661128887082, + "unitCode": "" + }, + { + "name": "RMPProtocol.soma.v.voltage_base", + "value": -82.61411086119787, + "unitCode": "" + }, + { + "name": "SearchHoldingCurrent.soma.v.voltage_base", + "value": -84.66586024626444, + "unitCode": "" + }, + { + "name": "RinProtocol.soma.v.ohmic_input_resistance_vb_ssse", + "value": 41.14594355932795, + "unitCode": "" + }, + { + "name": "IV_-100.soma.v.voltage_base", + "value": -82.36801591154078, + "unitCode": "" + }, + { + "name": "IV_-100.soma.v.ohmic_input_resistance_vb_ssse", + "value": 47.63760804275347, + "unitCode": "" + }, + { + "name": "APWaveform_280.soma.v.AP_amplitude", + "value": 86.96376736202178, + "unitCode": "" + }, + { + "name": "APWaveform_280.soma.v.AP1_amp", + "value": 86.96376736202178, + "unitCode": "" + }, + { + "name": "APWaveform_280.soma.v.AP_duration_half_width", + "value": 0.7499999999999574, + "unitCode": "" + }, + { + "name": "APWaveform_280.soma.v.AHP_depth", + "value": 15.28003015363403, + "unitCode": "" + }, + { + "name": "sAHP_220.soma.v.mean_frequency", + "value": 4.372859121057693, + "unitCode": "" + }, + { + "name": "sAHP_220.soma.v.voltage_base", + "value": -82.70982692444534, + "unitCode": "" + }, + { + "name": "sAHP_220.soma.v.depol_block_bool", + "value": 1.0, + "unitCode": "" + }, + { + "name": "SearchHoldingCurrent.soma.v.bpo_holding_current", + "value": -0.05, + "unitCode": "" + }, + { + "name": "SearchThresholdCurrent.soma.v.bpo_threshold_current", + "value": 0.37560539077915445, + "unitCode": "" + } + ], + "scoreValidation": [ + { + "name": "sAHP_220.soma.v.mean_frequency", + "value": 146.22976987850097, + "unitCode": "" + }, + { + "name": "sAHP_220.soma.v.voltage_base", + "value": 76.88298007269339, + "unitCode": "" + }, + { + "name": "sAHP_220.soma.v.depol_block_bool", + "value": 0.0, + "unitCode": "" + } + ], + "passedValidation": true, + "seed": 4 +} \ No newline at end of file diff --git a/tests/test_data/config/nexus/FCC__emodel=L5PC.json b/tests/test_data/config/nexus/FCC__emodel=L5PC.json new file mode 100644 index 00000000..8607a651 --- /dev/null +++ b/tests/test_data/config/nexus/FCC__emodel=L5PC.json @@ -0,0 +1,840 @@ +{ + "efeatures": [ + { + "efel_feature_name": "Spikecount", + "protocol_name": "IDrest_150", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 10.25, + "original_std": 2.8613807855648994, + "sample_size": 4, + "efeature_name": "Spikecount", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "mean_frequency", + "protocol_name": "IDrest_150", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 5.604467279505482, + "original_std": 1.1744570388537756, + "sample_size": 4, + "efeature_name": "mean_frequency", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "time_to_first_spike", + "protocol_name": "IDrest_150", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 30.62499999960926, + "original_std": 4.887803698999843, + "sample_size": 4, + "efeature_name": "time_to_first_spike", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "time_to_last_spike", + "protocol_name": "IDrest_150", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 1798.0000000047048, + "original_std": 153.4539833310668, + "sample_size": 4, + "efeature_name": "time_to_last_spike", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "inv_time_to_first_spike", + "protocol_name": "IDrest_150", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 33.48946564816112, + "original_std": 5.2623679014984965, + "sample_size": 4, + "efeature_name": "inv_time_to_first_spike", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "inv_first_ISI", + "protocol_name": "IDrest_150", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 6.848761634886355, + "original_std": 1.0498435670034043, + "sample_size": 4, + "efeature_name": "inv_first_ISI", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "inv_second_ISI", + "protocol_name": "IDrest_150", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 5.773614073436429, + "original_std": 0.9727599474383865, + "sample_size": 4, + "efeature_name": "inv_second_ISI", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "inv_third_ISI", + "protocol_name": "IDrest_150", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 4.951203923401405, + "original_std": 1.3227789803475907, + "sample_size": 4, + "efeature_name": "inv_third_ISI", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "inv_last_ISI", + "protocol_name": "IDrest_150", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 5.098217326184602, + "original_std": 1.2103481998372232, + "sample_size": 4, + "efeature_name": "inv_last_ISI", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "AHP_depth", + "protocol_name": "IDrest_150", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 10.635529561928397, + "original_std": 0.3988957892428203, + "sample_size": 4, + "efeature_name": "AHP_depth", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "AHP_time_from_peak", + "protocol_name": "IDrest_150", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 30.186512445968763, + "original_std": 1.674292515812361, + "sample_size": 4, + "efeature_name": "AHP_time_from_peak", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "min_AHP_values", + "protocol_name": "IDrest_150", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": -74.71276615599231, + "original_std": 0.47443121506895136, + "sample_size": 4, + "efeature_name": "min_AHP_values", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "depol_block_bool", + "protocol_name": "IDrest_150", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 0.5, + "original_std": 0.5, + "sample_size": 4, + "efeature_name": "depol_block_bool", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "voltage_base", + "protocol_name": "IDrest_150", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": -85.3482957179207, + "original_std": 0.3247129346842365, + "sample_size": 4, + "efeature_name": "voltage_base", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "Spikecount", + "protocol_name": "IDrest_250", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 23.0, + "original_std": 2.1213203435596424, + "sample_size": 4, + "efeature_name": "Spikecount", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "mean_frequency", + "protocol_name": "IDrest_250", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 11.734819514318126, + "original_std": 0.9132325239381061, + "sample_size": 4, + "efeature_name": "mean_frequency", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "time_to_first_spike", + "protocol_name": "IDrest_250", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 13.312499999625004, + "original_std": 1.109264959330169, + "sample_size": 4, + "efeature_name": "time_to_first_spike", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "time_to_last_spike", + "protocol_name": "IDrest_250", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 1957.8125000052862, + "original_std": 31.62691446785914, + "sample_size": 4, + "efeature_name": "time_to_last_spike", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "inv_time_to_first_spike", + "protocol_name": "IDrest_250", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 75.6263616579313, + "original_std": 6.122728754416209, + "sample_size": 4, + "efeature_name": "inv_time_to_first_spike", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "inv_first_ISI", + "protocol_name": "IDrest_250", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 21.905299784313108, + "original_std": 1.9463908402945282, + "sample_size": 4, + "efeature_name": "inv_first_ISI", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "inv_second_ISI", + "protocol_name": "IDrest_250", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 11.585060042821176, + "original_std": 0.8475928580854186, + "sample_size": 4, + "efeature_name": "inv_second_ISI", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "inv_third_ISI", + "protocol_name": "IDrest_250", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 10.842911569617055, + "original_std": 0.8771309324098986, + "sample_size": 4, + "efeature_name": "inv_third_ISI", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "inv_last_ISI", + "protocol_name": "IDrest_250", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 11.181785300456339, + "original_std": 0.960880230420604, + "sample_size": 4, + "efeature_name": "inv_last_ISI", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "AHP_depth", + "protocol_name": "IDrest_250", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 14.65928505460855, + "original_std": 0.40907679984590156, + "sample_size": 4, + "efeature_name": "AHP_depth", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "AHP_time_from_peak", + "protocol_name": "IDrest_250", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 22.910710474376955, + "original_std": 0.3014409809798328, + "sample_size": 4, + "efeature_name": "AHP_time_from_peak", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "min_AHP_values", + "protocol_name": "IDrest_250", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": -70.21595795897821, + "original_std": 0.3938297800235492, + "sample_size": 4, + "efeature_name": "min_AHP_values", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "depol_block_bool", + "protocol_name": "IDrest_250", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 0.0, + "original_std": 0.01, + "sample_size": 4, + "efeature_name": "depol_block_bool", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "voltage_base", + "protocol_name": "IDrest_250", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": -84.87524301358675, + "original_std": 0.37610776234519244, + "sample_size": 4, + "efeature_name": "voltage_base", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "steady_state_voltage_stimend", + "protocol_name": "RMPProtocol", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": -84.17305524849598, + "original_std": 0.2861156361984797, + "sample_size": 7, + "efeature_name": "voltage_base", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "steady_state_voltage_stimend", + "protocol_name": "SearchHoldingCurrent", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": -84.56752332522545, + "original_std": 0.2724992053861328, + "sample_size": 5, + "efeature_name": "voltage_base", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "ohmic_input_resistance_vb_ssse", + "protocol_name": "RinProtocol", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 50.527473523677365, + "original_std": 11.023083170297625, + "sample_size": 5, + "efeature_name": "ohmic_input_resistance_vb_ssse", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "voltage_base", + "protocol_name": "IV_-100", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": -84.40562008045337, + "original_std": 0.32873929592346396, + "sample_size": 6, + "efeature_name": "voltage_base", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "ohmic_input_resistance_vb_ssse", + "protocol_name": "IV_-100", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 46.6061413427208, + "original_std": 1.5622076373540625, + "sample_size": 6, + "efeature_name": "ohmic_input_resistance_vb_ssse", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "AP_amplitude", + "protocol_name": "APWaveform_280", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 85.07656351726156, + "original_std": 0.24349240769058497, + "sample_size": 3, + "efeature_name": "AP_amplitude", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "AP1_amp", + "protocol_name": "APWaveform_280", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 87.79791959126591, + "original_std": 0.565468702271776, + "sample_size": 3, + "efeature_name": "AP1_amp", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "AP_duration_half_width", + "protocol_name": "APWaveform_280", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 1.4749999999999164, + "original_std": 0.02041241452319235, + "sample_size": 3, + "efeature_name": "AP_duration_half_width", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "AHP_depth", + "protocol_name": "APWaveform_280", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 22.279999923706082, + "original_std": 0.21355936561674232, + "sample_size": 3, + "efeature_name": "AHP_depth", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "mean_frequency", + "protocol_name": "sAHP_220", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 5.835156819842703, + "original_std": 0.01, + "sample_size": 1, + "efeature_name": "mean_frequency", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "voltage_base", + "protocol_name": "sAHP_220", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": -81.9409971237184, + "original_std": 0.01, + "sample_size": 1, + "efeature_name": "voltage_base", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "depol_block_bool", + "protocol_name": "sAHP_220", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 1.0, + "original_std": 0.01, + "sample_size": 1, + "efeature_name": "depol_block_bool", + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true, + "Threshold": -20.0, + "interp_step": 0.025 + } + }, + { + "efel_feature_name": "bpo_holding_current", + "protocol_name": "SearchHoldingCurrent", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": -0.02340625027533282, + "original_std": 0.01, + "sample_size": null, + "efeature_name": null, + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true + } + }, + { + "efel_feature_name": "bpo_threshold_current", + "protocol_name": "SearchThresholdCurrent", + "recording_name": "soma.v", + "threshold_efeature_std": null, + "default_std_value": 0.01, + "mean": 0.179062495008111, + "original_std": 0.01, + "sample_size": null, + "efeature_name": null, + "weight": 1.0, + "efel_settings": { + "strict_stiminterval": true + } + } + ], + "protocols": [ + { + "name": "IDrest_150", + "stimuli": [ + { + "delay": 700.0000000000001, + "amp": 0.27664062613621354, + "thresh_perc": 154.49389673905893, + "duration": 2000.0, + "totduration": 3000.0, + "holding_current": -0.02679687598720193 + } + ], + "recordings_from_config": [ + { + "type": "CompRecording", + "name": "IDrest_150.soma.v", + "location": "soma", + "variable": "v" + } + ], + "validation": false, + "protocol_type": "ThresholdBasedProtocol", + "stochasticity": false + }, + { + "name": "IDrest_250", + "stimuli": [ + { + "delay": 700.0000000000001, + "amp": 0.45898438291624194, + "thresh_perc": 256.3263640973233, + "duration": 2000.0, + "totduration": 3000.0, + "holding_current": -0.025390625465661287 + } + ], + "recordings_from_config": [ + { + "type": "CompRecording", + "name": "IDrest_250.soma.v", + "location": "soma", + "variable": "v" + } + ], + "validation": false, + "protocol_type": "ThresholdBasedProtocol", + "stochasticity": false + }, + { + "name": "IV_-100", + "stimuli": [ + { + "delay": 20.0, + "amp": -0.17802083678543568, + "thresh_perc": -99.41827113342292, + "duration": 1000.0, + "totduration": 1320.0, + "holding_current": -0.025468749925494194 + } + ], + "recordings_from_config": [ + { + "type": "CompRecording", + "name": "IV_-100.soma.v", + "location": "soma", + "variable": "v" + } + ], + "validation": false, + "protocol_type": "ThresholdBasedProtocol", + "stochasticity": false + }, + { + "name": "APWaveform_280", + "stimuli": [ + { + "delay": 5.0, + "amp": 0.5091145721574625, + "thresh_perc": 284.32228208056694, + "duration": 50.00000000000001, + "totduration": 59.99, + "holding_current": -0.02437500034769376 + } + ], + "recordings_from_config": [ + { + "type": "CompRecording", + "name": "APWaveform_280.soma.v", + "location": "soma", + "variable": "v" + } + ], + "validation": false, + "protocol_type": "ThresholdBasedProtocol", + "stochasticity": false + }, + { + "name": "sAHP_220", + "stimuli": [ + { + "delay": 25.0, + "tmid": 520.0, + "tmid2": 720.0, + "toff": 2720.0, + "amp": 0.40656252205371857, + "long_amp": 0.09843749552965164, + "thresh_perc": 227.0506294661551, + "duration": 200.0, + "totduration": 2740.0, + "holding_current": 0.024062499403953552 + } + ], + "recordings_from_config": [ + { + "type": "CompRecording", + "name": "sAHP_220.soma.v", + "location": "soma", + "variable": "v" + } + ], + "validation": true, + "protocol_type": "ThresholdBasedProtocol", + "stochasticity": false + } + ] +} \ No newline at end of file diff --git a/tests/test_data/config/recipes_nexus.json b/tests/test_data/config/recipes_nexus.json new file mode 100644 index 00000000..8436ac32 --- /dev/null +++ b/tests/test_data/config/recipes_nexus.json @@ -0,0 +1,15 @@ +{ + "L5PC": { + "morph_path": "morphology", + "morphology": [ + [ + "C060114A5", + "C060114A5.asc" + ] + ], + "params": "config/nexus/EMC__emodel=L5PC.json", + "final": "config/nexus/EM__emodel=L5PC.json", + "pipeline_settings": "config/nexus/EMPS__emodel=L5PC.json", + "features": "config/nexus/FCC__emodel=L5PC.json" + } +} diff --git a/tests/unit_tests/test_local_access_point_from_nexus.py b/tests/unit_tests/test_local_access_point_from_nexus.py new file mode 100644 index 00000000..3d44f32a --- /dev/null +++ b/tests/unit_tests/test_local_access_point_from_nexus.py @@ -0,0 +1,114 @@ +""" +Copyright 2024, EPFL/Blue Brain Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import json +from pathlib import Path + +from dictdiffer import diff + +from tests.utils import DATA + + +def test_get_morphologies(db_from_nexus): + morphology = db_from_nexus.get_morphologies() + assert morphology["name"] == "C060114A5" + assert Path(morphology["path"]).name == "C060114A5.asc" + + +def test_get_available_morphologies(db_from_nexus): + names = db_from_nexus.get_available_morphologies() + assert len(names) == 1 + assert list(names)[0] == "C060114A5" + + +def test_get_recipes(db_from_nexus, emodel_dir): + recipes = db_from_nexus.get_recipes() + expected_recipes = json.load(open(db_from_nexus.recipes_path, "r"))["L5PC"] + assert list(diff(recipes, expected_recipes)) == [] + + +def test_get_model_configuration(db_from_nexus): + configuration = db_from_nexus.get_model_configuration() + + expected_parameters = json.load(open(DATA / "test_parameters.json", "r")) + + for p in configuration.parameters: + assert p.location in expected_parameters["parameters"] + for ep in expected_parameters["parameters"][p.location]: + if ep["name"] == p.name and ep["val"] == p.value: + break + else: + raise Exception("missing parameter") + + assert sorted(list(configuration.mechanism_names)) == [ + "CaDynamics_DC0", + "Ca_HVA2", + "Ca_LVAst", + "Ih", + "K_Pst", + "K_Tst", + "NaTg", + "Nap_Et2", + "SK_E2", + "SKv3_1", + "pas", + ] + + +def test_get_final_content(db_from_nexus): + final = db_from_nexus.get_final_content() + assert "L5PC" in final + assert "parameters" in final["L5PC"] + + +def test_load_pipeline_settings(db_from_nexus): + assert ( + db_from_nexus.pipeline_settings.path_extract_config + == "config/extract_config/L5PC_config.json" + ) + assert db_from_nexus.pipeline_settings.validation_protocols == ["sAHP_220"] + + +def test_get_model_name_for_final(db_from_nexus): + db_from_nexus.emodel_metadata.iteration = "" + assert db_from_nexus.get_model_name_for_final(seed=42) == "L5PC__42" + db_from_nexus.emodel_metadata.iteration = None + assert db_from_nexus.get_model_name_for_final(seed=42) == "L5PC__42" + db_from_nexus.emodel_metadata.iteration = "hash" + assert db_from_nexus.get_model_name_for_final(seed=42) == "L5PC__hash__42" + + +def test_get_ion_currents_concentrations(db_from_nexus): + expected_ion_currents = { + "ica_Ca_HVA2", + "ica_Ca_LVAst", + "ik_K_Pst", + "ik_K_Tst", + "ina_NaTg", + "ina_Nap_Et2", + "ik_SK_E2", + "ik_SKv3_1", + "ihcn_Ih", + "i_pas", + } + expected_ionic_concentrations = { + "cai", + "ki", + "nai", + } + ion_currents, ionic_concentrations = db_from_nexus.get_ion_currents_concentrations() + assert set(ion_currents) == expected_ion_currents + assert set(ionic_concentrations) == expected_ionic_concentrations From 2759a81e38d6849a2d8d00dfa79ba91bcd48e93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaquier=20Aur=C3=A9lien=20Tristan?= Date: Fri, 1 Mar 2024 14:42:17 +0100 Subject: [PATCH 4/6] add __str__ function to ProtocolRunner Change-Id: Id74a0caa20c9cd6d0b21e893034e66123bc5887f --- bluepyemodel/evaluation/protocols.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/bluepyemodel/evaluation/protocols.py b/bluepyemodel/evaluation/protocols.py index b4ac04a5..051da03a 100644 --- a/bluepyemodel/evaluation/protocols.py +++ b/bluepyemodel/evaluation/protocols.py @@ -914,3 +914,14 @@ def run(self, cell_model, param_values, sim=None, isolate=None, timeout=None): cell_model.unfreeze(param_values.keys()) return responses + + def __str__(self): + """String representation""" + + content = f"Sequence protocol {self.name}:\n" + + content += f"{len(self.protocols)} subprotocols:\n" + for protocol in self.protocols: + content += f"{protocol}\n" + + return content From 3d0ebe19ef761da6b9c479494544fc27e9c44839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaquier=20Aur=C3=A9lien=20Tristan?= Date: Fri, 1 Mar 2024 14:43:37 +0100 Subject: [PATCH 5/6] Use --forked in test to prevent one NEURON simulation to affect the others Change-Id: I2c203148cf77465731e28fd9b645906897a047b2 --- tests/functional_tests/test_protocols_from_nexus.py | 4 ++-- tox.ini | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/functional_tests/test_protocols_from_nexus.py b/tests/functional_tests/test_protocols_from_nexus.py index 0f27a665..378876d7 100644 --- a/tests/functional_tests/test_protocols_from_nexus.py +++ b/tests/functional_tests/test_protocols_from_nexus.py @@ -38,8 +38,8 @@ def test_protocols(db_from_nexus, tmp_path): print(responses) assert_allclose(responses["bpo_rmp"], -82.61402706564716, rtol=1e-06) assert_allclose(responses["bpo_holding_current"], -0.05, rtol=1e-06) - assert_allclose(responses["bpo_rin"], 41.13626022273351, rtol=1e-06) - assert_allclose(responses["bpo_threshold_current"], 0.3757011543411314, rtol=1e-06) + assert_allclose(responses["bpo_rin"], 41.151498, rtol=1e-06) + assert_allclose(responses["bpo_threshold_current"], 0.3755498847141163, rtol=1e-06) for prot_name in [ "APWaveform_280.soma.v", diff --git a/tox.ini b/tox.ini index a1820504..1ab455ef 100644 --- a/tox.ini +++ b/tox.ini @@ -26,6 +26,7 @@ deps = neuron pytest>=7.3.1 pytest-cov>=4.1.0 + pytest-forked>=1.6.0 # isolation download = true allowlist_externals = make @@ -36,7 +37,7 @@ setenv = OMP_NUM_THREADS=1 commands = make clean - pytest {[testenv]coverage_options} tests -vsx + pytest {[testenv]coverage_options} tests -vsx --forked coverage report --show-missing coverage xml From ad6594a870c15cb5f1d1ec611b37d106fb0ef7a3 Mon Sep 17 00:00:00 2001 From: Gianluca Ficarelli Date: Fri, 1 Mar 2024 15:21:43 +0100 Subject: [PATCH 6/6] Remove FIXME and print in tests --- tests/functional_tests/test_protocols_from_nexus.py | 2 -- tox.ini | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/functional_tests/test_protocols_from_nexus.py b/tests/functional_tests/test_protocols_from_nexus.py index 378876d7..c770785a 100644 --- a/tests/functional_tests/test_protocols_from_nexus.py +++ b/tests/functional_tests/test_protocols_from_nexus.py @@ -34,8 +34,6 @@ def test_protocols(db_from_nexus, tmp_path): protocols=evaluator.fitness_protocols.values(), param_values=params ) - # FIXME: remove print after clarifying the different result when executing the test alone - print(responses) assert_allclose(responses["bpo_rmp"], -82.61402706564716, rtol=1e-06) assert_allclose(responses["bpo_holding_current"], -0.05, rtol=1e-06) assert_allclose(responses["bpo_rin"], 41.151498, rtol=1e-06) diff --git a/tox.ini b/tox.ini index 1ab455ef..5812242e 100644 --- a/tox.ini +++ b/tox.ini @@ -37,6 +37,7 @@ setenv = OMP_NUM_THREADS=1 commands = make clean + # --forked is needed to run simulations with neuron in separate and clean processes pytest {[testenv]coverage_options} tests -vsx --forked coverage report --show-missing coverage xml