Skip to content

Ion Channel Simulator#536

Open
AurelienJaquier wants to merge 53 commits intomainfrom
simulate-icm
Open

Ion Channel Simulator#536
AurelienJaquier wants to merge 53 commits intomainfrom
simulate-icm

Conversation

@AurelienJaquier
Copy link
Contributor

This is not complete yet.
This is missing IonChannelModelStimulusUnion and IonChannelModelRecordingUnion implementation. Also the entitysdk implementation is not done yet.

@AurelienJaquier AurelienJaquier self-assigned this Jan 21, 2026
Copy link
Collaborator

@james-isbister james-isbister left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @AurelienJaquier , looks good!

I'll think through some alternatives for how we could approach the conductances for each selected ion channel, and the recording of any parameter for the selected ion channels. And then we can discuss soon!

@james-isbister james-isbister changed the title JSONSchema GUI update Ion Channel Simulator Jan 22, 2026
group=BlockGroup.STIMULI_RECORDINGS,
group_order=0,
)
# can we have recording union depending on what model we choose?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like an extension of the: ui_element: entity_property_dropdown

At the moment we have the example:

node_set: NodeSetType = Field(
        ui_element="entity_property_dropdown",
        entity_type=EntityType.CIRCUIT,
        property=CircuitPropertyType.NODE_SET,
        title="Node Set",
        description="Name of the node set to use.",
        min_length=1,
    )

Which specifies that the dropdown should display values returned by the mapped_circuit_properties endpoint - specifically the values in the list under the key CircuitPropertyType.NODE_SET

I think we could have a RecordVariable(Block) (or another name), which has a parameter recorded_variable with:

ui_element="entity_property_dropdown",
entity_type=EntityType.ION_CHANNEL_MODEL,
property=CircuitPropertyType.RECORDABLE_VARIABLES,

In the circuit simulation / extraction case there is only one circuit though so the dropdown only shows variables for one circuit

But in this case there will be multiple ion channels (selected somewhere in the configuration) - so we will have to think about how it is represented in the schema that the dropdown for recorded_variable should display values for all the ion channels (grouped by ion channel).

For now though, I think we could start by making a mapped_ion_channel_model_properties endpoint as we have for circuits, and we can go from there? what do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. I have implemented a mapped_ion_channel_properties_endpoint in my latest commit.

Here are some things that we should keep in the back of our minds: each ion channel can have multiple variables (although most of the time they will only have one), and we might have ion channels models with common variables (e.g. if we have two calcium mixing mechanisms, we would have calcium concentration as an available recording for both).
Also, we should always have voltage recording available in case the user wants to do some current clamp experiment.

Also, is the dropdown already somewhere in the platform? I would like to see what it looks like in the UI if possible.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

Just to be sure, for the common variables, is there a single value that are we recording (that is shared between the two mechanisms) or two values which are later combined into a third value (which we also might record)?

Atm we have a simple dropdown in the PredefinedNeuronSet. But on top of that we could add some sort of grouping by ion channel

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the common variable, it would be a single value that is shared between the two mechanisms. The only case right now would be the calcium concentration that can be modified by several mechanisms.

I like the grouping by ion channel, because it would make it very explicit from what ion channel the variable is recorded from.

}

class Initialize(Block):
ion_channel_models: list[IonChannelModelFromID] = Field(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One approach would be to have a block_dictionary i.e similar to what we have for Stimuli and NeuronSets in simulation.

So there would be an "Add Ion Channel Model" button under a heading say "Ion Channel Models"

When the user presses this button, the a new block is added to the dictionary with two parameters:

  1. ion_channel_model_entity - clicking on this launches a selection - similar to what we have for the selection of the ion channel recordings in the current ion channel build
  2. conductance

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added the block dictionary.

We will have edge cases of ion channel models with no conductances. In which case, I think we could have the conductance field being greyed out. I assume this will be handled by the UI, and I don't have to care for it in obi-one, correct?

@AurelienJaquier
Copy link
Contributor Author

What is lacking in this PR right now:

  • proper seclamp implementation (waiting on new SONATA definition)
  • entitycore/entitysdk config/campaign entities (waiting on generic config/campaign entities)
  • a way to properly have recording blocks depending on ion channel model selected

@AurelienJaquier
Copy link
Contributor Author

I tried implementing the recording union. Would that do the trick @james-isbister or is this implementation still missing something?

"conductance_name__isnull": False,
},
},
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder can't we use normal pydantic schemas? It is the first time I see this kind of schema declaration and frankly it's far from intuitive.

Copy link
Collaborator

