myokit 1.36.1__py3-none-any.whl → 1.37.0__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.
Files changed (43) hide show
  1. myokit/__init__.py +6 -19
  2. myokit/_datablock.py +45 -55
  3. myokit/_datalog.py +2 -2
  4. myokit/_err.py +26 -3
  5. myokit/_expressions.py +241 -127
  6. myokit/_model_api.py +19 -13
  7. myokit/_myokit_version.py +1 -1
  8. myokit/_sim/jacobian.py +3 -3
  9. myokit/_sim/openclsim.py +5 -5
  10. myokit/_sim/rhs.py +1 -1
  11. myokit/formats/__init__.py +4 -9
  12. myokit/formats/ansic/_ewriter.py +4 -20
  13. myokit/formats/heka/_patchmaster.py +16 -10
  14. myokit/formats/opencl/_ewriter.py +3 -42
  15. myokit/formats/opencl/template/minilog.py +1 -1
  16. myokit/formats/sympy/_ereader.py +2 -1
  17. myokit/formats/wcp/_wcp.py +3 -3
  18. myokit/gui/datalog_viewer.py +12 -7
  19. myokit/lib/markov.py +2 -2
  20. myokit/lib/plots.py +4 -4
  21. myokit/tests/data/formats/wcp-file-empty.wcp +0 -0
  22. myokit/tests/test_datablock.py +10 -10
  23. myokit/tests/test_datalog.py +4 -1
  24. myokit/tests/test_expressions.py +532 -251
  25. myokit/tests/test_formats_ansic.py +6 -18
  26. myokit/tests/test_formats_cpp.py +0 -5
  27. myokit/tests/test_formats_cuda.py +7 -15
  28. myokit/tests/test_formats_easyml.py +4 -9
  29. myokit/tests/test_formats_latex.py +10 -11
  30. myokit/tests/test_formats_matlab.py +0 -8
  31. myokit/tests/test_formats_opencl.py +0 -29
  32. myokit/tests/test_formats_python.py +2 -19
  33. myokit/tests/test_formats_stan.py +0 -13
  34. myokit/tests/test_formats_sympy.py +3 -3
  35. myokit/tests/test_formats_wcp.py +15 -0
  36. myokit/tests/test_model.py +20 -20
  37. myokit/tests/test_parsing.py +19 -0
  38. {myokit-1.36.1.dist-info → myokit-1.37.0.dist-info}/METADATA +1 -1
  39. {myokit-1.36.1.dist-info → myokit-1.37.0.dist-info}/RECORD +43 -42
  40. {myokit-1.36.1.dist-info → myokit-1.37.0.dist-info}/LICENSE.txt +0 -0
  41. {myokit-1.36.1.dist-info → myokit-1.37.0.dist-info}/WHEEL +0 -0
  42. {myokit-1.36.1.dist-info → myokit-1.37.0.dist-info}/entry_points.txt +0 -0
  43. {myokit-1.36.1.dist-info → myokit-1.37.0.dist-info}/top_level.txt +0 -0
myokit/_model_api.py CHANGED
@@ -716,7 +716,10 @@ class VarOwner(ModelPart, VarProvider):
716
716
  If ``recursive`` is ``True``, any child variables will be deleted as
717
717
  well.
718
718
 
719
- A :class:`myokit.IntegrityError` will be raised if
719
+ A :class:`myokit.IntegrityError` will be raised if the variable cannot
720
+ be removed because other variables depend on it. (Although dependencies
721
+ from child variables will be ignored if ``recursive`` is set to
722
+ ``True``).
720
723
  """
721
724
  if variable.parent() != self:
722
725
  raise ValueError(
@@ -1095,9 +1098,7 @@ class Model(ObjectWithMetaData, VarProvider):
1095
1098
  raise myokit.IncompatibleUnitError(msg, var._token)
1096
1099
 
1097
1100
  def clone(self):
1098
- """
1099
- Returns a (deep) clone of this model.
1100
- """
1101
+ """ Returns a (deep) clone of this model. """
1101
1102
  clone = Model()
1102
1103
 
1103
1104
  # Copy meta data
@@ -4520,16 +4521,10 @@ class Variable(VarOwner):
4520
4521
  warnings.warn('The keyword argument `state_value` is deprecated.'
4521
4522
  ' Please use `initial_value` instead.')
4522
4523
 
4523
- # Handle string and number rhs's
4524
- model = self.model()
4525
- if not isinstance(initial_value, myokit.Expression):
4526
- if isinstance(initial_value, str):
4527
- # Expressions are evaluated in model context
4528
- initial_value = myokit.parse_expression(
4529
- initial_value, context=model)
4530
- elif initial_value is not None:
4531
- initial_value = myokit.Number(initial_value)
4524
+ # Parse initial value
4525
+ initial_value = self._set_initial_value(initial_value, False)
4532
4526
 
4527
+ model = self.model()
4533
4528
  try:
4534
4529
  # Set lhs to derivative expression
4535
4530
  self._lhs = myokit.Derivative(myokit.Name(self))
@@ -4854,6 +4849,9 @@ class Variable(VarOwner):
4854
4849
  x.set_rhs(myokit.Plus(myokit.Number(1), myokit.Name(y)))
4855
4850
  x.set_rhs('1 + y')
4856
4851
 
4852
+ Expressions used as a variable's right-hand side must be numerical:
4853
+ :class:`myokit.Condition` operators can not be used as RHS.
4854
+
4857
4855
  Calling `set_rhs` will reset the validation status of the model this
4858
4856
  variable belongs to.
4859
4857
  """
@@ -5107,6 +5105,14 @@ class Equation:
5107
5105
  def __init__(self, lhs, rhs):
5108
5106
  self._lhs = lhs
5109
5107
  self._rhs = rhs
5108
+ if not isinstance(lhs, myokit.Expression):
5109
+ raise myokit.IntegrityError(
5110
+ 'Both sides of an equation must be myokit.Expression objects.'
5111
+ f' Found {type(lhs)} for LHS.')
5112
+ if not isinstance(rhs, myokit.Expression):
5113
+ raise myokit.IntegrityError(
5114
+ 'Both sides of an equation must be myokit.Expression objects.'
5115
+ f' Found {type(lhs)} for RHS.')
5110
5116
 
5111
5117
  def __eq__(self, other):
5112
5118
  if not isinstance(other, Equation):
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, 36, 1
17
+ __version_tuple__ = 1, 37, 0
18
18
 
19
19
  # String version of the version number
20
20
  __version__ = '.'.join([str(x) for x in __version_tuple__])
myokit/_sim/jacobian.py CHANGED
@@ -166,7 +166,7 @@ class JacobianTracer(myokit.CppModule):
166
166
  self._ext.calculate(state, bound, deriv, partial)
167
167
  # Discard derivatives
168
168
  # Convert partial derivatives to numpy array and store
169
- partial = np.array(partial, copy=False)
169
+ partial = np.asarray(partial)
170
170
  partial = partial.reshape((ns, ns))
171
171
  partials.append(partial)
172
172
  partials = np.array(partials)
@@ -282,8 +282,8 @@ class JacobianCalculator(myokit.CppModule):
282
282
  self._ext.calculate(state, inputs, deriv, partial)
283
283
 
284
284
  # Create numpy versions and return
285
- deriv = np.array(deriv, copy=False)
286
- partial = np.array(partial, copy=False).reshape((n, n))
285
+ deriv = np.asarray(deriv)
286
+ partial = np.asarray(partial).reshape((n, n))
287
287
  return deriv, partial
288
288
 
289
289
  def newton_root(self, x=None, accuracy=0, max_iter=50, damping=1):
myokit/_sim/openclsim.py CHANGED
@@ -534,7 +534,7 @@ class SimulationOpenCL(myokit.CModule):
534
534
  lower, upper = safe_range
535
535
  for dims in myokit._dimco(*self._dims):
536
536
  key = '.'.join([str(x) for x in dims]) + post
