From 83b003eb799c8b0d8b721671a2c65ccd1857996c Mon Sep 17 00:00:00 2001 From: kaeldai Date: Tue, 20 Jan 2026 11:49:00 -0800 Subject: [PATCH 1/3] adding initial cell_inspector code --- bmtk/simulator/bionet/bionetwork.py | 124 +++++++++++++++++++++++++++- bmtk/simulator/bionet/cell.py | 1 + bmtk/utils/inspector/__init__.py | 0 bmtk/utils/inspector/__main__.py | 52 ++++++++++++ 4 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 bmtk/utils/inspector/__init__.py create mode 100644 bmtk/utils/inspector/__main__.py diff --git a/bmtk/simulator/bionet/bionetwork.py b/bmtk/simulator/bionet/bionetwork.py index 8dea68954..17ebe91a0 100644 --- a/bmtk/simulator/bionet/bionetwork.py +++ b/bmtk/simulator/bionet/bionetwork.py @@ -387,4 +387,126 @@ def add_spike_trains(self, spike_trains, node_set, spikes_generator=None, sim=No elif edge_pop.mixed_connections: raise NotImplementedError() - self.io.barrier() \ No newline at end of file + self.io.barrier() + + def inspect_cells(self, format='json', output_path=None): + import pandas as pd + import json + from pathlib import Path + from pprint import pprint + + cell_mechs = { + 'population': [], + 'node_id': [], + 'model_type': [], + 'node_type_id': [], + 'sec_name': [], + 'attr_name': [], + 'mech_name': [], + 'attr_val': [], + 'type': [] + } + + sect_counts = { + 'population': [], + 'node_id': [], + 'nsections': [], + 'nsegments': [] + } + + for _, cell in self.get_local_cells().items(): + if cell['model_type'] == 'biophysical': + sect_counts['population'].append(cell.population_name) + sect_counts['node_id'].append(cell.node_id) + sect_counts['nsections'].append(sum(1 for _ in cell.hobj.all)) + sect_counts['nsegments'].append(sum(sec.nseg for sec in cell.hobj.all)) + + for sec in cell.hobj.all: + sec_name = sec.name().split('.')[-1].split('[')[0] + if hasattr(sec, 'Ra'): + cell_mechs['population'].append(cell.population_name) + cell_mechs['node_id'].append(cell.node_id) + cell_mechs['node_type_id'].append(cell['node_type_id']) + cell_mechs['model_type'].append(cell['model_type']) + cell_mechs['sec_name'].append(sec_name) + cell_mechs['mech_name'].append(None) + cell_mechs['attr_name'].append('Ra') + cell_mechs['attr_val'].append(sec.Ra) + cell_mechs['type'].append(None) + + for mech_name, mech_props in sec.psection()['density_mechs'].items(): + for attr_name, attr_val in mech_props.items(): + cell_mechs['population'].append(cell.population_name) + cell_mechs['node_id'].append(cell.node_id) + cell_mechs['node_type_id'].append(cell['node_type_id']) + cell_mechs['model_type'].append(cell['model_type']) + cell_mechs['sec_name'].append(sec_name) + cell_mechs['mech_name'].append(mech_name) + cell_mechs['attr_name'].append(attr_name) + cell_mechs['attr_val'].append(np.mean(attr_val)) + cell_mechs['type'].append('mechanism') + + for ion_name, ion_props in sec.psection()['ions'].items(): + for attr_name, attr_val in ion_props.items(): + cell_mechs['population'].append(cell.population_name) + cell_mechs['node_id'].append(cell.node_id) + cell_mechs['node_type_id'].append(cell['node_type_id']) + cell_mechs['model_type'].append(cell['model_type']) + cell_mechs['sec_name'].append(sec_name) + cell_mechs['mech_name'].append(ion_name) + cell_mechs['attr_name'].append(attr_name) + cell_mechs['attr_val'].append(np.mean(attr_val)) + cell_mechs['type'].append('ion') + + else: + pass + # print(type(cell.hobj)) + # print(dir(cell.hobj)) + # print(cell.hobj.hname()) + + + agg_mech = pd.DataFrame(cell_mechs).groupby(['population', 'node_id', 'node_type_id', 'model_type', 'sec_name', 'mech_name', 'attr_name', 'type']).agg('mean') + mechs_df = agg_mech.reset_index() + mechs_df['attr_name'] = mechs_df.apply(lambda r: f'{r["attr_name"]}_{r["mech_name"]}' if r['type'] == 'mechanism' else r['attr_name'], axis=1) + + sect_lu = pd.DataFrame(sect_counts).set_index(['population', 'node_id']) + + if format == 'csv': + if output_path: + Path(output_path).parent.mkdir(parents=True, exist_ok=True) + mechs_df.to_csv(output_path, index=False) + else: + csv_str = mechs_df.to_csv(index=False) + print(csv_str) + + else: + mechs_dict = {} + for pop, pop_df in mechs_df.groupby('population'): + mechs_dict[pop] = {} + for node_id, node_df in pop_df.groupby('node_id'): + mechs_dict[pop][node_id] = { + 'node_type_id': int(node_df['node_type_id'].iloc[0]), + 'model_type': node_df['model_type'].iloc[0], + 'nsections': int(sect_lu.loc[(pop, node_id)]['nsections']), + 'nsegments': int(sect_lu.loc[(pop, node_id)]['nsegments']) + } + for sec, sec_df in node_df.groupby('sec_name'): + mechs_dict[pop][node_id][sec] = {} + for _, r in sec_df.iterrows(): + mechs_dict[pop][node_id][sec][r['attr_name']] = {'default_value': float(r['attr_val']), 'mechanism': r['mech_name'], 'type': r['type']} + # print(pop, node_id, sec) + # print(sec_df) + # exit() + + # mechs_dict[pop][node_id] = cell_dict + # print(mechs_df) + # exit() + + # mechs_df.to_csv('network_mechanisms', index=False) + + if output_path: + Path(output_path).parent.mkdir(parents=True, exist_ok=True) + with open(output_path, 'w') as f: + json.dump(mechs_dict, f, indent=2) + else: + pprint(mechs_dict) diff --git a/bmtk/simulator/bionet/cell.py b/bmtk/simulator/bionet/cell.py index 261b1261a..da4e29ec2 100644 --- a/bmtk/simulator/bionet/cell.py +++ b/bmtk/simulator/bionet/cell.py @@ -38,6 +38,7 @@ class Cell(object): def __init__(self, node, population_name, network=None): self._node = node self._network = network + self.population_name = population_name self._gid = network.gid_pool.get_gid(name=population_name, node_id=node.node_id) self._node_id = node.node_id diff --git a/bmtk/utils/inspector/__init__.py b/bmtk/utils/inspector/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/bmtk/utils/inspector/__main__.py b/bmtk/utils/inspector/__main__.py new file mode 100644 index 000000000..540b630bc --- /dev/null +++ b/bmtk/utils/inspector/__main__.py @@ -0,0 +1,52 @@ +import logging +from argparse import ArgumentParser + +from bmtk.simulator.core.simulation_config import SimulationConfig + + +def inspect_bionet(config_path, format='json', output_path=None): + from bmtk.simulator import bionet + + conf = bionet.Config.from_json(config_path) + conf.output['log_to_console'] = False + conf.build_env() + + network = bionet.BioNetwork.from_config(conf) + network.build_nodes() + network.inspect_cells( + format=format, + output_path=output_path + ) + + bionet.nrn.quit_execution() + + +if __name__ == '__main__': + parser = ArgumentParser() + parser.add_argument('--to-json', action='store_true') + parser.add_argument('--to-csv', action='store_true') + parser.add_argument('--output-path', type=str) + parser.add_argument('config', type=str) + args = parser.parse_args() + + config_path = args.config + config_dict = SimulationConfig.load(config_path) + target_sim = config_dict.target_simulator.upper() + + output_path = args.output_path + format = 'json' + if args.to_json and args.to_csv: + raise ValueError('Both --to-csv and --to-json specified. Please specify a single output format!') + elif args.to_csv: + format = 'csv' + + + if target_sim in ['BIONET', 'NEURON', 'NRN']: + inspect_bionet(config_path=config_path, format=format, output_path=output_path) + elif target_sim in ['POINTNET', 'NEST']: + print('PointNet') + elif target_sim in ['FILTERNET', 'LGN']: + print('FilterNet') + + # print(.target_simulator) + From 7dde5e8fbc197800080f359f087817cdba829e06 Mon Sep 17 00:00:00 2001 From: kaeldai Date: Wed, 21 Jan 2026 12:28:20 -0800 Subject: [PATCH 2/3] adding inspector for pointnet --- bmtk/simulator/bionet/bionetwork.py | 9 --- bmtk/simulator/pointnet/pointnetwork.py | 102 ++++++++++++++++++++++++ bmtk/utils/inspector/__main__.py | 17 +++- 3 files changed, 118 insertions(+), 10 deletions(-) diff --git a/bmtk/simulator/bionet/bionetwork.py b/bmtk/simulator/bionet/bionetwork.py index 17ebe91a0..7b439ffbd 100644 --- a/bmtk/simulator/bionet/bionetwork.py +++ b/bmtk/simulator/bionet/bionetwork.py @@ -494,15 +494,6 @@ def inspect_cells(self, format='json', output_path=None): mechs_dict[pop][node_id][sec] = {} for _, r in sec_df.iterrows(): mechs_dict[pop][node_id][sec][r['attr_name']] = {'default_value': float(r['attr_val']), 'mechanism': r['mech_name'], 'type': r['type']} - # print(pop, node_id, sec) - # print(sec_df) - # exit() - - # mechs_dict[pop][node_id] = cell_dict - # print(mechs_df) - # exit() - - # mechs_df.to_csv('network_mechanisms', index=False) if output_path: Path(output_path).parent.mkdir(parents=True, exist_ok=True) diff --git a/bmtk/simulator/pointnet/pointnetwork.py b/bmtk/simulator/pointnet/pointnetwork.py index c0e675270..409a537c4 100644 --- a/bmtk/simulator/pointnet/pointnetwork.py +++ b/bmtk/simulator/pointnet/pointnetwork.py @@ -23,7 +23,9 @@ import os import json import functools +import pandas as pd import nest +from pathlib import Path from six import string_types import numpy as np @@ -254,3 +256,103 @@ def _nest_connect(self, nest_srcs, nest_trgs, conn_spec='one_to_one', syn_spec=N # Record exception to log file. self.io.log_error(str(e)) raise + + skip_attrs = [ + 'archiver_length', + 'element_type', + 'frozen', + 'global_id', + 'local', + 'model', + 'model_id', + 'node_uses_wfr', + 'post_trace', + 'recordables', + 'refractory_input', + 'synaptic_elements', + 'thread', + 'thread_local_id' + ] + + def inspect(self, format='json', output_path=None, filter=None): + from pprint import pprint + + filter = filter or {'model_type': 'point_neuron'} + node_set = self.get_node_set(filter) + # print(self._node_sets) + # exit() + # for n in list(node_set.fetch_nodes()): + # print(n.node_id, n.population_name, n.node_type_id) + # print(node_set.population_names()) + # exit() + nodes = list(node_set.fetch_nodes()) + nest_ids = list(node_set.gids()) + # populations = node_set.population_names() + # populations = populations*len(node_ids) if len(populations) < len(node_ids) else populations + + attrs_table = { + 'population': [], + 'node_id': [], + 'nest_id': [], + # 'model_type': [], + 'model_template': [], + 'node_type_id': [], + 'attr_name': [], + 'attr_val': [], + 'type': [] + } + + nest_attrs = nest.GetStatus(nest.NodeCollection(nest_ids)) + for nest_id, cell_attr, node in zip(nest_ids, nest_attrs, nodes): + model_template = cell_attr['model'] + for key, val in cell_attr.items(): + if key in self.skip_attrs: + continue + else: + attrs_table['population'].append(node.population_name) + attrs_table['node_id'].append(node.node_id) + attrs_table['node_type_id'].append(node.node_type_id) + attrs_table['nest_id'].append(nest_id) + attrs_table['model_template'].append(model_template) + attrs_table['attr_name'].append(key) + attrs_table['attr_val'].append(val) + if key in cell_attr.get('recordables', {}): + attrs_table['type'].append('recordable') + else: + attrs_table['type'].append('attribute') + + attrs_table_df = pd.DataFrame(attrs_table) + attrs_table_df['model_type'] = 'point_neuron' + + if format == 'csv': + if output_path: + Path(output_path).parent.mkdir(parents=True, exist_ok=True) + attrs_table_df.to_csv(output_path, index=False) + else: + csv_str = attrs_table_df.to_csv(index=False) + print(csv_str) + else: + attrs_dict = {} + for pop, pop_df in attrs_table_df.groupby('population'): + attrs_dict[pop] = {} + for node_id, node_df in pop_df.groupby('node_id'): + attrs_dict[pop][node_id] = { + 'model_type': node_df['model_type'].iloc[0], + 'nest_id': int(node_df['nest_id'].iloc[0]), + 'node_type_id': int(node_df['node_type_id'].iloc[0]), + 'model_template': node_df['model_template'].iloc[0], + } + attrs_dict[pop][node_id]['attributes'] = [] + for _, row in node_df.iterrows(): + attrs_dict[pop][node_id]['attributes'].append({ + 'name': row['attr_name'], + 'value': row['attr_val'], + 'type': row['type'] + }) + + if output_path: + Path(output_path).parent.mkdir(parents=True, exist_ok=True) + with open(output_path, 'w') as f: + json.dump(attrs_dict, f, indent=2) + else: + pprint(attrs_dict) diff --git a/bmtk/utils/inspector/__main__.py b/bmtk/utils/inspector/__main__.py index 540b630bc..4ad7902e6 100644 --- a/bmtk/utils/inspector/__main__.py +++ b/bmtk/utils/inspector/__main__.py @@ -21,6 +21,21 @@ def inspect_bionet(config_path, format='json', output_path=None): bionet.nrn.quit_execution() +def inspect_pointnet(config_path, format='json', output_path=None): + from bmtk.simulator import pointnet + + conf = pointnet.Config.from_json(config_path) + conf.output['log_to_console'] = False + conf.build_env() + + network = pointnet.PointNetwork.from_config(conf) + pointnet.PointSimulator.from_config(conf, network) + network.inspect( + format=format, + output_path=output_path + ) + + if __name__ == '__main__': parser = ArgumentParser() parser.add_argument('--to-json', action='store_true') @@ -44,7 +59,7 @@ def inspect_bionet(config_path, format='json', output_path=None): if target_sim in ['BIONET', 'NEURON', 'NRN']: inspect_bionet(config_path=config_path, format=format, output_path=output_path) elif target_sim in ['POINTNET', 'NEST']: - print('PointNet') + inspect_pointnet(config_path=config_path, format=format, output_path=output_path) elif target_sim in ['FILTERNET', 'LGN']: print('FilterNet') From 1cc829022f7b2108f041e874d77b69ee458a4ad0 Mon Sep 17 00:00:00 2001 From: kaeldai Date: Thu, 22 Jan 2026 11:01:45 -0800 Subject: [PATCH 3/3] finishing up filtering ability --- bmtk/simulator/bionet/bionetwork.py | 34 +++++++++++++++++++++++------ bmtk/utils/inspector/__main__.py | 31 +++++++++++++++++--------- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/bmtk/simulator/bionet/bionetwork.py b/bmtk/simulator/bionet/bionetwork.py index 7b439ffbd..e459485cf 100644 --- a/bmtk/simulator/bionet/bionetwork.py +++ b/bmtk/simulator/bionet/bionetwork.py @@ -389,7 +389,9 @@ def add_spike_trains(self, spike_trains, node_set, spikes_generator=None, sim=No self.io.barrier() - def inspect_cells(self, format='json', output_path=None): + INTFIRE_ATTRS = ['tau', 'refrac', 'm', 'taum', 'taus', 'ib', 'i', 'I', 'taue', 'taui1', 'taui2', 'i1', 'i2'] + + def inspect_cells(self, format='json', output_path=None, filter=None): import pandas as pd import json from pathlib import Path @@ -414,7 +416,16 @@ def inspect_cells(self, format='json', output_path=None): 'nsegments': [] } + + filtered_gids = set() + filter = filter or 'all' + for a in self.get_node_set(filter).fetch_nodes(): + filtered_gids.add((a.node_id, a.population_name)) + for _, cell in self.get_local_cells().items(): + if (cell.node_id, cell.population_name) not in filtered_gids: + continue + if cell['model_type'] == 'biophysical': sect_counts['population'].append(cell.population_name) sect_counts['node_id'].append(cell.node_id) @@ -458,12 +469,21 @@ def inspect_cells(self, format='json', output_path=None): cell_mechs['attr_val'].append(np.mean(attr_val)) cell_mechs['type'].append('ion') - else: - pass - # print(type(cell.hobj)) - # print(dir(cell.hobj)) - # print(cell.hobj.hname()) - + elif cell['model_type'] in ['point_neuron', 'point', 'point_process']: + process_name = sec.name().split('.')[-1].split('[')[0] + for attr_name in dir(cell.hobj): + if attr_name.startswith('__') or attr_name not in self.INTFIRE_ATTRS: + continue + + cell_mechs['population'].append(cell.population_name) + cell_mechs['node_id'].append(cell.node_id) + cell_mechs['node_type_id'].append(cell['node_type_id']) + cell_mechs['model_type'].append(cell['model_type']) + cell_mechs['sec_name'].append('NA') + cell_mechs['mech_name'].append(process_name) + cell_mechs['attr_name'].append(attr_name) + cell_mechs['attr_val'].append(getattr(cell.hobj, attr_name)) + cell_mechs['type'].append('ARTIFICIAL_CELL') agg_mech = pd.DataFrame(cell_mechs).groupby(['population', 'node_id', 'node_type_id', 'model_type', 'sec_name', 'mech_name', 'attr_name', 'type']).agg('mean') mechs_df = agg_mech.reset_index() diff --git a/bmtk/utils/inspector/__main__.py b/bmtk/utils/inspector/__main__.py index 4ad7902e6..0c2da5d61 100644 --- a/bmtk/utils/inspector/__main__.py +++ b/bmtk/utils/inspector/__main__.py @@ -1,10 +1,11 @@ import logging +import re from argparse import ArgumentParser from bmtk.simulator.core.simulation_config import SimulationConfig -def inspect_bionet(config_path, format='json', output_path=None): +def inspect_bionet(config_path, format='json', output_path=None, filter=None): from bmtk.simulator import bionet conf = bionet.Config.from_json(config_path) @@ -15,13 +16,14 @@ def inspect_bionet(config_path, format='json', output_path=None): network.build_nodes() network.inspect_cells( format=format, - output_path=output_path + output_path=output_path, + filter=filter ) bionet.nrn.quit_execution() -def inspect_pointnet(config_path, format='json', output_path=None): +def inspect_pointnet(config_path, format='json', output_path=None, filter=None): from bmtk.simulator import pointnet conf = pointnet.Config.from_json(config_path) @@ -32,7 +34,8 @@ def inspect_pointnet(config_path, format='json', output_path=None): pointnet.PointSimulator.from_config(conf, network) network.inspect( format=format, - output_path=output_path + output_path=output_path, + filter=filter ) @@ -41,6 +44,8 @@ def inspect_pointnet(config_path, format='json', output_path=None): parser.add_argument('--to-json', action='store_true') parser.add_argument('--to-csv', action='store_true') parser.add_argument('--output-path', type=str) + parser.add_argument('-p', '--population', type=str, required=False) + parser.add_argument('--node-ids', type=str, required=False) parser.add_argument('config', type=str) args = parser.parse_args() @@ -55,13 +60,19 @@ def inspect_pointnet(config_path, format='json', output_path=None): elif args.to_csv: format = 'csv' + filter_dict = {} + if args.population: + filter_dict['population'] = re.split(r'[,;\s]+', args.population) + + if args.node_ids: + print(args.node_ids) + node_ids = re.split(r'[,;\s]+', args.node_ids) + node_ids = list(map(int, node_ids)) + filter_dict['node_id'] = node_ids if target_sim in ['BIONET', 'NEURON', 'NRN']: - inspect_bionet(config_path=config_path, format=format, output_path=output_path) + inspect_bionet(config_path=config_path, format=format, output_path=output_path, filter=filter_dict) elif target_sim in ['POINTNET', 'NEST']: - inspect_pointnet(config_path=config_path, format=format, output_path=output_path) + inspect_pointnet(config_path=config_path, format=format, output_path=output_path, filter=filter_dict) elif target_sim in ['FILTERNET', 'LGN']: - print('FilterNet') - - # print(.target_simulator) - + raise NotImplementedError