diff --git a/.pylintrc b/.pylintrc index 04f7ed09..78eccd97 100644 --- a/.pylintrc +++ b/.pylintrc @@ -12,7 +12,8 @@ #W0622 - redefined-builti #C0325 - superfluous-parens #R0205 - useless-object-inheritance -disable=C0103,R0904,R0903,W0511,R0801,R0401,I0013,W0622,C0325,R0205,C0302,C0415,E1133,C0112,C0116,W0221,W1514,C0413,W0105 +#R0917 - too-many-positional-arguments +disable=C0103,R0904,R0903,W0511,R0801,R0401,I0013,W0622,C0325,R0205,C0302,C0415,E1133,C0112,C0116,W0221,W1514,C0413,W0105,R0917 [FORMAT] # Maximum number of characters on a single line. diff --git a/bluepyemodel/emodel_pipeline/emodel_pipeline.py b/bluepyemodel/emodel_pipeline/emodel_pipeline.py index 2007d64c..787a1442 100644 --- a/bluepyemodel/emodel_pipeline/emodel_pipeline.py +++ b/bluepyemodel/emodel_pipeline/emodel_pipeline.py @@ -245,7 +245,7 @@ def validation(self, preselect_for_validation=False): preselect_for_validation=preselect_for_validation, ) - def plot(self, only_validated=False, load_from_local=False): + def plot(self, only_validated=False, load_from_local=False, seeds=None): """Plot the results of the optimisation in a subfolder called "figures". Args: @@ -254,6 +254,7 @@ def plot(self, only_validated=False, load_from_local=False): load_from_local (bool): if True, loads responses of the e-models from local files instead of recomputing them. Responses are automatically saved locally when plotting currentscapes. + seeds (list): list of seeds to use for plot. If None, all emodels will be plotted. """ pp_settings = self.access_point.pipeline_settings @@ -306,7 +307,7 @@ def plot(self, only_validated=False, load_from_local=False): return plotting.plot_models( access_point=self.access_point, mapper=self.mapper, - seeds=None, + seeds=seeds, figures_dir=pathlib.Path("./figures") / self.access_point.emodel_metadata.emodel, plot_distributions=True, plot_scores=True, diff --git a/bluepyemodel/emodel_pipeline/plotting.py b/bluepyemodel/emodel_pipeline/plotting.py index 66a3256e..f53c1138 100644 --- a/bluepyemodel/emodel_pipeline/plotting.py +++ b/bluepyemodel/emodel_pipeline/plotting.py @@ -861,11 +861,12 @@ def plot_bAP( apical_distances, apical_values = apical_feature.get_distances_feature_values(responses) basal_distances, basal_values = basal_feature.get_distances_feature_values(responses) - if 0 in apical_distances: - apical_x_fit, apical_y_fit = bAP_fit(apical_feature, apical_distances, apical_values) - if 0 in basal_distances: - basal_x_fit, basal_y_fit = bAP_fit(basal_feature, basal_distances, basal_values) - + if apical_values is None or basal_values is None: + logger.warning( + "Cannot plot figure for bAP because dendrite feature could not be computed. " + "This can happen when the emodel is bad and cannot even compute curent threshold." + ) + return fig, ax if len(apical_distances) != len(apical_values) or len(basal_distances) != len(basal_values): logger.warning( "Cannot plot figure for bAP because of mismatch between " @@ -874,6 +875,11 @@ def plot_bAP( ) return fig, ax + if 0 in apical_distances: + apical_x_fit, apical_y_fit = bAP_fit(apical_feature, apical_distances, apical_values) + if 0 in basal_distances: + basal_x_fit, basal_y_fit = bAP_fit(basal_feature, basal_distances, basal_values) + ax.scatter( apical_distances, apical_values, @@ -1667,6 +1673,18 @@ def plot_sinespec( current_key = f"{prot_name}.iclamp.i" voltage_key = f"{prot_name}.soma.v" + fig, axs = plt.subplots(3, figsize=(6, 12)) + + if voltage_key not in responses or current_key not in responses: + logger.warning( + "Could not find sinespec responses %s for emodel with seed %s. " + "This is most probably due to a bad model unable to compute threshold. " + "Skipping Sinespec plot.", + model.emodel_metadata.emodel, + model.seed, + ) + return fig, axs + freq, smooth_Z = get_impedance( responses[voltage_key]["time"], responses[voltage_key]["voltage"], @@ -1676,9 +1694,8 @@ def plot_sinespec( efel_settings, ) if freq is None or smooth_Z is None: - return None, None + return fig, axs - fig, axs = plt.subplots(3, figsize=(6, 12)) # current trace axs[0].plot(responses[current_key]["time"], responses[current_key]["voltage"]) axs[0].set_xlabel("Time (ms)") diff --git a/bluepyemodel/emodel_pipeline/plotting_utils.py b/bluepyemodel/emodel_pipeline/plotting_utils.py index e213cda1..527b0402 100644 --- a/bluepyemodel/emodel_pipeline/plotting_utils.py +++ b/bluepyemodel/emodel_pipeline/plotting_utils.py @@ -385,7 +385,13 @@ def get_simulated_FI_curve_for_plotting(evaluator, responses, prot_name): protocol_name = get_protocol_name(val) amp_temp = float(protocol_name.split("_")[-1]) if "mean_frequency" in val: - simulated_freq.append(values[val]) + mean_freq = values[val] + # Expects a one-sized array or None. + # If list is a mix of arrays and Nones, matplotlib will raise an error when trying + # to turn the list into a numpy array. + # -> turn one-sized array into number + mean_freq = mean_freq[0] if mean_freq is not None else None + simulated_freq.append(mean_freq) if "bpo_threshold_current" in responses: simulated_amp_rel.append(amp_temp) simulated_amp.append(rel_to_abs_amplitude(amp_temp, responses)) @@ -393,6 +399,8 @@ def get_simulated_FI_curve_for_plotting(evaluator, responses, prot_name): simulated_amp_rel.append(numpy.nan) simulated_amp.append(amp_temp) + # turn Nones into NaNs + simulated_freq = numpy.asarray(simulated_freq, dtype=float) return simulated_amp_rel, simulated_amp, simulated_freq @@ -514,6 +522,8 @@ def get_sinespec_evaluator(evaluator, sinespec_settings, efel_settings): FixedDtRecordingCustom(f"{prot_name}.soma.v", location=soma_loc, variable="v"), FixedDtRecordingStimulus(f"{prot_name}.iclamp.i", location=None, variable="i"), ], + # with constant Vm change, cvode would actually take longer to compute than fixed dt + cvode_active=False, ) new_prots[prot_name] = sinespec_prot diff --git a/bluepyemodel/model/model.py b/bluepyemodel/model/model.py index 1323b168..c2083dfe 100644 --- a/bluepyemodel/model/model.py +++ b/bluepyemodel/model/model.py @@ -56,11 +56,27 @@ def multi_locations(section_name, additional_multiloc_map): if additional_multiloc_map is not None: multiloc_map.update(additional_multiloc_map) - return [ + seclist_locations = [ ephys.locations.NrnSeclistLocation(sec, seclist_name=sec) for sec in multiloc_map.get(section_name, [section_name]) ] + for sec_loc in seclist_locations: + # all and myelinated are also accepted + if ( + sec_loc.seclist_name not in multiloc_map["allact"] + and sec_loc.seclist_name != "all" + and sec_loc.seclist_name != "myelinated" + ): + logger.warning( + "Section location %s not in expected locations %s. " + "This might make the cell crash at run time.", + sec_loc.seclist_name, + multiloc_map["allact"], + ) + + return seclist_locations + def define_distributions(distributions_definition, morphology=None): """Create a list of ParameterScaler from a the definition of channel distributions diff --git a/bluepyemodel/tasks/luigi_tools.py b/bluepyemodel/tasks/luigi_tools.py index 1a6ca2f8..ffbe6772 100644 --- a/bluepyemodel/tasks/luigi_tools.py +++ b/bluepyemodel/tasks/luigi_tools.py @@ -63,7 +63,7 @@ def __init__(self, *args, **kwargs): species=self.species, brain_region=self.brain_region, iteration_tag=self.iteration_tag, - **EmodelAPIConfig().api_args + **EmodelAPIConfig().api_args, ) def get_mapper(self): @@ -112,7 +112,7 @@ def __init__( species=species, brain_region=brain_region, iteration_tag=iteration_tag, - **EmodelAPIConfig().api_args + **EmodelAPIConfig().api_args, ) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..f3ef473b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,94 @@ +[build-system] +requires = ["setuptools >= 64", "setuptools_scm"] +build-backend = "setuptools.build_meta" + +[project] +name = "bluepyemodel" +authors = [ + {name = "Blue Brain Project, EPFL"}, +] +description="Blue Brain Python Electrical Modeling Pipeline" +readme = "README.rst" +license = {file = "LICENSE.txt"} +requires-python = ">= 3.9" +dynamic = ["version"] +dependencies = [ + "numpy", + "scipy", + "pandas", + "ipyparallel>=6.3", + "tqdm", + "pyyaml", + "gitpython", + "bluepyopt>=1.14.10", + "bluepyefe>=2.2.0", + "neurom>=3.0", + "efel>=5.5.5", + "configparser", + "neuron>=8.0", + "morph_tool>=2.8", + "morphio", + "fasteners>=0.16", + "jinja2>=3.0.3", + "currentscape>=0.0.11", +] +keywords=[ + "computational neuroscience", + "simulation", + "analysis", + "parameters", + "Blue Brain Project", +] +classifiers=[ + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "License :: OSI Approved :: Apache Software License", + "Operating System :: POSIX", + "Topic :: Scientific/Engineering", + "Programming Language :: Python :: 3", + "Topic :: Utilities", +] + +[project.optional-dependencies] +luigi = [ + "luigi>=3.0", + "luigi-tools>=0.0.12", +] +docs = [ + "graphviz", + "sphinx", + "sphinx-bluebrain-theme", + "luigi>=3.0", + "luigi-tools>=0.0.12", +] +test = [ + "pytest>=6.2", + "dictdiffer>=0.8", +] +nexus = [ + "nexusforge>=0.8.2", + "entity_management>=1.2", + "pyJWT>=2.1.0", +] +all = [ + "luigi>=3.0", + "luigi-tools>=0.0.12", + "nexusforge>=0.8.2", + "entity_management>=1.2", + "pyJWT>=2.1.0", + "pytest>=6.2", + "dictdiffer>=0.8", +] + +[project.urls] +Homepage = "https://github.com/BlueBrain/BluePyEModel" + +[tool.setuptools] +include-package-data = true + +[tool.setuptools.packages.find] +exclude = ["tests",] + +[tool.setuptools_scm] +version_scheme = "python-simplified-semver" +local_scheme = "no-local-version" diff --git a/setup.py b/setup.py deleted file mode 100644 index 7026cf6e..00000000 --- a/setup.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python - -""" -Copyright 2023-2024 Blue Brain Project / EPFL - -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. -""" - -from setuptools import find_packages, setup - -# Read the contents of the README file -with open("README.rst", encoding="utf-8") as f: - README = f.read() - - -EXTRA_LUIGI = [ - "luigi>=3.0", - "luigi-tools>=0.0.12", -] - -EXTRA_TEST = ["pytest>=6.2", "dictdiffer>=0.8"] - -EXTRA_DOC = [ - "graphviz", - "sphinx", - "sphinx-bluebrain-theme", -] - -EXTRA_NEXUS = [ - "nexusforge>=0.8.2", - "entity_management>=1.2", - "pyJWT>=2.1.0", -] - -setup( - name="bluepyemodel", - use_scm_version={ - "version_scheme": "python-simplified-semver", - "local_scheme": "no-local-version", - }, - setup_requires=["setuptools_scm"], - author="Blue Brain Project, EPFL", - author_email="", - description="Blue Brain Python Electrical Modeling Pipeline", - long_description=README, - long_description_content_type="text/x-rst", - license="Apache-2.0", - python_requires=">=3.9", - install_requires=[ - "numpy", - "scipy", - "pandas", - "ipyparallel>=6.3", - "tqdm", - "pyyaml", - "gitpython", - "bluepyopt>=1.14.10", - "bluepyefe>=2.2.0", - "neurom>=3.0", - "efel>=5.5.5", - "configparser", - "neuron>=8.0", - "morph_tool>=2.8", - "morphio", - "fasteners>=0.16", - "jinja2>=3.0.3", - "currentscape>=0.0.11", - ], - extras_require={ - "luigi": EXTRA_LUIGI, - "all": EXTRA_LUIGI + EXTRA_TEST + EXTRA_NEXUS, - "docs": EXTRA_DOC + EXTRA_LUIGI, - "test": EXTRA_TEST, - "nexus": EXTRA_NEXUS, - }, - packages=find_packages(exclude=("tests",)), - include_package_data=True, - keywords=[ - "computational neuroscience", - "simulation", - "analysis", - "parameters", - "Blue Brain Project", - ], - url="https://github.com/BlueBrain/BluePyEModel", - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Environment :: Console", - "License :: OSI Approved :: Apache Software License", - "Operating System :: POSIX", - "Topic :: Scientific/Engineering", - "Programming Language :: Python :: 3", - "Topic :: Utilities", - ], -)