From 299d0f7b96fd6c41914a1b006dbcc6d18b370c84 Mon Sep 17 00:00:00 2001
From: "Ankur Sinha (Ankur Sinha Gmail)" <sanjay.ankur@gmail.com>
Date: Tue, 14 Jan 2025 17:19:46 +0000
Subject: [PATCH 1/4] feat(utils): replace undetermined gates with standard
 ones

Wherever possible.

Fixes #205
---
 neuroml/utils.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/neuroml/utils.py b/neuroml/utils.py
index af26761..1406531 100644
--- a/neuroml/utils.py
+++ b/neuroml/utils.py
@@ -472,6 +472,63 @@ def fix_external_morphs_biophys_in_cell(
     return newdoc
 
 
+def create_new_typed_gate(gate):
+    """Convert an undetermined gate to a "determined" gate
+
+    :param gate: gate object of GateHHUndetermined type
+    :returns: new gate object, or None if the gate is not of a standard type
+    """
+    gates_name_map = {
+        "gateHHrates": "GateHHRates",
+        "gateHHratesTau": "GateHHRatesTau",
+        "gateHHratesInf": "GateHHRatesInf",
+        "gateHHratesTauInf": "GateHHRatesTauInf",
+        "gateHHtauInf": "GateHHTauInf",
+        "gateHHInstantaneous": "GateHHInstantaneous",
+        "gateFractional": "GateFractional",
+        "gateKS": "GateKS",
+    }
+    gate_type = getattr(gate, "type", None)
+    if gate_type:
+        try:
+            gate_class_type = gates_name_map[gate_type]
+        # if it isn't in our dict, it's non standard
+        except KeyError:
+            return None
+
+        new_gate = component_factory(component_type=gate_class_type, validate=False)
+        print(gate.__dict__)
+        new_gate.__dict__.update(gate.__dict__)
+        return new_gate
+
+
+def move_undetermined_gates_to_typed(nml2_doc):
+    """Replace gates of GateHHUndetermined type with their standard
+    counterparts where possible.
+
+    Note that this modifies the passed NeuroMLDocument object in-place.
+
+    :param nml2_doc: NeuroMLDocument object
+    :returns: None
+
+    """
+    all_channels = (
+        list(nml2_doc.ion_channel_hhs.__iter__())
+        + list(nml2_doc.ion_channel.__iter__())
+        + list(nml2_doc.ion_channel_v_shifts.__iter__())
+    )
+    for achannel in all_channels:
+        determined_gates = []
+        undetermined_gates = getattr(achannel, "gates", [])
+        for gate in undetermined_gates:
+            new_typed_gate = create_new_typed_gate(gate)
+            if new_typed_gate:
+                achannel.add(new_typed_gate)
+                determined_gates.append(gate)
+        for d_gate in determined_gates:
+            undetermined_gates.remove(d_gate)
+
+
 def main():
     if len(sys.argv) != 2:
         print("Please specify the name of the NeuroML2 file...")

From 91269dbbe31efa77831398f75810b1b64a05e492 Mon Sep 17 00:00:00 2001
From: "Ankur Sinha (Ankur Sinha Gmail)" <sanjay.ankur@gmail.com>
Date: Tue, 14 Jan 2025 17:21:12 +0000
Subject: [PATCH 2/4] feat(loader): move undetermined gates to standard ones

---
 neuroml/loaders.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/neuroml/loaders.py b/neuroml/loaders.py
index 863ea5f..eb8d1f4 100644
--- a/neuroml/loaders.py
+++ b/neuroml/loaders.py
@@ -49,6 +49,7 @@ def __nml2_doc(cls, file_name: str) -> neuroml.NeuroMLDocument:
             if supressGeneratedsWarnings:
                 warnings.simplefilter("ignore")
             nml2_doc = nmlparse(file_name, silence=True)
+            utils.move_undetermined_gates_to_typed(nml2_doc)
             if supressGeneratedsWarnings:
                 warnings.resetwarnings()
         except Exception as e:

From 42424f3b48c4629e8a16f8f332fd07c787eab115 Mon Sep 17 00:00:00 2001
From: "Ankur Sinha (Ankur Sinha Gmail)" <sanjay.ankur@gmail.com>
Date: Tue, 14 Jan 2025 17:30:35 +0000
Subject: [PATCH 3/4] feat(utils): improve type hints, docs

---
 neuroml/utils.py | 42 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 39 insertions(+), 3 deletions(-)

diff --git a/neuroml/utils.py b/neuroml/utils.py
index 1406531..f7bbff9 100644
--- a/neuroml/utils.py
+++ b/neuroml/utils.py
@@ -15,7 +15,20 @@
 import networkx
 
 import neuroml.nml.nml as schema
-from neuroml import BiophysicalProperties, Morphology, NeuroMLDocument
+from neuroml import (
+    BiophysicalProperties,
+    GateFractional,
+    GateHHInstantaneous,
+    GateHHRates,
+    GateHHRatesInf,
+    GateHHRatesTau,
+    GateHHRatesTauInf,
+    GateHHTauInf,
+    GateHHUndetermined,
+    GateKS,
+    Morphology,
+    NeuroMLDocument,
+)
 
 from . import loaders
 
@@ -472,10 +485,24 @@ def fix_external_morphs_biophys_in_cell(
     return newdoc
 
 
-def create_new_typed_gate(gate):
+def create_new_typed_gate(
+    gate: GateHHUndetermined,
+) -> Optional[
+    Union[
+        GateHHRates,
+        GateHHRatesTau,
+        GateHHRatesInf,
+        GateHHRatesTauInf,
+        GateHHTauInf,
+        GateHHInstantaneous,
+        GateFractional,
+        GateKS,
+    ]
+]:
     """Convert an undetermined gate to a "determined" gate
 
     :param gate: gate object of GateHHUndetermined type
+    :type gate: GateHHUndetermined
     :returns: new gate object, or None if the gate is not of a standard type
     """
     gates_name_map = {
@@ -501,17 +528,26 @@ def create_new_typed_gate(gate):
         new_gate.__dict__.update(gate.__dict__)
         return new_gate
 
+    return None
 
-def move_undetermined_gates_to_typed(nml2_doc):
+
+def move_undetermined_gates_to_typed(nml2_doc: NeuroMLDocument):
     """Replace gates of GateHHUndetermined type with their standard
     counterparts where possible.
 
     Note that this modifies the passed NeuroMLDocument object in-place.
 
+    If `nml2_doc` is not a NeuroMLDocument, this function does nothing and
+    simply returns None.
+
     :param nml2_doc: NeuroMLDocument object
+    :type nml2_doc: NeuroMLDocument
     :returns: None
 
     """
+    if not isinstance(nml2_doc, NeuroMLDocument):
+        return None
+
     all_channels = (
         list(nml2_doc.ion_channel_hhs.__iter__())
         + list(nml2_doc.ion_channel.__iter__())

From 2e38e82ba5191e500b5e8e2d064f8cc5b0d0eb7b Mon Sep 17 00:00:00 2001
From: "Ankur Sinha (Ankur Sinha Gmail)" <sanjay.ankur@gmail.com>
Date: Tue, 14 Jan 2025 17:30:52 +0000
Subject: [PATCH 4/4] feat(loader): do gate tweaking after document is
 correctly read

---
 neuroml/loaders.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/neuroml/loaders.py b/neuroml/loaders.py
index eb8d1f4..7df336b 100644
--- a/neuroml/loaders.py
+++ b/neuroml/loaders.py
@@ -49,12 +49,12 @@ def __nml2_doc(cls, file_name: str) -> neuroml.NeuroMLDocument:
             if supressGeneratedsWarnings:
                 warnings.simplefilter("ignore")
             nml2_doc = nmlparse(file_name, silence=True)
-            utils.move_undetermined_gates_to_typed(nml2_doc)
             if supressGeneratedsWarnings:
                 warnings.resetwarnings()
         except Exception as e:
             raise Exception("Not a valid NeuroML 2 doc (%s): %s" % (file_name, e), e)
 
+        utils.move_undetermined_gates_to_typed(nml2_doc)
         return nml2_doc