@james-isbister james-isbister Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there is a better way to specify an entity filter? This was suggested by @bilalesi as a filter which can easily be applied in the frontend, but ideally I would like a way of specifying a filter which is usable both in obi-one through entitysdk and the frontend

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • the suggested filter is the same as entitycore, and it should align strictly with the terminology and naming conventions defined by entitycore, it's not something specific to the FE
  • in this case, we are specifying the filter (key: value) that should be used in the configuration-editor and it must be exactly what the ui element is expecting to use it and render the expected data and not the type so i don't see why using pydantic schema here, may be i'm missing smthg ?
  • for the properties used model_selector_entity_type and model_selector_property_filter, wouldn't be better to have one single dictionary defining the query shape as:
entity_query or model_query = { 
     type: xxxxx, 
     filters: { 
         conductance_name__isnull: xxxx, 
         yyyy: aaaa 
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I followed your suggestion in my latest commit @bilalesi . Does it look ok to you?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @AurelienJaquier , looks ok !

Comment on lines +130 to +142
if isinstance(circuit, Circuit):
L.info("initialize.circuit is a Circuit instance.")
self._circuit = self.config.initialize.circuit
self._sonata_config["network"] = self.config.initialize.circuit.path
self._circuit = circuit
self._sonata_config["network"] = circuit.path

elif isinstance(
self.config.initialize.circuit,
(CircuitFromID, MEModelFromID, MEModelWithSynapsesCircuitFromID),
circuit,
(
CircuitFromID,
MEModelFromID,
MEModelWithSynapsesCircuitFromID,
FakeCircuitFromIonChannelModels,
),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve noticed several conditional checks (e.g., configuration value checks, isinstance of types) that route execution into different flows. This approach makes the code difficult to follow and reason about, as it’s not clear what will be executed for any given task without tracing multiple branches.

A more maintainable design would define a dedicated task for each simulation type, with each task encapsulating all logic and responsibilities specific to that simulation type. This would improve readability, separation of concerns, and testability.

In its current form, the code is hard to maintain and will likely become increasingly difficult to extend or modify safely over time.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, thank you for highlighting this - these types of comments are very useful to us so please keep them coming. We integrated a lot of your suggestions about separattion of concerns last year, which has improved the code base a lot. Sometimes there is just a lag given how many other things we're working on

I agree that we need to improve readability, separation of concerns and testability - and this if statement logic needs to be majorly simplified or indeed removed as you suggest.

I'm not yet totally convinced about separating into separate tasks for each config but I'm totally open to it - I just need to think a bit more about it.

I'd be very happy to discuss this further at some point once I've had time to think about it.

else:
circuit_config_path = dest_dir / "circuit_config.json"

return MEModelCircuit(name="single_cell", path=str(circuit_config_path))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having staging logic inside config classes feels out of place. Staging is a task-level concern, while config classes should just hold and provide configuration data. Mixing the two blurs responsibilities and can make the code harder to maintain. It’s usually better to keep config focused on data, and handle staging or other task-specific logic elsewhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.
I was trying to stay close to the logic used in the from_id classes.
Maybe we can keep this logic for now (here and in from_id classes), and think about how to disentangle staging logic from config classes in another PR after the webinar

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, fine with me. I just left some comments here an there with things I noticed.

@AurelienJaquier
Copy link
Contributor Author

I think all the comments have been answered. Is this good to be merged or do you have more comments @james-isbister @eleftherioszisis @bilalesi ?

@eleftherioszisis
Copy link
Contributor

Given the extent of the PR I cannot give my approval without any tests. Maybe someone that is more familiar with the correctness of the code or has tested the feature could approve.

james-isbister
james-isbister previously approved these changes Mar 6, 2026
Copy link
Collaborator

@james-isbister james-isbister left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice work, thank you.

I just applied some minor renaming:

  • config.ion_channel_model(s).py
  • Removed "Fake"

@AurelienJaquier Please give it a quick test with @bilalesi core-web-app branch locally. If it works as expected we can merge this and make a release

@AurelienJaquier
Copy link
Contributor Author

How am I supposed to test it exactly?
I used the feat/146/ion-channel-ui-elements branch from core-web-app and followed your instructions to launch it, but then on the local version of the platform, I don't know how to test it: there is no button to launch an ion channel model simulation.

@bilalesi
Copy link
Contributor

bilalesi commented Mar 9, 2026

the simulation is not yet done, I will notify you when the branch preview link will be ready
feat/146/ion-channel-ui-elements this is for single neuron-beta and it's merged so you can test it in main.preview

@AurelienJaquier
Copy link
Contributor Author

I tested with bilal's core web app branch and everything is good.
@james-isbister Should we merge this as it is, or should we proceed to add back the SEClamp stimulus and the new recordings?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants