myokit 1.37.3__py3-none-any.whl → 1.37.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
myokit/_datablock.py CHANGED
@@ -2169,8 +2169,7 @@ class ColorMapViridis(ColorMap):
2169
2169
  https://github.com/BIDS/colormap/blob/master/colormaps.py
2170
2170
  and were distributed under a CC0 license.
2171
2171
 
2172
- Viridis was designed by Eric Firing, Nathaniel J. Smith, and Stefan van der
2173
- Walt.
2172
+ Viridis was designed by Nathaniel J. Smith and Stefan van der Walt.
2174
2173
  """
2175
2174
  _VALUES = np.array((
2176
2175
  (68, 1, 84),
@@ -2456,14 +2455,13 @@ class ColorMapViridis(ColorMap):
2456
2455
 
2457
2456
  class ColorMapInferno(ColorMap):
2458
2457
  """
2459
- A colormap using "Viridis".
2458
+ A colormap using "Inferno".
2460
2459
 
2461
- The values used by this color map are taken from
2460
+ The values used by this color map are adapted from
2462
2461
  https://github.com/BIDS/colormap/blob/master/colormaps.py
2463
2462
  and were distributed under a CC0 license.
2464
2463
 
2465
- Viridis was designed by Eric Firing, Nathaniel J. Smith, and Stefan van der
2466
- Walt.
2464
+ Inferno was designed by Nathaniel J. Smith and Stefan van der Walt.
2467
2465
  """
2468
2466
  _VALUES = np.array((
2469
2467
  (0, 0, 4),
myokit/_myokit_version.py CHANGED
@@ -14,7 +14,7 @@ __release__ = True
14
14
  # incompatibility
15
15
  # - Changes to revision indicate bugfixes, tiny new features
16
16
  # - There is no significance to odd/even numbers
17
- __version_tuple__ = 1, 37, 3
17
+ __version_tuple__ = 1, 37, 5
18
18
 
19
19
  # String version of the version number
20
20
  __version__ = '.'.join([str(x) for x in __version_tuple__])
myokit/_parsing.py CHANGED
@@ -949,6 +949,10 @@ def parse_protocol_from_stream(stream):
949
949
  'Expecting [[protocol]]')
950
950
  expect(next(stream), EOL)
951
951
 
952
+ # Handling "next" accuracy: after summing, times set using "next" will be
953
+ # rounded to 9 digits precision.
954
+ round_next = lambda x: round(x, 9)
955
+
952
956
  # Create protocol
953
957
  protocol = myokit.Protocol()
954
958
 
@@ -971,7 +975,7 @@ def parse_protocol_from_stream(stream):
971
975
  'Unable to determine end of previous event, "next" cannot'
972
976
  ' be used here.')
973
977
  else:
974
- t = t_next
978
+ t = round_next(t_next)
975
979
  else:
976
980
  t = parse_number(stream)
977
981
  if t_last is None:
myokit/_sim/cable.c CHANGED
@@ -272,7 +272,7 @@ sim_clean()
272
272
  return 0;
273
273
  }
274
274
  static PyObject*
275
- py_sim_clean()
275
+ py_sim_clean(PyObject *self, PyObject *args)
276
276
  {
277
277
  sim_clean();
278
278
  Py_RETURN_NONE;
@@ -291,7 +291,7 @@ sim_clean()
291
291
  return 0;
292
292
  }
293
293
  static PyObject*
294
- py_sim_clean()
294
+ py_sim_clean(PyObject *self, PyObject *args)
295
295
  {
296
296
  #ifdef MYOKIT_DEBUG_MESSAGES
297
297
  printf("Python py_sim_clean called.\n");
myokit/_unit.py CHANGED
@@ -186,7 +186,7 @@ class Unit:
186
186
  ``unit2``.
187
187
 
188
188
  Exponents are stored internally as floating point numbers, and the
189
- check for closeness if made with a relative tolerance of ``reltol`` and
189
+ check for closeness is made with a relative tolerance of ``reltol`` and
190
190
  absolute tolerance of ``abstol``, using::
191
191
 
192
192
  abs(a - b) < max(reltol * max(abs(a), abs(b)), abstol)
@@ -254,7 +254,6 @@ class AbfFile(myokit.formats.SweepSource):
254
254
  # Read the protocol information
255
255
  self._n_adc = None
256
256
  self._n_dac = None
257
-
258
257
  self._rate = None
259
258
  self._mode = None
260
259
 
@@ -264,6 +263,7 @@ class AbfFile(myokit.formats.SweepSource):
264
263
  self._run_start_to_start = None
265
264
  self._sweeps_per_run = None
266
265
  self._sweep_start_to_start = None
266
+ self._equal_length_sweeps = None
267
267
  self._samples_per_channel = None
268
268
 
269
269
  # To be able to treat v1 and v2 slightly more easily, we define 3
@@ -281,7 +281,6 @@ class AbfFile(myokit.formats.SweepSource):
281
281
  # will have A/D but no (or no supported) D/A. Conversely protocol files
282
282
  # will have D/A only. So all in one sweep is easiest.
283
283
  self._sweeps = None
284
-
285
284
  self._read_3_protocol_information()
286
285
 
287
286
  # Read and calculate conversion factors for integer data in ADC
@@ -553,7 +552,10 @@ class AbfFile(myokit.formats.SweepSource):
553
552
  # 1.x versions only seem to have 1 DAC channel, but this is not
554
553
  # supported here.
555
554
  self._n_adc = int(h['nADCNumChannels'])
556
- self._n_dac = min(len(h['sDACChannelName']), 2)
555
+ if h['sDACChannelName'] is None: # pragma: no cover
556
+ self._n_dac = 2
557
+ else:
558
+ self._n_dac = min(len(h['sDACChannelName']), 2)
557
559
  self._rate = 1e6 / (h['fADCSampleInterval'] * self._n_adc)
558
560
  self._mode = h['nOperationMode']
559
561
  else:
@@ -577,11 +579,7 @@ class AbfFile(myokit.formats.SweepSource):
577
579
  'Unsupported acquisition method'
578
580
  f' {acquisition_modes[self._mode]}; unable to read D/A'
579
581
  ' channels.')
580
-
581
- # Remaining code is all about reading D/A info for episodic
582
- # stimulation, so return
583
582
  self._n_dac = 0
584
- return
585
583
 
586
584
  # Gather protocol information
587
585
  if self._version < 2:
@@ -668,9 +666,16 @@ class AbfFile(myokit.formats.SweepSource):
668
666
  self._sweep_start_to_start = self._samples_per_channel / self._rate
669
667
 
670
668
  # Create empty sweeps
671
- n = h['lActualSweeps']
672
- if self._is_protocol_file:
669
+ if self._mode == ACMODE_GAP_FREE: # pragma: no cover
670
+ # In gap-free mode, treat the data as a single sweep
671
+ n = 1
672
+ elif self._is_protocol_file:
673
+ # In protocol file, there is no number of recorded sweeps, so use
674
+ # intended number of sweeps instead
673
675
  n = self._sweeps_per_run
676
+ else:
677
+ # Use number of recorded sweeps
678
+ n = h['lActualSweeps']
674
679
  self._sweeps = [Sweep() for i in range(n)]
675
680
 
676
681
  # User lists are not supported for D/A reconstruction
@@ -811,7 +816,8 @@ class AbfFile(myokit.formats.SweepSource):
811
816
  # Get binary integer format
812
817
  dt = np.dtype('i2') if h['nDataFormat'] == 0 else np.dtype('f4')
813
818
 
814
- # Get number of channels, create a numpy memory map
819
+ # Get number of channels, create a numpy memory map of all data
820
+ # The map has size n and starts at offset o
815
821
  if self._version < 2:
816
822
  # Old files, get info from fields stored directly in header
817
823
  o = h['lDataSectionPtr'] * BLOCKSIZE \
@@ -823,37 +829,57 @@ class AbfFile(myokit.formats.SweepSource):
823
829
  n = h['sections']['Data']['length']
824
830
  data = np.memmap(self._filepath, dt, 'r', shape=(n,), offset=o)
825
831
 
826
- # Load list of sweeps (Sweeps are called 'episodes' in ABF < 2)
832
+ # Load information about sweep sizes from "SynchArray".
833
+ # The number of sweep information structs will be n, and information
834
+ # starts at an offset o
827
835
  if self._version < 2:
828
836
  n = h['lSynchArraySize']
829
837
  o = h['lSynchArrayPtr'] * BLOCKSIZE
830
838
  else:
831
839
  n = h['sections']['SynchArray']['length']
832
840
  o = h['sections']['SynchArray']['index'] * BLOCKSIZE
833
- if n > 0:
834
- dt = [(str('offset'), str('i4')), (str('len'), str('i4'))]
835
- sweep_data = np.memmap(
836
- self._filepath, dt, 'r', shape=(n,), offset=o)
837
- else: # pragma: no cover
838
- # Cover pragma: Don't have appropriate test file
839
- sweep_data = np.empty((1), dt)
840
- sweep_data[0]['len'] = data.size
841
- sweep_data[0]['offset'] = 0
842
841
 
843
- # Number of sweeps must equal n
844
- if n != h['lActualSweeps']:
845
- raise NotImplementedError(
846
- 'Unable to read file with different sizes per sweep.')
847
-
848
- # Time-offset at start of first sweep
849
- start = sweep_data[0]['offset'] / rate
842
+ # Get sweep start times (real times, not array offsets) and sizes
843
+ # (as number of points in an array)
844
+ if self._mode != ACMODE_GAP_FREE:
845
+ # This will be read in as an array of numpy.Void objects, which are
846
+ # similar to structs. In ``dt`` we name the entries offset and len.
847
+ sweep_starts_sizes = np.memmap(
848
+ self._filepath, [('offset', 'i4'), ('len', 'i4')], 'r',
849
+ shape=(n,), offset=o)
850
+ else: # pragma: no cover
851
+ # In gap-free model, all data is a single sweep
852
+ sweep_starts_sizes = [(0, len(data))]
853
+
854
+ # Sanity check
855
+ if len(sweep_starts_sizes) != len(self._sweeps): # pragma: no cover
856
+ raise RuntimeError(
857
+ 'Unexpected situation: number of sweeps is'
858
+ f' {len(self._sweeps)} but found list of'
859
+ f' {len(sweep_starts_sizes)} sweep sizes.')
860
+
861
+ # Check if sweep sizes are all the same
862
+ self._equal_length_sweeps = True
863
+ if len(sweep_starts_sizes) > 0:
864
+ size = sweep_starts_sizes[0][1]
865
+ for o, s in sweep_starts_sizes[1:]:
866
+ if s != size: # pragma: no cover
867
+ self._equal_length_sweeps = False
868
+ break
869
+ del size
870
+
871
+ # Episodic stimulation? Then increment start time using sweep interval
872
+ # First time is time listed in first sweep
873
+ if self._mode == ACMODE_EPISODIC_STIMULATION:
874
+ tstart = sweep_starts_sizes[0][0] / rate
850
875
 
851
876
  # Get data
852
877
  pos = 0
853
- for i_sweep, sdat in enumerate(sweep_data):
878
+ for i_sweep, (start, size) in enumerate(sweep_starts_sizes):
854
879
 
855
- # Get the number of data points
856
- size = sdat['len']
880
+ # Not episodic: Then use listed start time
881
+ if self._mode != ACMODE_EPISODIC_STIMULATION: # pragma: no cover
882
+ tstart = start / rate
857
883
 
858
884
  # Calculate the correct size for variable-length event mode
859
885
  if self._mode == ACMODE_VARIABLE_LENGTH_EVENTS: # pragma: no cover
@@ -874,25 +900,20 @@ class AbfFile(myokit.formats.SweepSource):
874
900
  # If needed, reformat the integers
875
901
  if h['nDataFormat'] == 0:
876
902
  # Data given as integers? Convert to floating point
877
-
878
903
  for i in range(self._n_adc):
879
904
  part[:, i] *= self._adc_factors[i]
880
905
  part[:, i] += self._adc_offsets[i]
881
906
 
882
- # Get start in other modes
883
- if self._mode != ACMODE_EPISODIC_STIMULATION: # pragma: no cover
884
- # All modes except episodic stimulation
885
- start = data['offset'] / rate
886
-
887
- # Create and populate sweep
907
+ # Create and populate sweep object
888
908
  sweep = self._sweeps[i_sweep]
889
909
  for i in range(self._n_adc):
890
910
  c = Channel(self)
891
911
  c._data = part[:, i] # Actually store the data
892
912
  c._rate = rate
893
- c._start = start
913
+ c._start = tstart
894
914
  c._is_reconstruction = False
895
915
 
916
+ # Add meta data
896
917
  if self._version < 2:
897
918
  j = h['nADCSamplingSeq'][i]
898
919
 
@@ -947,9 +968,9 @@ class AbfFile(myokit.formats.SweepSource):
947
968
 
948
969
  sweep._channels.append(c)
949
970
 
971
+ # In episodic mode, increase time according to sweep interval
950
972
  if self._mode == ACMODE_EPISODIC_STIMULATION:
951
- # Increase time according to sweeps in episodic stim. mode
952
- start += self._sweep_start_to_start
973
+ tstart += self._sweep_start_to_start
953
974
 
954
975
  def _read_6_da_reconstructions(self):
955
976
  """
