From 6e118d9d4fa9ea785aefc2f091fec2a746d1d967 Mon Sep 17 00:00:00 2001 From: "Joseph G. Shuttleworth" Date: Thu, 6 Nov 2025 11:18:58 +0000 Subject: [PATCH 01/11] Update ramp bound function and docstring --- pcpostprocess/detect_ramp_bounds.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pcpostprocess/detect_ramp_bounds.py b/pcpostprocess/detect_ramp_bounds.py index a924ba99..72f81b5c 100644 --- a/pcpostprocess/detect_ramp_bounds.py +++ b/pcpostprocess/detect_ramp_bounds.py @@ -1,23 +1,23 @@ import numpy as np -def detect_ramp_bounds(times, voltage_sections, ramp_no=0): +def detect_ramp_bounds(times, voltage_sections, ramp_index=0): """ - Extract the the times at the start and end of the nth ramp in the protocol. + Extract the timepoint indices at the start and end of the nth ramp in the protocol. @param times: np.array containing the time at which each sample was taken @param voltage_sections 2d np.array where each row describes a segment of the protocol: (tstart, tend, vstart, end) - @param ramp_no: the index of the ramp to select. Defaults to 0 - the first ramp + @param ramp_index: the index of the ramp to select. Defaults to 0 - the first ramp - @returns tstart, tend: the start and end times for the ramp_no+1^nth ramp + @returns istart, iend: the start and end timepoint indices for the specified ramp """ ramps = [(tstart, tend, vstart, vend) for tstart, tend, vstart, vend in voltage_sections if vstart != vend] try: - ramp = ramps[ramp_no] + ramp = ramps[ramp_index] except IndexError: - print(f"Requested {ramp_no+1}th ramp (ramp_no={ramp_no})," + print(f"Requested {ramp_index+1}th ramp (ramp_index={ramp_index})," " but there are only {len(ramps)} ramps") tstart, tend = ramp[:2] From df4dd8884a47686ad920bd03860232b9d132559f Mon Sep 17 00:00:00 2001 From: "Joseph G. Shuttleworth" Date: Thu, 6 Nov 2025 11:21:43 +0000 Subject: [PATCH 02/11] Fix infer reversal --- pcpostprocess/infer_reversal.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pcpostprocess/infer_reversal.py b/pcpostprocess/infer_reversal.py index fd5b892b..26fcf285 100644 --- a/pcpostprocess/infer_reversal.py +++ b/pcpostprocess/infer_reversal.py @@ -33,10 +33,7 @@ def infer_reversal_potential(current, times, voltage_segments, voltages, """ # Get ramp bounds, assuming final ramp is the reversal ramp - tstart, tend = detect_ramp_bounds(times, voltage_segments, -1) - - istart = np.argmax(times > tstart) - iend = np.argmax(times > tend) + istart, iend = detect_ramp_bounds(times, voltage_segments, -1) current = current[istart:iend] voltages = voltages[istart:iend] From cfabef350bc370b55451639e085c5932e381a404 Mon Sep 17 00:00:00 2001 From: "Joseph G. Shuttleworth" Date: Thu, 6 Nov 2025 11:40:57 +0000 Subject: [PATCH 03/11] Add test for reversal inference --- tests/test_infer_reversal.py | 59 ++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100755 tests/test_infer_reversal.py diff --git a/tests/test_infer_reversal.py b/tests/test_infer_reversal.py new file mode 100755 index 00000000..3560d789 --- /dev/null +++ b/tests/test_infer_reversal.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +import os +import unittest + +from syncropatch_export.trace import Trace + +from pcpostprocess import infer_reversal, leak_correct + + +class TestInferReversal(unittest.TestCase): + def setUp(self): + test_data_dir = os.path.join('tests', 'test_data', '13112023_MW2_FF', + "staircaseramp (2)_2kHz_15.01.07") + json_file = "staircaseramp (2)_2kHz_15.01.07.json" + + self.output_dir = os.path.join('test_output', 'test_trace_class') + + if not os.path.exists(self.output_dir): + os.makedirs(self.output_dir) + + self.ramp_bounds = [1700, 2500] + self.test_trace = Trace(test_data_dir, json_file) + + # get currents and QC from trace object + self.currents = self.test_trace.get_all_traces(leakcorrect=False) + self.currents['times'] = self.test_trace.get_times() + self.currents['voltages'] = self.test_trace.get_voltage() + + self.protocol_desc = self.test_trace.get_voltage_protocol().get_all_sections() + self.voltages = self.test_trace.get_voltage() + + self.correct_Erev = -90.06155612421054001970333047211170196533203125 + + def test_plot_leak_fit(self): + well = 'A03' + sweep = 0 + + voltage = self.test_trace.get_voltage() + times = self.test_trace.get_times() + + current = self.test_trace.get_trace_sweeps(sweeps=[sweep])[well][0, :] + params, Ileak = leak_correct.fit_linear_leak(current, voltage, times, + *self.ramp_bounds, + output_dir=self.output_dir, + save_fname=f"{well}_sweep{sweep}_leak_correction") + + I_corrected = current - Ileak + + E_rev = infer_reversal.infer_reversal_potential( + I_corrected, times, self.protocol_desc, + self.voltages, + output_path=os.path.join(self.output_dir, + f"{well}_staircase"), + known_Erev=self.correct_Erev) + self.assertEqual(E_rev, self.correct_Erev) + + +if __name__ == "__main__": + pass From 7380b62c99fba82e61f50c687f1c8cc384cbbbe9 Mon Sep 17 00:00:00 2001 From: "Joseph G. Shuttleworth" Date: Thu, 6 Nov 2025 11:44:51 +0000 Subject: [PATCH 04/11] Fix test output folders --- tests/test_infer_reversal.py | 2 +- tests/test_leak_correct.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_infer_reversal.py b/tests/test_infer_reversal.py index 3560d789..e2047432 100755 --- a/tests/test_infer_reversal.py +++ b/tests/test_infer_reversal.py @@ -13,7 +13,7 @@ def setUp(self): "staircaseramp (2)_2kHz_15.01.07") json_file = "staircaseramp (2)_2kHz_15.01.07.json" - self.output_dir = os.path.join('test_output', 'test_trace_class') + self.output_dir = os.path.join('test_output', self.__class__.__name__) if not os.path.exists(self.output_dir): os.makedirs(self.output_dir) diff --git a/tests/test_leak_correct.py b/tests/test_leak_correct.py index 2fa717c5..a43fc217 100755 --- a/tests/test_leak_correct.py +++ b/tests/test_leak_correct.py @@ -13,7 +13,7 @@ def setUp(self): "staircaseramp (2)_2kHz_15.01.07") json_file = "staircaseramp (2)_2kHz_15.01.07.json" - self.output_dir = os.path.join('test_output', 'test_trace_class') + self.output_dir = os.path.join('test_output', self.__class__.__name__) if not os.path.exists(self.output_dir): os.makedirs(self.output_dir) From 2f3913ac49d7e79321603a105fae4bf022faf55d Mon Sep 17 00:00:00 2001 From: "Joseph G. Shuttleworth" Date: Thu, 6 Nov 2025 11:18:58 +0000 Subject: [PATCH 05/11] Update ramp bound function and docstring --- pcpostprocess/detect_ramp_bounds.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pcpostprocess/detect_ramp_bounds.py b/pcpostprocess/detect_ramp_bounds.py index a924ba99..72f81b5c 100644 --- a/pcpostprocess/detect_ramp_bounds.py +++ b/pcpostprocess/detect_ramp_bounds.py @@ -1,23 +1,23 @@ import numpy as np -def detect_ramp_bounds(times, voltage_sections, ramp_no=0): +def detect_ramp_bounds(times, voltage_sections, ramp_index=0): """ - Extract the the times at the start and end of the nth ramp in the protocol. + Extract the timepoint indices at the start and end of the nth ramp in the protocol. @param times: np.array containing the time at which each sample was taken @param voltage_sections 2d np.array where each row describes a segment of the protocol: (tstart, tend, vstart, end) - @param ramp_no: the index of the ramp to select. Defaults to 0 - the first ramp + @param ramp_index: the index of the ramp to select. Defaults to 0 - the first ramp - @returns tstart, tend: the start and end times for the ramp_no+1^nth ramp + @returns istart, iend: the start and end timepoint indices for the specified ramp """ ramps = [(tstart, tend, vstart, vend) for tstart, tend, vstart, vend in voltage_sections if vstart != vend] try: - ramp = ramps[ramp_no] + ramp = ramps[ramp_index] except IndexError: - print(f"Requested {ramp_no+1}th ramp (ramp_no={ramp_no})," + print(f"Requested {ramp_index+1}th ramp (ramp_index={ramp_index})," " but there are only {len(ramps)} ramps") tstart, tend = ramp[:2] From 87e33138115913a39022af89605622ca1ba91526 Mon Sep 17 00:00:00 2001 From: "Joseph G. Shuttleworth" Date: Thu, 6 Nov 2025 11:21:43 +0000 Subject: [PATCH 06/11] Fix infer reversal --- pcpostprocess/infer_reversal.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pcpostprocess/infer_reversal.py b/pcpostprocess/infer_reversal.py index fd5b892b..26fcf285 100644 --- a/pcpostprocess/infer_reversal.py +++ b/pcpostprocess/infer_reversal.py @@ -33,10 +33,7 @@ def infer_reversal_potential(current, times, voltage_segments, voltages, """ # Get ramp bounds, assuming final ramp is the reversal ramp - tstart, tend = detect_ramp_bounds(times, voltage_segments, -1) - - istart = np.argmax(times > tstart) - iend = np.argmax(times > tend) + istart, iend = detect_ramp_bounds(times, voltage_segments, -1) current = current[istart:iend] voltages = voltages[istart:iend] From e26b206b74df2963d9a595bdc2f097c4773cc547 Mon Sep 17 00:00:00 2001 From: "Joseph G. Shuttleworth" Date: Thu, 6 Nov 2025 11:40:57 +0000 Subject: [PATCH 07/11] Add test for reversal inference --- tests/test_infer_reversal.py | 59 ++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100755 tests/test_infer_reversal.py diff --git a/tests/test_infer_reversal.py b/tests/test_infer_reversal.py new file mode 100755 index 00000000..3560d789 --- /dev/null +++ b/tests/test_infer_reversal.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +import os +import unittest + +from syncropatch_export.trace import Trace + +from pcpostprocess import infer_reversal, leak_correct + + +class TestInferReversal(unittest.TestCase): + def setUp(self): + test_data_dir = os.path.join('tests', 'test_data', '13112023_MW2_FF', + "staircaseramp (2)_2kHz_15.01.07") + json_file = "staircaseramp (2)_2kHz_15.01.07.json" + + self.output_dir = os.path.join('test_output', 'test_trace_class') + + if not os.path.exists(self.output_dir): + os.makedirs(self.output_dir) + + self.ramp_bounds = [1700, 2500] + self.test_trace = Trace(test_data_dir, json_file) + + # get currents and QC from trace object + self.currents = self.test_trace.get_all_traces(leakcorrect=False) + self.currents['times'] = self.test_trace.get_times() + self.currents['voltages'] = self.test_trace.get_voltage() + + self.protocol_desc = self.test_trace.get_voltage_protocol().get_all_sections() + self.voltages = self.test_trace.get_voltage() + + self.correct_Erev = -90.06155612421054001970333047211170196533203125 + + def test_plot_leak_fit(self): + well = 'A03' + sweep = 0 + + voltage = self.test_trace.get_voltage() + times = self.test_trace.get_times() + + current = self.test_trace.get_trace_sweeps(sweeps=[sweep])[well][0, :] + params, Ileak = leak_correct.fit_linear_leak(current, voltage, times, + *self.ramp_bounds, + output_dir=self.output_dir, + save_fname=f"{well}_sweep{sweep}_leak_correction") + + I_corrected = current - Ileak + + E_rev = infer_reversal.infer_reversal_potential( + I_corrected, times, self.protocol_desc, + self.voltages, + output_path=os.path.join(self.output_dir, + f"{well}_staircase"), + known_Erev=self.correct_Erev) + self.assertEqual(E_rev, self.correct_Erev) + + +if __name__ == "__main__": + pass From 63fcb988d138df50b34194c1ae74d2a3e8e517d5 Mon Sep 17 00:00:00 2001 From: "Joseph G. Shuttleworth" Date: Thu, 6 Nov 2025 14:13:32 +0000 Subject: [PATCH 08/11] Fix test infer reversal --- tests/test_infer_reversal.py | 15 ++++++++++----- tests/test_leak_correct.py | 3 +-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/tests/test_infer_reversal.py b/tests/test_infer_reversal.py index 3560d789..487cb313 100755 --- a/tests/test_infer_reversal.py +++ b/tests/test_infer_reversal.py @@ -5,6 +5,7 @@ from syncropatch_export.trace import Trace from pcpostprocess import infer_reversal, leak_correct +from pcpostprocess.detect_ramp_bounds import detect_ramp_bounds class TestInferReversal(unittest.TestCase): @@ -13,12 +14,11 @@ def setUp(self): "staircaseramp (2)_2kHz_15.01.07") json_file = "staircaseramp (2)_2kHz_15.01.07.json" - self.output_dir = os.path.join('test_output', 'test_trace_class') + self.output_dir = os.path.join('test_output', self.__class__.__name__) if not os.path.exists(self.output_dir): os.makedirs(self.output_dir) - self.ramp_bounds = [1700, 2500] self.test_trace = Trace(test_data_dir, json_file) # get currents and QC from trace object @@ -27,12 +27,16 @@ def setUp(self): self.currents['voltages'] = self.test_trace.get_voltage() self.protocol_desc = self.test_trace.get_voltage_protocol().get_all_sections() + self.leak_ramp_bound_indices = detect_ramp_bounds(self.currents['times'], + self.protocol_desc, + ramp_index=0) + self.voltages = self.test_trace.get_voltage() - self.correct_Erev = -90.06155612421054001970333047211170196533203125 + self.correct_Erev = -89.57184330525791438049054704606533050537109375 def test_plot_leak_fit(self): - well = 'A03' + well = "A03" sweep = 0 voltage = self.test_trace.get_voltage() @@ -40,7 +44,7 @@ def test_plot_leak_fit(self): current = self.test_trace.get_trace_sweeps(sweeps=[sweep])[well][0, :] params, Ileak = leak_correct.fit_linear_leak(current, voltage, times, - *self.ramp_bounds, + *self.leak_ramp_bound_indices, output_dir=self.output_dir, save_fname=f"{well}_sweep{sweep}_leak_correction") @@ -52,6 +56,7 @@ def test_plot_leak_fit(self): output_path=os.path.join(self.output_dir, f"{well}_staircase"), known_Erev=self.correct_Erev) + self.assertEqual(E_rev, self.correct_Erev) diff --git a/tests/test_leak_correct.py b/tests/test_leak_correct.py index a764b206..17fa391d 100755 --- a/tests/test_leak_correct.py +++ b/tests/test_leak_correct.py @@ -14,8 +14,7 @@ def setUp(self): "staircaseramp (2)_2kHz_15.01.07") json_file = "staircaseramp (2)_2kHz_15.01.07.json" - self.output_dir = os.path.join("test_output", - self.__class__.__name__) + self.output_dir = os.path.join('test_output', self.__class__.__name__) os.makedirs(self.output_dir, exist_ok=True) From ccae12242672e839617f35bf7f27a622c6964506 Mon Sep 17 00:00:00 2001 From: "Joseph G. Shuttleworth" Date: Thu, 6 Nov 2025 14:22:22 +0000 Subject: [PATCH 09/11] Update test --- tests/test_infer_reversal.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/test_infer_reversal.py b/tests/test_infer_reversal.py index 0737a95c..83ded489 100755 --- a/tests/test_infer_reversal.py +++ b/tests/test_infer_reversal.py @@ -32,15 +32,10 @@ def setUp(self): ramp_index=0) self.voltages = self.test_trace.get_voltage() - self.correct_Erev = -89.57184330525791438049054704606533050537109375 def test_plot_leak_fit(self): well = "A03" - self.voltages = self.test_trace.get_voltage() - - self.correct_Erev = -90.06155612421054001970333047211170196533203125 - sweep = 0 voltage = self.test_trace.get_voltage() From 93d4ca4ef81ad0ea0252b139d9e53260bae47094 Mon Sep 17 00:00:00 2001 From: "Joseph G. Shuttleworth" Date: Thu, 6 Nov 2025 14:22:30 +0000 Subject: [PATCH 10/11] Update test --- tests/test_leak_correct.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_leak_correct.py b/tests/test_leak_correct.py index 17fa391d..4b4ed399 100755 --- a/tests/test_leak_correct.py +++ b/tests/test_leak_correct.py @@ -30,7 +30,7 @@ def setUp(self): # Find first times ahead of these times voltage_protocol = self.test_trace.get_voltage_protocol().get_all_sections() times = self.currents['times'].flatten() - self.ramp_bound_indices = detect_ramp_bounds(times, voltage_protocol, ramp_no=0) + self.ramp_bound_indices = detect_ramp_bounds(times, voltage_protocol, ramp_index=0) def test_plot_leak_fit(self): well = 'A01' From 72cd27a3acc21690b86aaf217d569787c39480e4 Mon Sep 17 00:00:00 2001 From: "Joseph G. Shuttleworth" Date: Thu, 6 Nov 2025 14:33:32 +0000 Subject: [PATCH 11/11] Modify test --- tests/test_infer_reversal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_infer_reversal.py b/tests/test_infer_reversal.py index 83ded489..75070e9d 100755 --- a/tests/test_infer_reversal.py +++ b/tests/test_infer_reversal.py @@ -55,7 +55,7 @@ def test_plot_leak_fit(self): output_path=os.path.join(self.output_dir, f"{well}_staircase"), known_Erev=self.correct_Erev) - self.assertEqual(E_rev, self.correct_Erev) + self.assertLess(abs(E_rev - self.correct_Erev), 1e-5) if __name__ == "__main__":