537
- ar = np.array(_log[key], copy=False)
537
+ ar = np.asarray(_log[key])
538
538
  i = np.where(
539
539
  (ar < lower)
540
540
  | (ar > upper)
@@ -1080,7 +1080,7 @@ class SimulationOpenCL(myokit.CModule):
1080
1080
  n = len(self._fields) * self._nx * self._ny
1081
1081
  if n:
1082
1082
  field_data = self._fields.values()
1083
- field_data = [np.array(x, copy=False) for x in field_data]
1083
+ field_data = [np.asarray(x) for x in field_data]
1084
1084
  field_data = np.vstack(field_data)
1085
1085
  field_data = list(field_data.reshape(n, order='F'))
1086
1086
  else:
@@ -1342,7 +1342,7 @@ class SimulationOpenCL(myokit.CModule):
1342
1342
  'This method is unavailable when diffusion is disabled.')
1343
1343
 
1344
1344
  # Check the field's size
1345
- gx = np.array(gx, copy=False, dtype=float)
1345
+ gx = np.asarray(gx, dtype=float)
1346
1346
  if len(self._dims) == 1:
1347
1347
  s = self._nx - 1
1348
1348
  if gx.shape != (s, ):
@@ -1360,7 +1360,7 @@ class SimulationOpenCL(myokit.CModule):
1360
1360
  if gy is None:
1361
1361
  raise ValueError(
1362
1362
  'The argument `gy` must be set for 2-d simulations.')
1363
- gy = np.array(gy, copy=False, dtype=float)
1363
+ gy = np.asarray(gy, dtype=float)
1364
1364
  s = (self._ny - 1, self._nx)
1365
1365
  if gy.shape != s:
1366
1366
  raise ValueError(
@@ -1514,7 +1514,7 @@ class SimulationOpenCL(myokit.CModule):
1514
1514
  if not var.is_constant():
1515
1515
  raise ValueError('Only constants can be used for fields.')
1516
1516
  # Check values
1517
- values = np.array(values, copy=False, dtype=float)
1517
+ values = np.asarray(values, dtype=float)
1518
1518
  if len(self._dims) == 1:
1519
1519
  if values.shape != (self._nx, ):
1520
1520
  raise ValueError(
myokit/_sim/rhs.py CHANGED
@@ -153,7 +153,7 @@ class RhsBenchmarker(myokit.CModule):
153
153
  the given benchmarked times.
154
154
  """
155
155
  import numpy as np
156
- times = np.array(times, copy=False)
156
+ times = np.asarray(times)
157
157
  # Remove outliers twice
158
158
  for i in range(0, 2):
159
159
  avg = np.mean(times)
@@ -128,15 +128,7 @@ class ExpressionWriter:
128
128
  ``a**b**c`` is interpreted as ``a**(b**c)``, necessitating a different
129
129
  bracket-adding logic than used in Myokit.
130
130
 
131
- 3. Binary operators are sometimes implemented as n-ary operators. In
132
- Myokit, ``0 == 0 == 0`` is a sequence of two binary operators,
133
- interpreted as ``(0 == 0) == 0``. Because ``(0 == 0)`` evaluates to
134
- ``1``, this expression returns ``0`` (1 does not equal 0). In Python,
135
- the expression ``0 == 0 == 0`` is a ternary (n-ary) operator,
136
- interpreted as ``all_equal(0, 0, 0)``, which evaluates to ``1``. For
137
- languages that use this convention, extra brackets must be added.
138
-
139
- 4. Myokit does not have increment or decrement operators ``--`` and ``++``,
131
+ 3. Myokit does not have increment or decrement operators ``--`` and ``++``,
140
132
  so the expression ``--x`` is interpreted as ``-(-x)``. This is the same
141
133
  in Python. But in C-based languages, this is interpreted as a decrement
142
134
  operator so care must be taken to add extra brackets.
@@ -886,6 +878,9 @@ class SweepSource:
886
878
 
887
879
  Note that a source with zero recorded channels may still report a
888
880
  non-zero number of sweeps if it can provide D/A outputs.
881
+
882
+ Similarly, formats like WCP can report zero sweeps but have a non-zero
883
+ channel count (if no data was recorded).
889
884
  """
890
885
  raise NotImplementedError
891
886
 
@@ -102,23 +102,14 @@ class CBasedExpressionWriter(PythonExpressionWriter):
102
102
 
103
103
  def _ex_not(self, e):
104
104
  # C conditions all have brackets, so don't add more
105
- if isinstance(e[0], (myokit.Condition)):
106
- return f'(!{self.ex(e[0])})'
107
- # But do add more if the user's being silly
108
- return f'(!({self.ex(e[0])}))'
105
+ return f'(!{self.ex(e[0])})'
109
106
 
110
107
  def _ex_if(self, e):
111
- _if, _then, _else = self.ex(e._i), self.ex(e._t), self.ex(e._e)
112
- # If i is not a condtion (which always gets brackets from this writer)
113
- # then add brackets
114
- if not isinstance(e._i, myokit.Condition):
115
- _if = f'({_if})'
116
- return f'({_if} ? {_then} : {_else})'
108
+ return f'({self.ex(e._i)} ? {self.ex(e._t)} : {self.ex(e._e)})'
117
109
 
118
110
  def _ex_piecewise(self, e):
119
111
  # Render ifs; add extra bracket if not a condition (see _ex_if)
120
- _ifs = [self.ex(x) if isinstance(x, myokit.Condition)
121
- else f'({self.ex(x)})' for x in e._i]
112
+ _ifs = [self.ex(x) for x in e._i]
122
113
  _thens = [self.ex(x) for x in e._e]
123
114
 
124
115
  s = []
@@ -200,13 +191,7 @@ class AnsiCExpressionWriter(CBasedExpressionWriter):
200
191
  #def _ex_not(self, e):
201
192
 
202
193
  def _ex_if(self, e):
203
- # Allow _fcond
204
-
205
194
  _if, _then, _else = self.ex(e._i), self.ex(e._t), self.ex(e._e)
206
- # If i is not a condtion (which always gets brackets from this writer)
207
- # then add brackets
208
- if not isinstance(e._i, myokit.Condition):
209
- _if = f'({_if})'
210
195
 
211
196
  # Use if-then-else function?
212
197
  if self._fcond is not None:
@@ -219,8 +204,7 @@ class AnsiCExpressionWriter(CBasedExpressionWriter):
219
204
  # Allow _fcond
220
205
 
221
206
  # Render ifs; add extra bracket if not a condition (see _ex_if)
222
- _ifs = [self.ex(x) if isinstance(x, myokit.Condition)
223
- else f'({self.ex(x)})' for x in e._i]
207
+ _ifs = [self.ex(x) for x in e._i]
224
208
  _thens = [self.ex(x) for x in e._e]
225
209
 
226
210
  s = []
@@ -928,6 +928,7 @@ class Series(TreeNode, myokit.formats.SweepSource):
928
928
  include_da = False
929
929
 
930
930
  # Populate log
931
+ log.set_time_key('time')
931
932
  if join_sweeps:
932
933
  # Join sweeps
933
934
  offsets = (self._sweep_starts_r if use_real_start_times
@@ -964,31 +965,34 @@ class Series(TreeNode, myokit.formats.SweepSource):
964
965
  log.cmeta[name]['unit'] = self._da_units[0]
965
966
 
966
967
  # Add meta data
967
- log.set_time_key('time')
968
+ log.meta['time'] = self._time.strftime(myokit.DATE_FORMAT)
968
969
  a = self.amplifier_state()
970
+ t = self[0][0] if len(self) and len(self[0]) else None
969
971
  log.meta['current_gain_mV_per_pA'] = a.current_gain()
972
+ log.meta['filter1'] = a.filter1()
973
+ if t is not None:
974
+ log.meta['r_pipette_MOhm'] = t.r_pipette()
975
+ log.meta['r_seal_MOhm'] = t.r_seal()
970
976
  log.meta['ljp_correction_mV'] = a.ljp()
971
- log.meta['c_slow_compensation_pF'] = a.c_slow()
977
+ log.meta['voltage_offset_mV'] = a.v_off()
972
978
  if a.c_fast_enabled():
973
979
  log.meta['c_fast_compensation_enabled'] = 'true'
974
980
  log.meta['c_fast_pF'] = a.c_fast()
975
- log.meta['c_fast_tau_micro_s'] = a.c_fast_tau()
981
+ log.meta['c_fast_tau_us'] = a.c_fast_tau()
976
982
  else:
977
983
  log.meta['c_fast_compensation_enabled'] = 'false'
984
+ log.meta['c_slow_pF'] = a.c_slow()
978
985
  log.meta['r_series_MOhm'] = a.r_series()
979
986
  if a.r_series_enabled():
980
987
  log.meta['r_series_compensation_enabled'] = 'true'
981
988
  log.meta['r_series_compensation_percent'] = round(
982
989
  a.r_series_fraction() * 100, 1)
990
+ log.meta['r_series_compensation_tau_us'] = a.r_series_tau()
991
+ if t is not None:
992
+ log.meta['r_series_post_compensation_MOhm'] = \
993
+ t.r_series_remaining()
983
994
  else:
984
995
  log.meta['r_series_compensation_enabled'] = 'false'
985
- if len(self) and len(self[0]):
986
- t = self[0][0]
987
- log.meta['r_pipette_MOhm'] = t.r_pipette()
988
- log.meta['r_seal_MOhm'] = t.r_series()
989
- log.meta['r_series_post_compensation_MOhm'] = \
990
- t.r_series_remaining()
991
- log.meta['c_slow_pF'] = t.c_slow()
992
996
 
993
997
  return log
994
998
 
@@ -1035,6 +1039,8 @@ class Series(TreeNode, myokit.formats.SweepSource):
1035
1039
  if a.r_series_enabled():
1036
1040
  p = round(a.r_series_fraction() * 100, 1)
1037
1041
  out.append(f' R series compensation: {p} %.')
1042
+ p = round(a.r_series_tau(), 1)
1043
+ out.append(f' R series compensation tau: {p} us')
1038
1044
  else:
1039
1045
  out.append(' R series compensation: not enabled')
1040
1046
  if len(self) and len(self[0]):
@@ -31,27 +31,6 @@ class OpenCLExpressionWriter(CBasedExpressionWriter):
31
31
  self._sp = (precision == myokit.SINGLE_PRECISION)
32
32
  self._nm = bool(native_math)
33
33
 
34
- def _exc(self, e):
35
- """Returns ``ex(e)`` if ``e`` is a Condition, else ``ex(e != 0)``."""
36
- # Can be removed after https://github.com/myokit/myokit/issues/1056
37
- if isinstance(e, myokit.Condition):
38
- return self.ex(e)
39
- return self.ex(myokit.NotEqual(e, myokit.Number(0)))
40
-
41
- def _ex_infix_comparison(self, e, op):
42
- """Handles ex() for infix condition operators (==, !=, > etc.)."""
43
- # Can be removed after https://github.com/myokit/myokit/issues/1056
44
- c1 = isinstance(e[0], myokit.Condition)
45
- c2 = isinstance(e[1], myokit.Condition)
46
- if (c1 and c2) or not (c1 or c2):
47
- return f'({self.ex(e[0])} {op} {self.ex(e[1])})'
48
- else:
49
- return f'({self._exc(e[0])} {op} {self._exc(e[1])})'
50
-
51
- def _ex_infix_logical(self, e, op):
52
- # Can be removed after https://github.com/myokit/myokit/issues/1056
53
- return f'({self._exc(e[0])} {op} {self._exc(e[1])})'
54
-
55
34
  #def _ex_name(self, e):
56
35
  #def _ex_derivative(self, e):
57
36
  #def _ex_initial_value(self, e):
@@ -126,25 +105,7 @@ class OpenCLExpressionWriter(CBasedExpressionWriter):
126
105
 
127
106
  #def _ex_and(self, e):
128
107
  #def _ex_or(self, e):
129
-
130
- def _ex_not(self, e):
131
- # Can be removed after https://github.com/myokit/myokit/issues/1056
132
- return f'(!{self._exc(e[0])})'
133
-
134
- def _ex_if(self, e):
135
- # Can be removed after https://github.com/myokit/myokit/issues/1056
136
- _if, _then, _else = self._exc(e._i), self.ex(e._t), self.ex(e._e)
137
- return f'({_if} ? {_then} : {_else})'
138
-
139
- def _ex_piecewise(self, e):
140
- # Can be removed after https://github.com/myokit/myokit/issues/1056
141
- _ifs = [self._exc(x) for x in e._i]
142
- _thens = [self.ex(x) for x in e._e]
143
- s = []
144
- n = len(_ifs)
145
- for _if, _then in zip(_ifs, _thens):
146
- s.append(f'({_if} ? {_then} : ')
147
- s.append(_thens[-1])
148
- s.append(')' * len(_ifs))
149
- return ''.join(s)
108
+ #def _ex_not(self, e):
109
+ #def _ex_if(self, e):
110
+ #def _ex_piecewise(self, e):
150
111
 
@@ -177,7 +177,7 @@ class DataLog(OrderedDict):
177
177
  import numpy as np
178
178
  out = DataLog()
179
179
  for k, d in self.items():
180
- out[k] = np.array(d, copy=False)
180
+ out[k] = np.asarray(d)
181
181
  return out
182
182
 
183
183
  def save_csv(self, filename, pad=None):
@@ -156,7 +156,8 @@ class SymPyExpressionReader:
156
156
  def _ex_abs(self, e):
157
157
  return myokit.Abs(self.ex(e.args[0]))
158
158
 
159
- def _ex_not(self, e):
159
+ def _ex_not(self, e): # pragma: no cover
160
+ # Sympy turns not(a == b) into a != b etc., so can't test!
160
161
  return myokit.Not(self.ex(e.args[0]))
161
162
 
162
163
  def _ex_equal(self, e):
@@ -241,7 +241,7 @@ class WcpFile(myokit.formats.SweepSource):
241
241
 
242
242
  def _channel_id(self, channel_id):
243
243
  """ Checks an int or str channel id and returns a valid int. """
244
- if self._nr == 0: # pragma: no cover
244
+ if self._nr == 0:
245
245
  raise KeyError(f'Channel {channel_id} not found (empty file).')
246
246
 
247
247
  # Handle string
@@ -314,7 +314,7 @@ class WcpFile(myokit.formats.SweepSource):
314
314
 
315
315
  # Create log
316
316
  log = myokit.DataLog()
317
- if self._nr == 0: # pragma: no cover
317
+ if self._nr == 0:
318
318
  return log
319
319
 
320
320
  # Get channel names
@@ -433,7 +433,7 @@ class WcpFile(myokit.formats.SweepSource):
433
433
  return self._nr
434
434
 
435
435
  def records(self):
436
- """ Deprecated alias of :meth:`sweep_count`. """
436
+ """ Deprecated alias of :meth:`record_count`. """
437
437
  # Deprecated since 2023-06-22
438
438
  import warnings
439
439
  warnings.warn(
@@ -641,13 +641,18 @@ class SweepSourceTab(GraphTabWidget):
641
641
  def __init__(self, parent, source):
642
642
  super().__init__(parent)
643
643
 
644
- # Add A/D
645
- for i in range(source.channel_count()):
646
- self._add_graph_tab(source, i)
647
-
648
- # Add D/A
649
- for i in range(source.da_count()):
650
- self._add_graph_tab(source, i, True)
644
+ if source.sweep_count() > 0:
645
+ # Must have this condition to avoid getting exceptions with e.g.
646
+ # the empty WCP file in the test data set.
647
+ # myokit log myokit/tests/data/formats/wcp-file-empty.wcp
648
+
649
+ # Add A/D
650
+ for i in range(source.channel_count()):
651
+ self._add_graph_tab(source, i)
652
+
653
+ # Add D/A
654
+ for i in range(source.da_count()):
655
+ self._add_graph_tab(source, i, True)
651
656
 
652
657
  # Add meta data
653
658
  self._add_meta_tab(source)
myokit/lib/markov.py CHANGED
@@ -1077,7 +1077,7 @@ class AnalyticalSimulation:
1077
1077
  y0 = PI.dot(self._state.reshape((n, 1)))
1078
1078
 
1079
1079
  # Reshape times array
1080
- times = np.array(times, copy=False).reshape((len(times),))
1080
+ times = np.asarray(times).reshape((len(times),))
1081
1081
 
1082
1082
  # Calculate state
1083
1083
  x = P.dot(y0 * np.exp(times * E))
@@ -1223,7 +1223,7 @@ class DiscreteSimulation:
1223
1223
 
1224
1224
  Returns a discretized state ``y`` where ``sum(y) = nchannels``.
1225
1225
  """
1226
- x = np.array(x, copy=False, dtype=float)
1226
+ x = np.asarray(x, dtype=float)
1227
1227
  if (np.abs(1 - np.sum(x))) > 1e-6:
1228
1228
  raise ValueError(
1229
1229
  'The sum of fractions in the state to be discretized must'
myokit/lib/plots.py CHANGED
@@ -62,28 +62,28 @@ def simulation_times(
62
62
  def stair(ax, time, realtime, evaluations):
63
63
  if time is None:
64
64
  raise ValueError('This plotting mode requires "time" to be set.')
65
- time = np.array(time, copy=False)
65
+ time = np.asarray(time)
66
66
  step = np.arange(0, len(time))
67
67
  ax.step(time, step, label=label)
68
68
 
69
69
  def stair_inverse(ax, time, realtime, evaluations):
70
70
  if time is None:
71
71
  raise ValueError('This plotting mode requires "time" to be set.')
72
- time = np.array(time, copy=False)
72
+ time = np.asarray(time)
73
73
  step = np.arange(0, len(time))
74
74
  ax.step(step, time, label=label)
75
75
 
76
76
  def load(ax, time, realtime, evaluations):
77
77
  if time is None:
78
78
  raise ValueError('This plotting mode requires "time" to be set.')
79
- time = np.array(time, copy=False)
79
+ time = np.asarray(time)
80
80
  size = np.log(1 / (time[1:] - time[:-1]))
81
81
  ax.step(time[1:], size, label=label)
82
82
 
83
83
  def histo(ax, time, realtime, evaluations):
84
84
  if time is None:
85
85
  raise ValueError('This plotting mode requires "time" to be set.')
86
- time = np.array(time, copy=False)
86
+ time = np.asarray(time)
87
87
  zero = float(time[0])
88
88
  bucket_w = (time[-1] - zero) / nbuckets
89
89
  bucket_x = np.zeros(nbuckets)
@@ -167,11 +167,11 @@ class DataBlock1dTest(unittest.TestCase):
167
167
  d = myokit.DataLog()
168
168
  d.set_time_key('time')
169
169
  d['time'] = time
170
- d['x', 0] = np.array(down, copy=True)
171
- d['x', 1] = np.array(down, copy=True)
172
- d['x', 2] = np.array(down, copy=True)
173
- d['x', 3] = np.array(down, copy=True)
174
- d['x', 4] = np.array(down, copy=True)
170
+ d['x', 0] = np.copy(down)
171
+ d['x', 1] = np.copy(down)
172
+ d['x', 2] = np.copy(down)
173
+ d['x', 3] = np.copy(down)
174
+ d['x', 4] = np.copy(down)
175
175
  d['x', 1][10:] = 40
176
176
  b = myokit.DataBlock1d.from_log(d)
177
177
  self.assertEqual(b.cv('x'), 0)
@@ -180,11 +180,11 @@ class DataBlock1dTest(unittest.TestCase):
180
180
  d = myokit.DataLog()
181
181
  d.set_time_key('time')
182
182
  d['time'] = time
183
- d['x', 0] = np.array(down, copy=True)
184
- d['x', 1] = np.array(down, copy=True)
185
- d['x', 2] = np.array(down, copy=True)
186
- d['x', 3] = np.array(down, copy=True)
187
- d['x', 4] = np.array(down, copy=True)
183
+ d['x', 0] = np.copy(down)
184
+ d['x', 1] = np.copy(down)
185
+ d['x', 2] = np.copy(down)
186
+ d['x', 3] = np.copy(down)
187
+ d['x', 4] = np.copy(down)
188
188
  d['x', 1][10:] = 40
189
189
  d['x', 2][10:] = 40
190
190
  d['x', 3][10:] = 40
@@ -2109,9 +2109,12 @@ class DataLogTest(unittest.TestCase):
2109
2109
  self.assertRaises(ValueError, d.split_periodic, 0)
2110
2110
  self.assertRaises(ValueError, d.split_periodic, -1)
2111
2111
 
2112
- # Test larger period than log data
2112
+ # Test larger period than log data: Should return length-1 list
2113
2113
  d['x'] = [4, 5, 6, 7]
2114
2114
  e = d.split_periodic(100)
2115
+ self.assertIsInstance(e, list)
2116
+ self.assertEqual(len(e), 1)
2117
+ e = e[0]
2115
2118
  self.assertEqual(set(d.keys()), set(e.keys()))
2116
2119
  self.assertFalse(d is e)
2117
2120