@@ -1034,6 +1055,12 @@ class AbfFile(myokit.formats.SweepSource):
1034
1055
 
1035
1056
  return int_id
1036
1057
 
1058
+ def acquisition_mode_str(self):
1059
+ """
1060
+ Returns the acquisition mode, as a string.
1061
+ """
1062
+ return acquisition_modes[self._mode]
1063
+
1037
1064
  def channel(self, channel_id, join_sweeps=False):
1038
1065
  # Docstring in SweepSource
1039
1066
  channel_id = self._channel_id(channel_id)
@@ -1193,8 +1220,7 @@ class AbfFile(myokit.formats.SweepSource):
1193
1220
  return self._da_units[index]
1194
1221
 
1195
1222
  def equal_length_sweeps(self):
1196
- # Always true for ABF
1197
- return True
1223
+ return self._equal_length_sweeps
1198
1224
 
1199
1225
  def filename(self):
1200
1226
  """ Returns this ABF file's filename. """
@@ -1530,13 +1556,13 @@ HEADER_FIELDS = {
1530
1556
  # Note that a lot of the groups in the version 1 header start with obsolete
1531
1557
  # fields, followed later by their newer equivalents.
1532
1558
  1: [
1533
- ('fFileSignature', 0, '4s'), # Coarse file version indication
1559
+ ('fFileSignature', 0, '4s'), # Coarse file version indication
1534
1560
  # Group 1, File info and sizes
1535
- ('fFileVersionNumber', 4, 'f'), # Version number as float
1536
- ('nOperationMode', 8, 'h'), # Acquisition mode
1561
+ ('fFileVersionNumber', 4, 'f'), # Version number as float
1562
+ ('nOperationMode', 8, 'h'), # Acquisition mode
1537
1563
  ('lActualAcqLength', 10, 'i'),
1538
1564
  ('nNumPointsIgnored', 14, 'h'),
1539
- ('lActualSweeps', 16, 'i'),
1565
+ ('lActualSweeps', 16, 'i'), # AKA lActualEpisodes
1540
1566
  ('lFileStartDate', 20, 'i'),
1541
1567
  ('lFileStartTime', 24, 'i'),
1542
1568
  ('lStopWatchTime', 28, 'i'),
@@ -3,8 +3,6 @@
3
3
  #
4
4
  # Specifically, it targets the 2x90.2 format.
5
5
  #
6
- # It has not been extensively tested.
7
- #
8
6
  # Notes:
9
7
  # - HEKA publishes a lot of information about its file format on its FTP
10
8
  # server: server.hekahome.de
@@ -25,6 +23,9 @@
25
23
  # numbers of steps are given; the manual mentions two types of log
26
24
  # interpretation but there is no obvious field to select one; a "toggle"
27
25
  # mode is mentioned in the manual but again is not easy to find in the file.
26
+ # - Series resistance prediction percentage is not stored separately in the
27
+ # Patchmaster file. In addition, the value stored seems to be either
28
+ # percentage compensation or prediction, without much consistency.
28
29
  #
29
30
  # This file is part of Myokit.
30
31
  # See http://myokit.org for copyright, sharing, and licensing details.
@@ -78,7 +79,8 @@ class PatchMasterFile:
78
79
  represent a single cell, and each "series" will be a protocol (called a
79
80
  "stimulus") run on that cell. Groups are named by the user. Series are
80
81
  named after the "stimulus" they run. Sweeps are usually unnamed (although
81
- they do have a ``label`` property), and channels are named by the user.
82
+ they do have a ``label`` property), and traces (channels) are named by the
83
+ user.
82
84
 
83
85
  To access groups, index them by integer, or use the :meth:`group` method to
84
86
  find the first group with a given label::
@@ -236,6 +238,9 @@ class PatchMasterFile:
236
238
  def __iter__(self):
237
239
  return iter(self._pulsed_tree)
238
240
 
241
+ def __len__(self):
242
+ return len(self._pulsed_tree)
243
+
239
244
  def amplifier_tree(self):
240
245
  """
241
246
  Returns this file's amplifier tree (an :class:`AmplifierFile` object),
@@ -291,8 +296,6 @@ class EndianAwareReader:
291
296
 
292
297
  def read(self, form):
293
298
  """ Read and unpack using the struct format ``form``. """
294
- #if offset is not None:
295
- # f.seek(offset)
296
299
  return struct.unpack(
297
300
  self._e + form, self._f.read(struct.calcsize(form)))
298
301
 
@@ -626,7 +629,7 @@ class Series(TreeNode, myokit.formats.SweepSource):
626
629
  1. In the individual :class:`Trace` objects. This is somewhat
627
630
  counter-intuitive as some of these properties (e.g.
628
631
  :meth:Trace.r_pipette()`) were set before a series was
629
- acquired and do no change between channels or sweeps.
632
+ acquired and do not change between channels or sweeps.
630
633
  2. In the series' :class:`AmplifierState`, which can be accessed via
631
634
  the :meth:`amplifier_state()` method.
632
635
 
@@ -984,6 +987,8 @@ class Series(TreeNode, myokit.formats.SweepSource):
984
987
  log.meta[f'{pre}current_gain_mV_per_pA'] = a.current_gain()
985
988
  log.meta[f'{pre}filter1'] = a.filter1_str()
986
989
  log.meta[f'{pre}filter2'] = a.filter2_str()
990
+ log.meta[f'{pre}filter1_kHz'] = a.filter1().frequency()
991
+ log.meta[f'{pre}filter2_kHz'] = a.filter2()[2]
987
992
  log.meta[f'{pre}stimulus_filter'] = a.stimulus_filter_str()
988
993
  log.meta[f'{pre}ljp_correction_mV'] = a.ljp()
989
994
  log.meta[f'{pre}voltage_offset_mV'] = a.v_off()
@@ -991,6 +996,10 @@ class Series(TreeNode, myokit.formats.SweepSource):
991
996
  if a.c_fast_enabled():
992
997
  log.meta[f'{pre}c_fast_compensation_enabled'] = 'true'
993
998
  log.meta[f'{pre}c_fast_pF'] = a.c_fast()
999
+ cf = a.c_fast_detailed()
1000
+ log.meta[f'{pre}c_fast_amp1_pF'] = cf[0]
1001
+ log.meta[f'{pre}c_fast_amp2_pF'] = cf[1]
1002
+ log.meta[f'{pre}c_fast_tau2_us'] = cf[2]
994
1003
  else:
995
1004
  log.meta[f'{pre}c_fast_compensation_enabled'] = 'false'
996
1005
  log.meta[f'{pre}c_slow_pF'] = a.c_slow()
@@ -1080,6 +1089,9 @@ class Series(TreeNode, myokit.formats.SweepSource):
1080
1089
  # C fast
1081
1090
  if a.c_fast_enabled():
1082
1091
  out.append(f' C fast compensation: {a.c_fast()} pF,')
1092
+ cf = a.c_fast_detailed()
1093
+ out.append(f' C fast (amp1, amp2, tau2): {cf[0]} pF, {cf[1]}'
1094
+ f' pF, {cf[2]} us')
1083
1095
  else:
1084
1096
  out.append(' C fast compensation: not enabled')
1085
1097
  # C slow
@@ -1197,7 +1209,6 @@ class Sweep(TreeNode):
1197
1209
  # Seconds since first sweep, based on self._time, set by parent series
1198
1210
  self._time_since_first = None
1199
1211
 
1200
- #self._data_offset = None
1201
1212
  self._stimulus_id = None
1202
1213
 
1203
1214
  def _read_properties(self, handle, reader):
@@ -1205,7 +1216,6 @@ class Sweep(TreeNode):
1205
1216
  i = handle.tell()
1206
1217
  handle.seek(i + 4) # SwLabel = 4; (* String32Type *)
1207
1218
  self._label = reader.str(32)
1208
- #self._data_offset = reader.read1('i')
1209
1219
  handle.seek(i + 40) # SwStimCount = 40; (* INT32 *)
1210
1220
  self._stimulus_id = reader.read1('i') - 1
1211
1221
  handle.seek(i + 48) # SwTime = 48; (* LONGREAL *)
@@ -1352,6 +1362,7 @@ class Trace(TreeNode):
1352
1362
  handle.seek(i + 40) # TrData
1353
1363
  self._data_pos = reader.read1('i')
1354
1364
  self._n = reader.read1('i')
1365
+ handle.seek(i + 70) # TrDataFormat
1355
1366
  dtype = int(reader.read1('b'))
1356
1367
  self._data_type = _data_types[dtype]
1357
1368
  self._data_size = _data_sizes[dtype]
@@ -1635,6 +1646,20 @@ class Trace(TreeNode):
1635
1646
  class AmplifierState:
1636
1647
  """
1637
1648
  Describes the state of an amplifier used by PatchMaster.
1649
+
1650
+ This "state" contains lots of valuable meta data about the fast and slow
1651
+ capacitance correction, the series resistance compensation (and/or
1652
+ prediction, but see below!), the filtering, the gain, and voltage offset
1653
+ corrections (zeroing and online LJP).
1654
+
1655
+ A notable issue is information omission is information about the series
1656
+ compensation _prediction_ feature. By default, the level of prediction is
1657
+ the same as the level of correction. A checkbox in the software (which the
1658
+ manual erroneously described as an on/off switch) can be used to set both
1659
+ values independently. If this is done, it becomes unclear what the value in
1660
+ the file represents, as only one value is stored. For this reason, if
1661
+ prediction is set seperately from correction, both values must be noted
1662
+ down by the experimenter and the value from the file should be ignored.
1638
1663
  """
1639
1664
  def __init__(self, handle, reader):
1640
1665
 
@@ -1697,10 +1722,13 @@ class AmplifierState:
1697
1722
  # 2 is the combined bandwidth of Filter 1 and Filter 2.
1698
1723
  handle.seek(i + 239) # sF2Response = 239; (* BYTE *)
1699
1724
  self._filter2_type = Filter2Type(reader.read1('b'))
1700
- handle.seek(i + 24) # sF2Frequency = 24; (* LONGREAL *)
1701
- self._filter2_freq_solo = reader.read1('d')
1725
+ if self._filter2_type is Filter2Type.BYPASS:
1726
+ self._filter2_freq_solo = 0
1727
+ else:
1728
+ handle.seek(i + 24) # sF2Frequency = 24; (* LONGREAL *)
1729
+ self._filter2_freq_solo = reader.read1('d') * 1e-3
1702
1730
  handle.seek(i + 16) # sF2Bandwidth = 16; (* LONGREAL *)
1703
- self._filter2_freq_both = reader.read1('d')
1731
+ self._filter2_freq_both = reader.read1('d') * 1e-3
1704
1732
 
1705
1733
  # Suspect this indicates external filter 2
1706
1734
  #handle.seek(i + 287) # sF2Source = 287; (* BYTE *)
@@ -1818,11 +1846,13 @@ class AmplifierState:
1818
1846
  """
1819
1847
  Returns a tuple ``(type, f_both, f_solo)`` describing the Filter 2
1820
1848
  settings, where ``type`` is a :class:`Filter2Type`, where ``f_both`` is
1821
- the frequency in Hz of both filters combined, and when ``f_solo`` is
1822
- the frequency of Filter 2 alone.
1849
+ the frequency in kHz of both filters combined, and when ``f_solo`` is
1850
+ the frequency in kHz of Filter 2 alone.
1823
1851
 
1824
1852
  The frequency shown in PatchMaster is that of both filters combined.
1825
1853
  If the "bypass" filter is selected, the frequency settings are unused.
1854
+ In this case, ``f_both`` will be the frequency of filter 1 and
1855
+ ``f_solo`` will be returned as 0.
1826
1856
 
1827
1857
  For more information on Filter 1 and 2, see :class:`Filter1Setting`.
1828
1858
  """
@@ -1840,8 +1870,8 @@ class AmplifierState:
1840
1870
  """
1841
1871
  if self._filter2_type is Filter2Type.BYPASS:
1842
1872
  return str(self._filter2_type)
1843
- fb = round(self._filter2_freq_both * 1e-3, 2)
1844
- fs = round(self._filter2_freq_solo * 1e-3, 2)
1873
+ fb = round(self._filter2_freq_both, 2)
1874
+ fs = round(self._filter2_freq_solo, 2)
1845
1875
  fb = int(fb) if fb == int(fb) else fb
1846
1876
  return f'{self._filter2_type} {fb} kHz combined, {fs} kHz f2-only'
1847
1877
 
@@ -1949,6 +1979,17 @@ class Filter1Setting(enum.Enum):
1949
1979
  else:
1950
1980
  return 'HQ 30 kHz'
1951
1981
 
1982
+ def frequency(self):
1983
+ """ Returns the cut-off frequency for this filter setting, in kHz. """
1984
+ if self is Filter1Setting.BESSEL_100K:
1985
+ return 100
1986
+ elif self is Filter1Setting.BESSEL_30K:
1987
+ return 30
1988
+ elif self is Filter1Setting.BESSEL_10K:
1989
+ return 10
1990
+ else:
1991
+ return 30
1992
+
1952
1993
 
1953
1994
  class Filter2Type(enum.Enum):
1954
1995
  """
@@ -313,15 +313,18 @@ class Model:
313
313
  variable.uname(),
314
314
  variable.is_constant(),
315
315
  )
316
- if variable.is_state() or variable.is_literal():
317
- v.set_value(variable.rhs(), variable.is_state())
318
- else:
319
- # if variable is constant but not literal,
320
- # set via initial value
321
- v.set_initial_value(variable.rhs())
322
316
 
323
- if variable.is_state():
324
- v.set_initial_value(variable.initial_value())
317
+ if variable.is_constant():
318
+ if variable.is_literal():
319
+ v.set_value(variable.rhs())
320
+ else:
321
+ v.set_initial_value(variable.rhs())
322
+ else:
323
+ if variable.is_state():
324
+ v.set_initial_value(variable.initial_value())
325
+ v.set_value(variable.rhs(), True)
326
+ else:
327
+ v.set_value(variable.rhs())
325
328
 
326
329
  unit = variable_unit(variable, time_unit)
327
330
  if unit is not None:
@@ -63,8 +63,8 @@ SETTINGS_FILE = os.path.join(myokit.DIR_USER, 'DataLogViewer.ini')
63
63
  # About
64
64
  ABOUT = '<h1>' + TITLE + '</h1>' + """
65
65
  <p>
66
- The DataLog viewer is a PROTOTYPE utility to examine time series data.
67
- At the moment, exclusively WinWCP, ABF and CSV files.
66
+ The DataLog viewer is a PROTOTYPE utility to examine time series data in
67
+ formats supported by Myokit.
68
68
  </p>
69
69
  <p>
70
70
  System info:
@@ -162,6 +162,8 @@ class DataLogViewer(myokit.gui.MyokitApplication):
162
162
  self._tool_next_file.setEnabled(True)
163
163
  self._tool_prev_file.setEnabled(True)
164
164
  self._tabs.setCurrentIndex(0)
165
+ self._tool_close.setEnabled(True)
166
+ self._tool_close_all.setEnabled(True)
165
167
 
166
168
  def action_about(self):
167
169
  """
@@ -178,15 +180,48 @@ class DataLogViewer(myokit.gui.MyokitApplication):
178
180
  self._tabs.removeTab(index)
179
181
 
180
182
  # Update buttons
181
- if self._tabs.count() < 2:
183
+ n = self._tabs.count()
184
+ if n < 2:
182
185
  self._tool_next_file.setEnabled(False)
183
186
  self._tool_prev_file.setEnabled(False)
187
+ if n < 1:
188
+ self._tool_close.setEnabled(False)
189
+ self._tool_close_all.setEnabled(False)
184
190
 
185
191
  # Delete tab
186
192
  if tab is not None:
187
193
  tab.deleteLater()
188
- gc.collect()
189
194
  del tab
195
+ gc.collect()
196
+
197
+ def action_close_current(self):
198
+ """ Close the current tab. """
199
+ idx = self._tabs.currentIndex()
200
+ if idx >= 0:
201
+ self.action_close(idx)
202
+
203
+ def action_close_all(self):
204
+ """ Called to close all tabs. """
205
+ n = self._tabs.count()
206
+ if n < 1:
207
+ return
208
+
209
+ # Get all tabs, to delete later
210
+ tabs = [self._tabs.widget(i) for i in range(n)]
211
+ # Remove all tabs, without deleting
212
+ self._tabs.clear()
213
+
214
+ # Update buttons
215
+ self._tool_close.setEnabled(False)
216
+ self._tool_close_all.setEnabled(False)
217
+ self._tool_next_file.setEnabled(False)
218
+ self._tool_prev_file.setEnabled(False)
219
+
220
+ # Delete tabs
221
+ for tab in tabs:
222
+ tab.deleteLater()
223
+ del tab, tabs
224
+ gc.collect()
190
225
 
191
226
  def action_first_var(self):
192
227
  """ Select the first variable in the current file. """
@@ -225,6 +260,10 @@ class DataLogViewer(myokit.gui.MyokitApplication):
225
260
  if tab_count_new > tab_count:
226
261
  self._tabs.setCurrentIndex(tab_count)
227
262
 
263
+ # Enable tab closing buttons
264
+ self._tool_close.setEnabled(True)
265
+ self._tool_close_all.setEnabled(True)
266
+
228
267
  # Enable next/previous file menu items
229
268
  if tab_count_new > 1:
230
269
  self._tool_next_file.setEnabled(True)
@@ -285,6 +324,21 @@ class DataLogViewer(myokit.gui.MyokitApplication):
285
324
  self._tool_open.setIcon(QtGui.QIcon.fromTheme('document-open'))
286
325
  self._tool_open.triggered.connect(self.action_open)
287
326
  self._menu_file.addAction(self._tool_open)
327
+ # File > Close
328
+ self._tool_close = QtGui.QAction('&Close tab', self)
329
+ self._tool_close.setShortcut('Ctrl+W')
330
+ self._tool_close.setStatusTip('Close the current tab')
331
+ self._tool_close.setIcon(QtGui.QIcon.fromTheme('document-close'))
332
+ self._tool_close.triggered.connect(self.action_close_current)
333
+ self._tool_close.setEnabled(False)
334
+ self._menu_file.addAction(self._tool_close)
335
+ # File > Close all
336
+ self._tool_close_all = QtGui.QAction('Close &all tabs', self)
337
+ self._tool_close_all.setStatusTip('Close all tabs')
338
+ self._tool_close_all.setIcon(QtGui.QIcon.fromTheme('document-close'))
339
+ self._tool_close_all.triggered.connect(self.action_close_all)
340
+ self._tool_close_all.setEnabled(False)
341
+ self._menu_file.addAction(self._tool_close_all)
288
342
  # File > ----
289
343
  self._menu_file.addSeparator()
290
344
  # File > Quit
@@ -401,9 +455,7 @@ class DataLogViewer(myokit.gui.MyokitApplication):
401
455
  self._path = path
402
456
 
403
457
  def load_file(self, filename):
404
- """
405
- Loads a data file.
406
- """
458
+ """ Loads a data file. """
407
459
  root, ext = os.path.splitext(os.path.basename(filename))
408
460
  try:
409
461
  action = self._load_actions[ext.lower()]
@@ -700,7 +752,8 @@ class SweepSourceTab(GraphTabWidget):
700
752
  widget.setLayout(vbox)
701
753
 
702
754
  # Add tab
703
- self.addTab(widget, name)
755
+ adda = 'D/A' if da else 'A/D'
756
+ self.addTab(widget, f'{adda}: {name}')
704
757
 
705
758
  '''
706
759
  def debug_tab(self, channel_index, da=True):
@@ -208,6 +208,8 @@ class AbfTest(unittest.TestCase):
208
208
  self.assertIn('version 1.65', abf.meta_str())
209
209
  self.maxDiff = None
210
210
  self.assertEqual(abf.meta_str(), V1_INFO)
211
+ self.assertEqual(abf.acquisition_mode_str(),
212
+ 'Episodic stimulation mode')
211
213
 
212
214
  # Test getting full header runs without crashing
213
215
  abf.meta_str(True)
@@ -445,6 +447,8 @@ class AbfTest(unittest.TestCase):
445
447
  self.assertIn('version 2.0', abf.meta_str())
446
448
  self.maxDiff = None
447
449
  self.assertEqual(abf.meta_str(), V2_INFO)
450
+ self.assertEqual(abf.acquisition_mode_str(),
451
+ 'Episodic stimulation mode')
448
452
 
449
453
  # Test getting full header runs without crashing
450
454
  abf.meta_str(True)
@@ -170,6 +170,7 @@ class TestSBMLExport(unittest.TestCase):
170
170
  self.assertIn("<listOfUnitDefinitions>", sbml_str)
171
171
  self.assertIn('<unitDefinition id="m_per_s_times_1e3">', sbml_str)
172
172
 
173
+ def test_list_of_parameters_const_depend(self):
173
174
  # Test setting a constant parameter that depends on another parameter
174
175
  model = Model()
175
176
  p = model.add_parameter("my_parameter")
@@ -193,6 +194,7 @@ class TestSBMLExport(unittest.TestCase):
193
194
  self.assertIn('<initialAssignment symbol="my_parameter2">', sbml_str)
194
195
  self.assertIn("<times/>\n <ci>my_parameter</ci>\n <cn>2.0</cn>", sbml_str) # noqa: E501
195
196
 
197
+ def test_list_of_parameters_var(self):
196
198
  model = Model()
197
199
  p = model.add_parameter("my_parameter", is_constant=False)
198
200
  p.set_initial_value(myokit.Number(1))
@@ -325,3 +327,22 @@ class TestSBMLExport(unittest.TestCase):
325
327
  sbml_str
326
328
  )
327
329
 
330
+ def test_variable_dependent_variables(self):
331
+ m = myokit.Model()
332
+ c = m.add_component('comp')
333
+ t = c.add_variable('time', rhs=myokit.Number(0))
334
+ t.set_unit(myokit.units.second)
335
+ t.set_binding('time')
336
+ v = c.add_variable('var', initial_value=3)
337
+ v.set_rhs(myokit.Number(1))
338
+ v2 = c.add_variable('var2')
339
+ v2.set_rhs(myokit.Name(v))
340
+
341
+ s = sbml.Model.from_myokit_model(m)
342
+ self.assertEqual(s.parameter('var2').is_rate(), False)
343
+ self.assertEqual(s.parameter('var').is_rate(), True)
344
+
345
+ sbml_str = write_string(s).decode("utf8")
346
+ self.assertIn('<initialAssignment symbol="var">', sbml_str)
347
+ self.assertNotIn('<initialAssignment symbol="var2">', sbml_str)
348
+ self.assertIn('<assignmentRule variable="var2">', sbml_str)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: myokit
3
- Version: 1.37.3
3
+ Version: 1.37.5
4
4
  Summary: A modeling and simulation tool for cardiac cellular electrophysiology
5
5
  Home-page: http://myokit.org
6
6
  Author: Michael Clerx
@@ -25,25 +25,25 @@ Description-Content-Type: text/markdown
25
25
  License-File: LICENSE.txt
26
26
  Requires-Dist: configparser
27
27
  Requires-Dist: lxml
28
- Requires-Dist: matplotlib >=2.2
28
+ Requires-Dist: matplotlib>=2.2
29
29
  Requires-Dist: numpy
30
30
  Requires-Dist: setuptools
31
31
  Provides-Extra: dev
32
- Requires-Dist: coverage ; extra == 'dev'
33
- Requires-Dist: flake8 >=3 ; extra == 'dev'
32
+ Requires-Dist: coverage; extra == "dev"
33
+ Requires-Dist: flake8>=3; extra == "dev"
34
34
  Provides-Extra: docs
35
- Requires-Dist: sphinx >=1.7.4 ; extra == 'docs'
35
+ Requires-Dist: sphinx>=1.7.4; extra == "docs"
36
36
  Provides-Extra: gui
37
- Requires-Dist: pyqt6 ; extra == 'gui'
38
- Requires-Dist: sip ; extra == 'gui'
37
+ Requires-Dist: pyqt6; extra == "gui"
38
+ Requires-Dist: sip; extra == "gui"
39
39
  Provides-Extra: optional
40
- Requires-Dist: scipy ; extra == 'optional'
41
- Requires-Dist: sympy ; extra == 'optional'
40
+ Requires-Dist: scipy; extra == "optional"
41
+ Requires-Dist: sympy; extra == "optional"
42
42
  Provides-Extra: pyqt
43
- Requires-Dist: pyqt6 ; extra == 'pyqt'
44
- Requires-Dist: sip ; extra == 'pyqt'
43
+ Requires-Dist: pyqt6; extra == "pyqt"
44
+ Requires-Dist: sip; extra == "pyqt"
45
45
  Provides-Extra: pyside
46
- Requires-Dist: pyside6 ; extra == 'pyside'
46
+ Requires-Dist: pyside6; extra == "pyside"
47
47
 
48
48
  [![Ubuntu unit tests](https://github.com/myokit/myokit/actions/workflows/unit-tests-ubuntu.yml/badge.svg)](https://github.com/myokit/myokit/actions/workflows/unit-tests-ubuntu.yml)
49
49
  [![MacOS unit tests](https://github.com/myokit/myokit/actions/workflows/unit-tests-macos.yml/badge.svg)](https://github.com/myokit/myokit/actions/workflows/unit-tests-macos.yml)
@@ -123,7 +123,7 @@ A high-level plan for Myokit's future is provided in the [roadmap](./ROADMAP.md)
123
123
  ### Meet the team!
124
124
 
125
125
  Myokit's development is driven by a [team](https://github.com/orgs/myokit/people) at the Universities of Nottingham, Oxford, and Macao, led by Michael Clerx (Nottingham).
126
- It is guided by an external advisory board composed of Jordi Heijman (Maastricht University), Trine Krogh-Madsen (Weill Cornell Medicine), and David Gavaghan (Oxford).
126
+ It is guided by an external advisory group composed of Jordi Heijman (Maastricht University), Trine Krogh-Madsen (Weill Cornell Medicine), and David Gavaghan (Oxford).
127
127
 
128
128
 
129
129
  ## Citing Myokit
@@ -2,18 +2,18 @@ myokit/__init__.py,sha256=6Df0drToZtBEqsRWTDtedcU_OMMn8d0oNxRtJaFAkww,13644
2
2
  myokit/__main__.py,sha256=VXyt8m2GG62pz32H-qATT4tw988p-LlIKih0mkVYlJk,53688
3
3
  myokit/_aux.py,sha256=x8VgBbdKF9yY7bBwi2mbuITyIzN3K7fj-ylCZrJQCJs,23948
4
4
  myokit/_config.py,sha256=WoUMTcHOO9oerqtad7AnQgXAND41YO-WfqlSSyCuHgU,11989
5
- myokit/_datablock.py,sha256=3fWSvnhJAoEUXsauOn8Qc3mC3qNAM2CGvsuBJ2ZCOzA,88717
5
+ myokit/_datablock.py,sha256=kz9oqggFhQ2ovyqTOEeOSgm8GdosPpHgDBKxNXFVPgE,88683
6
6
  myokit/_datalog.py,sha256=eWMpgyLa5Nx98RQZO3fK2PZKp2vLsBCddq6nB-9aVK4,77749
7
7
  myokit/_err.py,sha256=n8Ggy2VKnyx_h_ySuNe77tIFXzh1yO7sWBvc7YPC2cM,11974
8
8
  myokit/_expressions.py,sha256=VpxJAax0OHAWj0AnIH5xkGVWLrmVxBP9tykaccV86BU,109246
9
9
  myokit/_io.py,sha256=2ll5Yb-sbP4tvc3pPmzhNuv_PaRMPJlNRWb1TzXfELg,8948
10
10
  myokit/_model_api.py,sha256=LRHT-RcqyB2UTNrvg7iMXN0D_lpParrks5YKcVZPJJY,194650
11
- myokit/_myokit_version.py,sha256=DpsDatFLxBQC5tRXhL6D6VFWDyhKYOxR0wnzl8CvAT4,726
12
- myokit/_parsing.py,sha256=GTVUwJvqjkQ3rc6BiwMFnzanLivbSUzW796gHCTJrNg,74065
11
+ myokit/_myokit_version.py,sha256=-XbOLRR_wIl6x4Vx_YsS-NvGU4aExjqMDkoe_gdCDoQ,726
12
+ myokit/_parsing.py,sha256=o7O39o50QWCKIPbadhxluv7lDdJbTfnYbV3kzfWOG78,74232
13
13
  myokit/_progress.py,sha256=Mi2QU6Vqj4qudhXc76amXM31iID2Mo8Gljw5OH2COZ4,4410
14
14
  myokit/_protocol.py,sha256=_N16LGHppIv4yCSPjy0q2f-Oed0yG1wMk0ipTtXnMck,30756
15
15
  myokit/_system.py,sha256=IX3Q7_7f9X9aYsxSL2CXJc9bTcFJsJbfq_e2w-OkhOI,4602
16
- myokit/_unit.py,sha256=prEupTNDeoqf2suN_i9ffBDGfVkid49gUDBTWTVzTww,30102
16
+ myokit/_unit.py,sha256=scKgDxN8bBab1jlYYMt3MkkJLC_DUUl5BbocjWYsop8,30102
17
17
  myokit/float.py,sha256=7-BqdABN5Vm4HIgRMHSgcZ4O4Vd085-5hMB6z8zJZtg,3066
18
18
  myokit/pacing.py,sha256=_My0GwIPJh4DuUiK_kYDNBcacUaOKsffjbK5LvgK6z8,4552
19
19
  myokit/pype.py,sha256=JTKuoiwxGui6iqBXK4gxNlCdtH_IGY0FiZLtmaJ580c,8230
@@ -79,7 +79,7 @@ myokit/_bin/sundials-win-vs/lib/sundials_cvodes.lib,sha256=FEmPq4BcmyOZReaM68pMD
79
79
  myokit/_bin/sundials-win-vs/lib/sundials_nvecserial.dll,sha256=QwCosLqDBV0YIYvxfTGm0h5rM6iDNNXt5apu3hMZDiY,160768
80
80
  myokit/_bin/sundials-win-vs/lib/sundials_nvecserial.lib,sha256=UmH8HCEFT_A8hsIw9Mj-ZgXeqNJJNadHnvQj9RJsfQg,59576
81
81
  myokit/_sim/__init__.py,sha256=CuO1se4k-kpyh0muzBjdTrVfm31ZjXRLAZYhBg8P0I4,13488
82
- myokit/_sim/cable.c,sha256=57FecqBgVnYTnqcH0F5ocA8O4zO2iwpkHBYBV7qW5Qg,19156
82
+ myokit/_sim/cable.c,sha256=mOWdKhx0tNwPVNItJd3-IzG8W_-fizaREqlrEZrrzK8,19186
83
83
  myokit/_sim/cable.py,sha256=GF1LJhoR9iSjVi16n7PhKYvSn1bDJVzSws1i8nJwM4c,18908
84
84
  myokit/_sim/cmodel.h,sha256=PlXRNDWV5JDsl28G-k3UlzwFCF5_crkAeWpRgPO8a0s,38795
85
85
  myokit/_sim/cmodel.py,sha256=9amSihccWepI1SGI98b5tvvSq2cmAMLX-ecgHoXnc0Y,15775
@@ -88,7 +88,7 @@ myokit/_sim/compiler.py,sha256=80Qwk7_LOjbu0jt9_aeMLQMEvYj3H4xIoE525zUUEK8,2664
88
88
  myokit/_sim/cvodessim.c,sha256=iWXHVSNTN2VqGGu24N8QFRWwpUUxDo17fTIP0Fv0Ikg,78062
89
89
  myokit/_sim/cvodessim.py,sha256=T2vwuSJFtHWTdzJv7xhFRC9l2OJY-7r4L6VVEpnIZI8,46158
90
90
  myokit/_sim/differential.hpp,sha256=LiaFNQks_4RkWeNEnUc48XjbdYyGSsxfWo35leygM68,15446
91
- myokit/_sim/fiber_tissue.c,sha256=mKtNy3Qs2Tg917hgCryaftwJGHqFn7WZAOuJWG3VVpY,47444
91
+ myokit/_sim/fiber_tissue.c,sha256=P9AJZlq9C3DNFxpzvseBbqozxMg6oMiolitWr-cQtv4,47474
92
92
  myokit/_sim/fiber_tissue.py,sha256=A3zyGPGHNjFdGgbIzCiKYN8o4L2nq7W_ZjO996jF9Kc,50780
93
93
  myokit/_sim/jacobian.cpp,sha256=mvuxHnxiyg4GW-ik1qOgOGbaj-sb-DbtidsRDXfRIjc,8012
94
94
  myokit/_sim/jacobian.py,sha256=-PWjGRqr8VygrqnITtju8aWDkPvt9srATcrenOShR1s,12927
@@ -111,7 +111,7 @@ myokit/formats/ansic/template/cable.c,sha256=e7V4CVeoIN0N8MSVenPmtst31HkiZb5Jfx1
111
111
  myokit/formats/ansic/template/euler.c,sha256=_ENsjDV-J0P7JEIeZBRO3L_kjyWZWiORyH5uFJXmB0Q,6044
112
112
  myokit/formats/ansic/template/sim.c,sha256=wf8aFpzyoEopZIG6vITfSkFdPPGkRX53hL8Lujjo5qs,14244
113
113
  myokit/formats/axon/__init__.py,sha256=qgo7UvLOYP_IZDYijkJhdGtwLc40Q80UpksivXaVojg,538
114
- myokit/formats/axon/_abf.py,sha256=MxaW18eJQpJ8ECHHPTAnRq1iGJ1vODTG6y-PYpfFSKw,76263
114
+ myokit/formats/axon/_abf.py,sha256=7SZPVeS43W8i-p6gGFX45_Zh3eIkYcQr5ceP24mBSUY,77759
115
115
  myokit/formats/axon/_atf.py,sha256=B0DQ_SiYv_izygBFCe8QXewsmxjPpq-7M2rqXcSFND4,10543
116
116
  myokit/formats/axon/_importer.py,sha256=WaPkQ7CCFOIQnEkOiXXd1ICHPiXPicv9YFyJB5yLt2k,826
117
117
  myokit/formats/cellml/__init__.py,sha256=Iz5kYXkjx0_mypcR0h5mV0sfVkVwaUWzUH8z4Goetq8,1466
@@ -142,7 +142,7 @@ myokit/formats/easyml/_ewriter.py,sha256=5T1GoiMtotlKMY3ZaYKcoWUeUZtWIbh3Xii8x0v
142
142
  myokit/formats/easyml/_exporter.py,sha256=lGUJBO3ZAJfVcbTBQUEkGqUqdmtMXDzfGGtVda54Bx0,15771
143
143
  myokit/formats/heka/__init__.py,sha256=JsHFKPdtzlYDgtCwA4ENODMAhOen9ygl-RwtrPXz-wU,927
144
144
  myokit/formats/heka/_importer.py,sha256=4HaxswB8Uvngahsh34eEXaQnE9SYakpugFo3jc6VNrA,1154
145
- myokit/formats/heka/_patchmaster.py,sha256=PjUrN6FRbKBeHOplcyR4-uQ6xJQEUDj7iwUb6p7O0sA,116522
145
+ myokit/formats/heka/_patchmaster.py,sha256=QxNSxT2pFpjqRfzHXFerUexDVhQTJJ-7dX0YwAEFVxk,118662
146
146
  myokit/formats/html/__init__.py,sha256=pFJtVdw_SIaZZO0dfYUH9XyqPO46CuxSTL4iwvcC-4U,457
147
147
  myokit/formats/html/_exporter.py,sha256=kWTB_6peG1MoxWPnGjm35Axkmw1tONSX1dCwWt0_arE,2343
148
148
  myokit/formats/html/_flatten.py,sha256=kosDcmr6KKrZbaIsXewGNiIAlpVq4N9POdPrKygcvQs,6606
@@ -173,7 +173,7 @@ myokit/formats/python/_ewriter.py,sha256=bdW3YrZLO7a_A_aqfnPQHAnyjU7EQBCG490oUF3
173
173
  myokit/formats/python/_exporter.py,sha256=_qgRV1mFDZxhjhWG5rUsCkhaoGh-mxiZPwYA_6RHNRA,969
174
174
  myokit/formats/python/template/sim.py,sha256=HHQMZUFKC0711AFgaA8mgYc75O576PMIsujjm00MZcM,10311
175
175
  myokit/formats/sbml/__init__.py,sha256=6JeedBTc4Ct-bc7Tdu_P_adKalmW3AQUj91dM3Pzwnw,944
176
- myokit/formats/sbml/_api.py,sha256=JCuRzBwdRgBu5Ho8ueAqc4SeLKn3S8tHTqWVm7YVylA,64766
176
+ myokit/formats/sbml/_api.py,sha256=s4jPk6_TyLP4Y7I5UcpTnRpoArcDoqXpH0iVr3dEGII,64828
177
177
  myokit/formats/sbml/_exporter.py,sha256=_cjogI63t0j_Pt6LrKvYy70_oTvvMXCHtc8j-7WZzhg,1430
178
178
  myokit/formats/sbml/_importer.py,sha256=CiejktBXOsVmq6FuP4ayBztUJ6d4peSqjxGltTBvn38,987
179
179
  myokit/formats/sbml/_parser.py,sha256=ZEchxxsd7IdS-PDnIDppULiUmCUlvPDYBM98cXkLTzs,29181
@@ -193,7 +193,7 @@ myokit/formats/xml/_exporter.py,sha256=PxoqHcc9abRF5TXWTOpZ96krrNdcRBLNWEiOvExr8
193
193
  myokit/formats/xml/_split.py,sha256=9q_ff11kqfFt9XNvJZHnNb_UcOymMxrpJrLLwJypKt4,393
194
194
  myokit/gui/__init__.py,sha256=CXXlxDpavx2xHOD30Q-EhVkCsR1vI1_8DPMe7PvZUoE,6426
195
195
  myokit/gui/datablock_viewer.py,sha256=DGVUl5qMJsPWdS7jA0Aw9d_lfU1x0HnoO1SDkb62LDo,49529
196
- myokit/gui/datalog_viewer.py,sha256=D-L5dc_3PK1-KhwooGWq9q1Ug8iYz0YCfonUj0HCu5s,33503
196
+ myokit/gui/datalog_viewer.py,sha256=GYv-s5dmxeFAX48b0HekUxvWDeCvRDXqNGEZdyyGM7Q,35558
197
197
  myokit/gui/explorer.py,sha256=WCAU9g7lPKxddIgp9Sr4nTfQ2wTzN9faVvIxg1D1PVA,8676
198
198
  myokit/gui/ide.py,sha256=T-Acv6bPt0TVADxhGeBjiASzISwjjpUabZ_Z-p3VKZ4,108238
199
199
  myokit/gui/progress.py,sha256=kzm7QsnoNySi9f2oosUcvIOGq9eGi0Pt4QfMl4EViPA,2157
@@ -227,7 +227,7 @@ myokit/tests/test_expressions.py,sha256=o7dQV95Gx4diX2vdJbVQ-bEN5ROa14SUAb4EFSGK
227
227
  myokit/tests/test_float.py,sha256=s4GpsSHDSmmstPTpXCFCLE-44lvZqM71OtVTooZBDSA,4882
228
228
  myokit/tests/test_formats.py,sha256=UfxN8d2SkEip9SLZ4aBIE95WnY1LaGvnxd2xh8RWIA4,8085
229
229
  myokit/tests/test_formats_ansic.py,sha256=3qnqOU-xvIPp-NSBno_IRVVgwBC8p59LprxYMFusD3E,12712
230
- myokit/tests/test_formats_axon.py,sha256=PMn7waANp-k7vXKgEOgwyGfgn89aUFy56gg0RmBipH0,33181
230
+ myokit/tests/test_formats_axon.py,sha256=vbc0_G4b7etzazyz0uFMSX9SveIExR8WXY41MdNGJ3k,33395
231
231
  myokit/tests/test_formats_cellml.py,sha256=WeNecMsTWubIBq6C0QMU4-cR-ZpNk69dJ7aHldb22zE,19078
232
232
  myokit/tests/test_formats_channelml.py,sha256=BS90euUpWN1DgkShTYK7fdwuP8qIt3CzUAXoiY5YZYc,6703
233
233
  myokit/tests/test_formats_cpp.py,sha256=giVwKzbHmjqx6VZHQBI1OV4oZrBnaKmquBmWlt9igx0,3537
@@ -271,7 +271,7 @@ myokit/tests/test_pype.py,sha256=vf3mympasLZwgTUQTSoe8e98Hp31cXo71APjlZEANKE,240
271
271
  myokit/tests/test_quantity.py,sha256=etmbqlqBNt2OZFku_P-s5n8RlA-gxVyRAWjOPq9xD7s,7373
272
272
  myokit/tests/test_rhs_benchmarker.py,sha256=Gb3LaAiu8SW__BBM3l0TbMzxML1PpEqm3g8TCteyCZg,3452
273
273
  myokit/tests/test_sbml_api.py,sha256=9SIwsAT5lyGYnVZvPjW-EOd6mdIbrGMFpB5e981vAxM,74820
274
- myokit/tests/test_sbml_export.py,sha256=qdhSZ_zht_yOLJ3OfY2yt1-5uwj48nWn2Xtn5nSH8mo,13351
274
+ myokit/tests/test_sbml_export.py,sha256=35KypObiBajpTaONhkv3HaT0JxN1r_i9p2bNBwB1N_I,14271
275
275
  myokit/tests/test_sbml_parser.py,sha256=ipiFs8VHcXq1-yC_9XUuonMQPLhlrYdkqvQ05BdtYiw,54542
276
276
  myokit/tests/test_simulation_1d.py,sha256=YIjqVoyOgxrvAabpSOfLfUAOBR6dIKJoqac92VjL4ww,19281
277
277
  myokit/tests/test_simulation_cvodes.py,sha256=wqjp8D8ksINWmC1tyYeSpAaAzGqrpXVOYvqip1FObSI,48408
@@ -401,9 +401,9 @@ myokit/tests/data/multi/beeler-no-name.mmt,sha256=tBeWcTVag1gAQAvxWlUqH6GQhF1D4D
401
401
  myokit/tests/data/multi/lr-1991.mmt,sha256=9jzHRAy1nLiBOPioiEpXmsRvIqwEezRY_ve2eHMDbTQ,6013
402
402
  myokit/tests/data/multi/not-a-model.csv,sha256=7ek3pQXr2fpjNjFTs-B-T_kEehx8IQ-evdcg24dtMEs,109
403
403
  myokit/tests/data/multi/subdir/beeler-no-name.mmt,sha256=tBeWcTVag1gAQAvxWlUqH6GQhF1D4DMnlmuePQn1vBY,3008
404
- myokit-1.37.3.dist-info/LICENSE.txt,sha256=0WNshB_he7HnJ4ROvVAaX95yz9760DE9ps8PbyvrGvw,1833
405
- myokit-1.37.3.dist-info/METADATA,sha256=NocrKBZA4t1VCvp0KCNuoSQfAoRzngbbE4XEvtPlkXw,6273
406
- myokit-1.37.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
407
- myokit-1.37.3.dist-info/entry_points.txt,sha256=yP9wy3w0YAAYUD5PYGliYKhnKvEp5l6p4S_XvXsqreM,48
408
- myokit-1.37.3.dist-info/top_level.txt,sha256=vopnhGEticqud7tKy6L6dvi_n_AMXoiYBEiboRTnsWY,7
409
- myokit-1.37.3.dist-info/RECORD,,
404
+ myokit-1.37.5.dist-info/LICENSE.txt,sha256=0WNshB_he7HnJ4ROvVAaX95yz9760DE9ps8PbyvrGvw,1833
405
+ myokit-1.37.5.dist-info/METADATA,sha256=dZnApEbm1uvpK324S7j7bY4ZrPOJQ4KjPxD-Ovfl03s,6260
406
+ myokit-1.37.5.dist-info/WHEEL,sha256=pL8R0wFFS65tNSRnaOVrsw9EOkOqxLrlUPenUYnJKNo,91
407
+ myokit-1.37.5.dist-info/entry_points.txt,sha256=yP9wy3w0YAAYUD5PYGliYKhnKvEp5l6p4S_XvXsqreM,48
408
+ myokit-1.37.5.dist-info/top_level.txt,sha256=vopnhGEticqud7tKy6L6dvi_n_AMXoiYBEiboRTnsWY,7
409
+ myokit-1.37.5.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (74.1.3)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5