From 93181793ace74bd45eafd9a6e14d3631547e461b Mon Sep 17 00:00:00 2001 From: Johannes Hjorth Date: Wed, 30 Oct 2024 13:01:26 +0100 Subject: [PATCH] Added new plotly plots for neuromodulation --- .../neuromodulation_bath_current.ipynb | 200 ++++++++++-------- snudda/plotting/__init__.py | 1 + snudda/plotting/plotly/__init__.py | 0 snudda/plotting/plotly/trace.py | 130 ++++++++++++ snudda/simulate/simulate.py | 11 +- 5 files changed, 248 insertions(+), 94 deletions(-) create mode 100644 snudda/plotting/plotly/__init__.py create mode 100644 snudda/plotting/plotly/trace.py diff --git a/examples/notebooks/neuromodulation/neuromodulation_bath_current.ipynb b/examples/notebooks/neuromodulation/neuromodulation_bath_current.ipynb index 74c17ef8a..18c084aab 100644 --- a/examples/notebooks/neuromodulation/neuromodulation_bath_current.ipynb +++ b/examples/notebooks/neuromodulation/neuromodulation_bath_current.ipynb @@ -39,6 +39,28 @@ { "cell_type": "code", "execution_count": 1, + "id": "1fe99a8b-bfd5-4759-8445-0ad12c9e2818", + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "034b649c-439d-43d6-809e-e0f705bcc12f", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.io as pio \n", + "pio.templates.default = \"simple_white\"" + ] + }, + { + "cell_type": "code", + "execution_count": 2, "id": "5b55f23d-62ac-4433-8639-07870af8c40a", "metadata": {}, "outputs": [], @@ -52,48 +74,10 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "ad92699c-455c-4fcd-b742-4391c78a0fd5", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Adding neurons: dspn from dir data/dspn\n", - "Writing networks/neuromodulation_bath_current/network-config.json\n", - "Writing networks/neuromodulation_bath_current/network-config.json\n", - "Placing neurons\n", - "Network path: networks/neuromodulation_bath_current\n", - "Reading SNUDDA_DATA=None from networks/neuromodulation_bath_current/network-config.json\n", - "Reading SNUDDA_DATA=/home/hjorth/HBP/Snudda/snudda/utils/../data from networks/neuromodulation_bath_current/network-synapses.hdf5\n", - "No n_putative_points and putative_density, setting n_putative_points = 63\n", - "(this must be larger than the number of neurons you want to place)\n", - "Generating 63 points for networks/neuromodulation_bath_current/mesh/Cube-cube-mesh-2.917951293943981e-05.obj\n", - "Filtering, keeping inside points: 2 / 26\n", - "neuron_name = 'dspn_0', num = 2, neuron_path = 'data/dspn/str-dspn-e150602_c1_D1-mWT-0728MSN01-v20211026'\n", - "stop_parallel disabled, to keep pool running.\n", - "\n", - "Execution time: 0.0s\n", - "Touch detection\n", - "Network path: networks/neuromodulation_bath_current\n", - "Reading SNUDDA_DATA=None from networks/neuromodulation_bath_current/network-config.json\n", - "Reading SNUDDA_DATA=/home/hjorth/HBP/Snudda/snudda/utils/../data from networks/neuromodulation_bath_current/network-synapses.hdf5\n", - "No d_view specified, running distribute neurons in serial\n", - "No connections specified in connectivity_distribution.\n", - "Reading SNUDDA_DATA=None from networks/neuromodulation_bath_current/network-config.json\n", - "stop_parallel disabled, to keep pool running.\n", - "\n", - "Execution time: 0.1s\n", - "Prune synapses\n", - "Network path: networks/neuromodulation_bath_current\n", - "No file networks/neuromodulation_bath_current/pruning_merge_info.json\n", - "stop_parallel disabled, to keep pool running.\n", - "\n", - "Execution time: 0.1s\n" - ] - } - ], + "outputs": [], "source": [ "snudda = Snudda(network_path=network_path)\n", "si = snudda.init_tiny(neuron_paths=neuron_path, neuron_names=\"dspn\", number_of_neurons=[2], \n", @@ -116,7 +100,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "97d5d05b-f51a-41cd-9641-63a6a835b309", "metadata": {}, "outputs": [], @@ -135,7 +119,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "id": "ae7ebf60-1556-4e07-81c6-85156c90bfad", "metadata": {}, "outputs": [], @@ -146,7 +130,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "d019b580-4193-4321-9ee8-9bdd3988209f", "metadata": {}, "outputs": [], @@ -160,7 +144,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "207658ef-0747-4c87-babb-d3076e3f7a33", "metadata": {}, "outputs": [], @@ -190,18 +174,10 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "6b6b0fa2-93d2-4d0b-92ef-3c4949ac3355", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "mpirun -n 2 snudda simulate networks/neuromodulation_bath_current --time 5 --simulation_config data/da_experiment_cur_inj_on_bath.json --mechdir /home/hjorth/BasalGangliaData/data/neurons/mechanisms\n" - ] - } - ], + "outputs": [], "source": [ "run_str_on = f\"mpirun -n {n_workers} snudda simulate {network_path} --time {sim_time} --simulation_config {sim_config_on} --mechdir {mech_dir}\"\n", "print(run_str_on)" @@ -228,18 +204,10 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "0f3648c5-2f5f-4b5b-b438-5eb15bffdd61", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "mpirun -n 2 snudda simulate networks/neuromodulation_bath_current --time 5 --simulation_config data/da_experiment_cur_inj_off_bath.json --mechdir /home/hjorth/BasalGangliaData/data/neurons/mechanisms --disable_rxd_neuromodulation\n" - ] - } - ], + "outputs": [], "source": [ "run_str_off = f\"mpirun -n {n_workers} snudda simulate {network_path} --time {sim_time} --simulation_config {sim_config_off} --mechdir {mech_dir} --disable_rxd_neuromodulation\"\n", "print(run_str_off)" @@ -265,7 +233,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 4, "id": "d84ee6f3-4193-4509-ad4a-0d93dff5e3d9", "metadata": {}, "outputs": [ @@ -292,7 +260,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 5, "id": "b72e5c19-3c2b-4c35-8488-e59b5a9d6db7", "metadata": {}, "outputs": [ @@ -337,7 +305,7 @@ " 'voltage']" ] }, - "execution_count": 10, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -348,7 +316,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 6, "id": "bd0f4edf-a622-490b-b39f-18f516975737", "metadata": {}, "outputs": [], @@ -361,7 +329,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 7, "id": "a63a7152-730e-4868-a81a-cc87fccaf9f4", "metadata": {}, "outputs": [ @@ -372,7 +340,7 @@ " scrolling=\"no\"\n", " width=\"1020px\"\n", " height=\"820\"\n", - " src=\"iframe_figures/figure_23.html\"\n", + " src=\"iframe_figures/figure_7.html\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", ">\n" @@ -398,7 +366,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 8, "id": "ad49456c-9e8c-4f16-b0de-524c84d33d35", "metadata": {}, "outputs": [], @@ -408,7 +376,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 9, "id": "511489ad-b3d6-4301-a8bd-fa870cf456e4", "metadata": {}, "outputs": [], @@ -421,7 +389,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 10, "id": "d231b4e4-4a56-47b5-84f6-e13b6e721125", "metadata": {}, "outputs": [ @@ -432,7 +400,7 @@ " scrolling=\"no\"\n", " width=\"1020px\"\n", " height=\"820\"\n", - " src=\"iframe_figures/figure_15.html\"\n", + " src=\"iframe_figures/figure_10.html\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", ">\n" @@ -457,7 +425,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 11, "id": "81722b1c-c30c-445e-a5f5-abaa823f0ce4", "metadata": {}, "outputs": [ @@ -477,7 +445,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 12, "id": "1e9ce899-6243-448b-a4ac-2ba3e83f522e", "metadata": {}, "outputs": [ @@ -488,7 +456,7 @@ " scrolling=\"no\"\n", " width=\"100%\"\n", " height=\"545px\"\n", - " src=\"iframe_figures/figure_17.html\"\n", + " src=\"iframe_figures/figure_12.html\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", ">\n" @@ -510,7 +478,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 13, "id": "c1c558be-b54e-41e7-a64c-6c98e955ed2a", "metadata": {}, "outputs": [ @@ -521,7 +489,7 @@ " scrolling=\"no\"\n", " width=\"100%\"\n", " height=\"545px\"\n", - " src=\"iframe_figures/figure_18.html\"\n", + " src=\"iframe_figures/figure_13.html\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", ">\n" @@ -551,7 +519,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 15, "id": "fc082055-9e38-4ed1-b842-40e166548071", "metadata": {}, "outputs": [ @@ -596,7 +564,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 16, "id": "191cf2cb-a61f-4b9e-89a9-4fe963f0343b", "metadata": {}, "outputs": [ @@ -654,7 +622,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 17, "id": "763cfbbf-c470-4138-843f-f0260bf2f0d5", "metadata": {}, "outputs": [], @@ -666,7 +634,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 18, "id": "d87c64bc-d4c0-40fc-8e2a-62664780064b", "metadata": {}, "outputs": [ @@ -677,7 +645,7 @@ " scrolling=\"no\"\n", " width=\"1020px\"\n", " height=\"820\"\n", - " src=\"iframe_figures/figure_22.html\"\n", + " src=\"iframe_figures/figure_18.html\"\n", " frameborder=\"0\"\n", " allowfullscreen\n", ">\n" @@ -703,17 +671,73 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 33, "id": "03b07fc6-b610-48ea-93ad-3e405a7f4a97", "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "data": { + "text/html": [ + "\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from snudda.plotting.plotly.trace import PlotTrace\n", + "\n", + "pt_on = PlotTrace(snudda_load_simulation=nd)\n", + "pt_on.define_colour_by_neuron_id({0: \"blue\", 1: \"red\"})\n", + "\n", + "pt_on.plot_traces()" + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 34, "id": "a8bd9d16-f96e-43c2-8c46-5dacb33e2d78", "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from snudda.plotting.plotly.trace import PlotTrace\n", + "\n", + "pt_off = PlotTrace(snudda_load_simulation=nd_off)\n", + "pt_off.define_colour_by_neuron_id({0: \"blue\", 1: \"red\"})\n", + "\n", + "pt_off.plot_traces()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "974a6eb4-311a-4b6c-a83f-7338acc81621", + "metadata": {}, "outputs": [], "source": [] } diff --git a/snudda/plotting/__init__.py b/snudda/plotting/__init__.py index 21e981b7c..e0ea0b076 100644 --- a/snudda/plotting/__init__.py +++ b/snudda/plotting/__init__.py @@ -5,4 +5,5 @@ from snudda.plotting.plot_traces import PlotTraces from snudda.plotting.plot_density import PlotDensity from snudda.plotting.plot_period_experiment import PlotPeriodExperiment +import snudda.plotting.plotly.trace import snudda.plotting.Blender diff --git a/snudda/plotting/plotly/__init__.py b/snudda/plotting/plotly/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/snudda/plotting/plotly/trace.py b/snudda/plotting/plotly/trace.py new file mode 100644 index 000000000..00c9bbf68 --- /dev/null +++ b/snudda/plotting/plotly/trace.py @@ -0,0 +1,130 @@ +import plotly.graph_objects as go +from plotly.subplots import make_subplots + +import numpy as np +from snudda.utils import SnuddaLoadSimulation +from snudda.utils.load import SnuddaLoad + +# To get clean plots: +# +# import plotly.io as pio +# pio.templates.default = "simple_white" +# + + +class PlotTrace: + + def __init__(self, snudda_load_simulation=None, network_simulation_file=None): + + self.snudda_load_simulation = None + self.colour_map = dict() + self.traces = dict() + self.default_colour = None + self.default_width = 1 + + if snudda_load_simulation is not None: + self.snudda_load_simulation = snudda_load_simulation + + if network_simulation_file is not None: + self.load_data(network_simulation_file=network_simulation_file) + + def load_data(self, network_simulation_file, do_depol_block_test=False): + self.snudda_load_simulation = SnuddaLoadSimulation(network_simulation_output_file=network_simulation_file, + quiet_load=True, do_test=do_depol_block_test) + + def define_colour_by_neuron_id(self, colour_by_neuron_id): + self.colour_map |= colour_by_neuron_id + + def define_colour_by_neuron_type(self, colour_by_neuron_type): + neuron_names = [SnuddaLoad.to_str(x) for x + in self.snudda_load_simulation.network_simulation_file["meta_data"]["name"]] + neuron_types = [x.split("_")[0] for x in neuron_names] + + for neuron_id, nt in enumerate(neuron_types): + if nt in colour_by_neuron_type: + self.colour_map[neuron_id] = colour_by_neuron_type[nt] + + def define_colour_by_neuron_name(self, colour_by_name): + neuron_names = self.snudda_load_simulation.get_neuron_name() + for neuron_id, name in enumerate(neuron_names): + if name in colour_by_name: + self.colour_map[neuron_id] = colour_by_name[name] + + def clear_colour(self): + self.colour_map = dict() + + def get_instant_frequency(self, neuron_id): + # neuron_id is assumed to be scalar int here + spike_times = self.snudda_load_simulation.get_spikes(neuron_id=neuron_id).flatten() + + isi = 1.0 / np.diff(spike_times) + isi_time = spike_times[1:] + + return isi_time, isi + + def get_voltage_trace(self, neuron_id): + + volt = self.snudda_load_simulation.get_voltage(neuron_id=neuron_id) + time = self.snudda_load_simulation.get_time() + + return time, volt[:, 0] + + def create_trace(self, neuron_id): + + time, volt = self.get_voltage_trace(neuron_id=neuron_id) + isi_time, isi = self.get_instant_frequency(neuron_id=neuron_id) + + colour = self.colour_map.get(neuron_id, self.default_colour) + neuron_name = self.snudda_load_simulation.get_neuron_name(neuron_id=[neuron_id])[0] + label = f"{neuron_name} ({neuron_id})" + + isi_sct = go.Scatter(x=isi_time.T, y=isi.T, mode="lines", + line={"color": colour, "width": self.default_width}, + name=label, legendgroup=label, showlegend=False) + + volt_sct = go.Scatter(x=time.T, y=volt.T, mode="lines", + line={"color": colour, "width": self.default_width}, + name=label, legendgroup=label) + + if neuron_id not in self.traces: + self.traces[neuron_id] = dict() + + self.traces[neuron_id]["isi"] = isi_sct + self.traces[neuron_id]["volt"] = volt_sct + + def create_traces(self, neuron_id_list=None): + if neuron_id_list is None: + neuron_id_list = [int(x) for x + in self.snudda_load_simulation.network_simulation_file["neurons"].keys()] + + for neuron_id in neuron_id_list: + self.create_trace(neuron_id=neuron_id) + + def make_figure(self, show=False): + + fig = make_subplots(cols=1, rows=2, shared_xaxes=True) + + for neuron_id, plots in self.traces.items(): + if neuron_id not in self.colour_map: + raise KeyError(f"No colour defined for {neuron_id = } or its neuron type") + fig.add_trace(plots["volt"], row=1, col=1) + fig.add_trace(plots["isi"], row=2, col=1) + + fig.update_layout(xaxis={"title": "Time (s)"}, + yaxis1={"title": "Volt (V)"}, + yaxis2={"title": "Frequency (Hz)"}) + + if show: + fig.show() + + return fig + + def plot_traces(self, neuron_id=None, show=False): + if isinstance(neuron_id, (int, np.integer)): + neuron_id = [neuron_id] + + self.create_traces(neuron_id_list=neuron_id) + fig = self.make_figure(show=show) + return fig + + # To save: fig.write_image("yourfigname.png") \ No newline at end of file diff --git a/snudda/simulate/simulate.py b/snudda/simulate/simulate.py index 055d50727..b19c49046 100644 --- a/snudda/simulate/simulate.py +++ b/snudda/simulate/simulate.py @@ -14,13 +14,13 @@ # ############################################################################ +import copy +import gc import json import os -import sys import re -import timeit import time -import gc +import timeit # Plot all sections # [neuron.h.psection(x) for x in neuron.h.allsec()] from collections import OrderedDict @@ -31,15 +31,14 @@ from mpi4py import MPI # This must be imported before neuron, to run parallel from neuron import h # , gui -import copy import snudda.utils.memory -from snudda.utils.snudda_path import snudda_parse_path, get_snudda_data from snudda.neurons.neuron_model_extended import NeuronModel from snudda.simulate.nrn_simulator_parallel import NrnSimulatorParallel +from snudda.simulate.save_network_recording import SnuddaSaveNetworkRecordings # If simulationConfig is set, those values override other values from snudda.utils.load import SnuddaLoad -from snudda.simulate.save_network_recording import SnuddaSaveNetworkRecordings +from snudda.utils.snudda_path import snudda_parse_path, get_snudda_data # !!! Need to gracefully handle the situation where there are more workers than