myokit 1.35.0__py3-none-any.whl → 1.35.2__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 (47) hide show
  1. myokit/__init__.py +11 -14
  2. myokit/__main__.py +0 -3
  3. myokit/_config.py +1 -3
  4. myokit/_datablock.py +914 -12
  5. myokit/_model_api.py +1 -3
  6. myokit/_myokit_version.py +1 -1
  7. myokit/_protocol.py +14 -28
  8. myokit/_sim/cable.c +1 -1
  9. myokit/_sim/cable.py +3 -2
  10. myokit/_sim/cmodel.h +1 -0
  11. myokit/_sim/cvodessim.c +79 -42
  12. myokit/_sim/cvodessim.py +20 -8
  13. myokit/_sim/fiber_tissue.c +1 -1
  14. myokit/_sim/fiber_tissue.py +3 -2
  15. myokit/_sim/openclsim.c +1 -1
  16. myokit/_sim/openclsim.py +8 -11
  17. myokit/_sim/pacing.h +121 -106
  18. myokit/_unit.py +1 -1
  19. myokit/formats/__init__.py +178 -0
  20. myokit/formats/axon/_abf.py +911 -841
  21. myokit/formats/axon/_atf.py +62 -59
  22. myokit/formats/axon/_importer.py +2 -2
  23. myokit/formats/heka/__init__.py +38 -0
  24. myokit/formats/heka/_importer.py +39 -0
  25. myokit/formats/heka/_patchmaster.py +2512 -0
  26. myokit/formats/wcp/_wcp.py +318 -133
  27. myokit/gui/datablock_viewer.py +144 -77
  28. myokit/gui/datalog_viewer.py +212 -231
  29. myokit/tests/ansic_event_based_pacing.py +3 -3
  30. myokit/tests/{ansic_fixed_form_pacing.py → ansic_time_series_pacing.py} +6 -6
  31. myokit/tests/data/formats/abf-v2.abf +0 -0
  32. myokit/tests/test_datablock.py +84 -0
  33. myokit/tests/test_datalog.py +2 -1
  34. myokit/tests/test_formats_axon.py +589 -136
  35. myokit/tests/test_formats_wcp.py +191 -22
  36. myokit/tests/test_pacing_system_c.py +51 -23
  37. myokit/tests/test_pacing_system_py.py +18 -0
  38. myokit/tests/test_simulation_1d.py +62 -22
  39. myokit/tests/test_simulation_cvodes.py +52 -3
  40. myokit/tests/test_simulation_fiber_tissue.py +35 -4
  41. myokit/tests/test_simulation_opencl.py +28 -4
  42. {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/LICENSE.txt +1 -1
  43. {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/METADATA +1 -1
  44. {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/RECORD +47 -44
  45. {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/WHEEL +0 -0
  46. {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/entry_points.txt +0 -0
  47. {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/top_level.txt +0 -0
myokit/_model_api.py CHANGED
@@ -1385,9 +1385,7 @@ class Model(ObjectWithMeta, VarProvider):
1385
1385
  def eval_state_derivatives(
1386
1386
  self, state=None, inputs=None, precision=myokit.DOUBLE_PRECISION,
1387
1387
  ignore_errors=False):
1388
- """
1389
- Deprecated alias of :meth:`evaluate_derivatives()`.
1390
- """
1388
+ """ Deprecated alias of :meth:`evaluate_derivatives()`. """
1391
1389
  # Deprecated since 2021-08-03
1392
1390
  import warnings
1393
1391
  warnings.warn(
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, 35, 0
17
+ __version_tuple__ = 1, 35, 2
18
18
 
19
19
  # String version of the version number
20
20
  __version__ = '.'.join([str(x) for x in __version_tuple__])
myokit/_protocol.py CHANGED
@@ -746,9 +746,10 @@ class PacingSystem:
746
746
  >>> pace = np.array([s.advance(t) for t in time])
747
747
 
748
748
  """
749
- def __init__(self, protocol):
750
- # The current time and pacing level
751
- self._time = 0
749
+ def __init__(self, protocol, initial_time=0):
750
+ # The initial and current time and pacing level
751
+ self._initial_time = initial_time # Needed if we add a reset()
752
+ self._time = initial_time
752
753
  self._pace = 0
753
754
 
754
755
  # Currently active event
@@ -758,15 +759,15 @@ class PacingSystem:
758
759
  self._tdown = None
759
760
 
760
761
  # The next time the pacing variable changes
761
- self._tnext = 0
762
+ self._tnext = initial_time
762
763
 
763
764
  # Create a copy of the protocol
764
765
  self._protocol = protocol.clone()
765
766
  #TODO: For periodic events, set an _t0, and a _i, use them to calculate
766
767
  # the next occurence
767
768
 
768
- # Advance to time zero
769
- self.advance(0)
769
+ # Advance to initial time
770
+ self.advance(initial_time)
770
771
 
771
772
  def advance(self, new_time):
772
773
  """
@@ -822,21 +823,15 @@ class PacingSystem:
822
823
  return self._pace
823
824
 
824
825
  def next_time(self):
825
- """
826
- Returns the next time the pacing system will halt at.
827
- """
826
+ """ Returns the next time the pacing system will halt at. """
828
827
  return self._tnext
829
828
 
830
829
  def pace(self):
831
- """
832
- Returns the current value of the pacing variable.
833
- """
830
+ """ Returns the current value of the pacing variable. """
834
831
  return self._pace
835
832
 
836
833
  def time(self):
837
- """
838
- Returns the current time in the pacing system.
839
- """
834
+ """ Returns the current time in the pacing system. """
840
835
  return self._time
841
836
 
842
837
 
@@ -863,7 +858,6 @@ class TimeSeriesProtocol:
863
858
  Protocols can be compared with ``==``, which will check if the sequence of
864
859
  time value pairs is the same, and the interpolation method is the same.
865
860
  Protocols can be serialized with ``pickle``.
866
-
867
861
  """
868
862
 
869
863
  def __init__(self, times, values, method=None):
@@ -909,15 +903,11 @@ class TimeSeriesProtocol:
909
903
  self._method = values['method']
910
904
 
911
905
  def clone(self):
912
- """
913
- Returns a clone of this protocol.
914
- """
906
+ """ Returns a clone of this protocol. """
915
907
  return TimeSeriesProtocol(self._times, self._values, self._method)
916
908
 
917
909
  def pace(self, t):
918
- """
919
- Returns the value of the pacing variable at time ``t``.
920
- """
910
+ """ Returns the value of the pacing variable at time ``t``. """
921
911
  if t < self._times[0]:
922
912
  return self._values[0]
923
913
  if t > self._times[-1]:
@@ -930,13 +920,9 @@ class TimeSeriesProtocol:
930
920
  ) / (self._times[i + 1] - self._times[i])
931
921
 
932
922
  def times(self):
933
- """
934
- Returns a list of the times in this protocol.
935
- """
923
+ """ Returns a list of the times in this protocol. """
936
924
  return self._times
937
925
 
938
926
  def values(self):
939
- """
940
- Returns a list of the values in this protocol.
941
- """
927
+ """ Returns a list of the values in this protocol. """
942
928
  return self._values
myokit/_sim/cable.c CHANGED
@@ -414,7 +414,7 @@ print(tab + '}')
414
414
  }
415
415
 
416
416
  /* Set up pacing */
417
- pacing = ESys_Create(&flag_pacing);
417
+ pacing = ESys_Create(tmin, &flag_pacing);
418
418
  if (flag_pacing!=ESys_OK) { ESys_SetPyErr(flag_pacing); return sim_clean(); }
419
419
  flag_pacing = ESys_Populate(pacing, protocol);
420
420
  if (flag_pacing!=ESys_OK) { ESys_SetPyErr(flag_pacing); return sim_clean(); }
myokit/_sim/cable.py CHANGED
@@ -35,11 +35,12 @@ class Simulation1d(myokit.CModule):
35
35
  ``time``
36
36
  The simulation time
37
37
  ``pace``
38
- The pacing level, this is set if a protocol was passed in.
38
+ The pacing level, this is set if a protocol was passed in. Will be set
39
+ to 0 if no protocol is provided.
39
40
  ``diffusion_current``
40
41
  The current flowing from the cell to its neighbors. This will be
41
42
  positive when the cell is acting as a source, negative when it is
42
- acting as a sink.
43
+ acting as a sink. Will be set to 0 if no connections are made.
43
44
 
44
45
  The variable ``time`` is set globally, meaning each cell uses the same
45
46
  value. The variables ``pace`` and ``diffusion_current`` have different
myokit/_sim/cmodel.h CHANGED
@@ -482,6 +482,7 @@ Model_SetupPacing(Model model, int n_pace)
482
482
  /* Clear values */
483
483
  for (int i = 0; i < n_pace; i++) {
484
484
  model->pace_values[i] = 0;
485
+ /* Note: This will be overruled by the first call to SetBoundVariables */
485
486
  }
486
487
 
487
488
  return Model_OK;
myokit/_sim/cvodessim.c CHANGED
@@ -192,15 +192,7 @@ Model model; /* A model object */
192
192
  /*
193
193
  * Pacing
194
194
  */
195
- union PSys {
196
- ESys event;
197
- FSys fixed;
198
- };
199
- enum PSysType {
200
- EVENT,
201
- FIXED
202
- };
203
- union PSys *pacing_systems; /* Array of pacing system (event or fixed) */
195
+ union PSys *pacing_systems; /* Array of pacing systems (event based or time series) */
204
196
  enum PSysType *pacing_types; /* Array of pacing system types */
205
197
  PyObject *protocols; /* The protocols used to generate the pacing systems */
206
198
  double* pacing; /* Pacing values, same size as pacing_systems and pacing_types */
@@ -351,16 +343,16 @@ benchmarker_print(char* message)
351
343
  int
352
344
  rhs(realtype t, N_Vector y, N_Vector ydot, void *user_data)
353
345
  {
354
- FSys_Flag flag_fpacing;
346
+ TSys_Flag flag_fpacing;
355
347
  UserData fdata;
356
348
  int i;
357
349
 
358
- /* Fixed-form pacing? Then look-up correct value of pacing variable */
350
+ /* Time-series pacing? Then look-up correct value of pacing variable */
359
351
  for (int i = 0; i < n_pace; i++) {
360
- if (pacing_types[i] == FIXED) {
361
- pacing[i] = FSys_GetLevel(pacing_systems[i].fixed, t, &flag_fpacing);
362
- if (flag_fpacing != FSys_OK) { /* This should never happen */
363
- FSys_SetPyErr(flag_fpacing);
352
+ if (pacing_types[i] == TSys_TYPE) {
353
+ pacing[i] = TSys_GetLevel(pacing_systems[i].tsys, t, &flag_fpacing);
354
+ if (flag_fpacing != TSys_OK) { /* This should never happen */
355
+ TSys_SetPyErr(flag_fpacing);
364
356
  return -1; /* Negative value signals irrecoverable error to CVODE */
365
357
  }
366
358
  }
@@ -441,6 +433,9 @@ sim_clean(void)
441
433
  #endif
442
434
 
443
435
  /* CVode arrays */
436
+ #ifdef MYOKIT_DEBUG_MESSAGES
437
+ printf("CM ..Sundials vectors.\n");
438
+ #endif
444
439
  if (y != NULL) { N_VDestroy_Serial(y); y = NULL; }
445
440
  if (ylast != NULL) { N_VDestroy_Serial(ylast); ylast = NULL; }
446
441
  if (sy != NULL) { N_VDestroyVectorArray(sy, model->ns_independents); sy = NULL; }
@@ -450,9 +445,15 @@ sim_clean(void)
450
445
  }
451
446
 
452
447
  /* Root finding results */
448
+ #ifdef MYOKIT_DEBUG_MESSAGES
449
+ printf("CM ..Root-finding results.\n");
450
+ #endif
453
451
  free(rf_direction); rf_direction = NULL;
454
452
 
455
453
  /* Sundials objects */
454
+ #ifdef MYOKIT_DEBUG_MESSAGES
455
+ printf("CM ..Sundials objects.\n");
456
+ #endif
456
457
  CVodeFree(&cvode_mem); cvode_mem = NULL;
457
458
  #if SUNDIALS_VERSION_MAJOR >= 3
458
459
  SUNLinSolFree(sundense_solver); sundense_solver = NULL;
@@ -463,6 +464,9 @@ sim_clean(void)
463
464
  #endif
464
465
 
465
466
  /* User data and parameter scale array*/
467
+ #ifdef MYOKIT_DEBUG_MESSAGES
468
+ printf("CM ..Sundials user-data.\n");
469
+ #endif
466
470
  free(pbar);
467
471
  if (udata != NULL) {
468
472
  free(udata->p);
@@ -470,11 +474,15 @@ sim_clean(void)
470
474
  }
471
475
 
472
476
  /* Pacing systems */
477
+ #ifdef MYOKIT_DEBUG_MESSAGES
478
+ printf("CM ..Pacing systems.\n");
479
+ #endif
473
480
  for (int i = 0; i < n_pace; i++) {
474
- if (pacing_types[i] == FIXED) {
475
- FSys_Destroy(pacing_systems[i].fixed);
476
- } else if (pacing_types[i] == EVENT) {
477
- ESys_Destroy(pacing_systems[i].event);
481
+ // Note: Type is ESys, TSys, or not set!
482
+ if (pacing_types[i] == ESys_TYPE) {
483
+ ESys_Destroy(pacing_systems[i].esys);
484
+ } else if (pacing_types[i] == TSys_TYPE) {
485
+ TSys_Destroy(pacing_systems[i].tsys);
478
486
  }
479
487
  }
480
488
  free(pacing_systems); pacing_systems = NULL;
@@ -482,6 +490,9 @@ sim_clean(void)
482
490
  free(pacing); pacing = NULL;
483
491
 
484
492
  /* CModel */
493
+ #ifdef MYOKIT_DEBUG_MESSAGES
494
+ printf("CM ..CModel.\n");
495
+ #endif
485
496
  Model_Destroy(model); model = NULL;
486
497
 
487
498
  /* Benchmarking and profiling */
@@ -508,6 +519,10 @@ sim_clean(void)
508
519
  PyObject*
509
520
  sim_cleanx(PyObject* ex_type, const char* msg, ...)
510
521
  {
522
+ #ifdef MYOKIT_DEBUG_MESSAGES
523
+ printf("CM Entering sim_cleanx.\n");
524
+ #endif
525
+
511
526
  va_list argptr;
512
527
  char errstr[1024];
513
528
 
@@ -544,11 +559,11 @@ sim_init(PyObject *self, PyObject *args)
544
559
  int flag_cvode;
545
560
  Model_Flag flag_model;
546
561
  ESys_Flag flag_epacing;
547
- FSys_Flag flag_fpacing;
562
+ TSys_Flag flag_fpacing;
548
563
 
549
564
  /* Pacing systems */
550
565
  ESys epacing;
551
- FSys fpacing;
566
+ TSys fpacing;
552
567
 
553
568
  /* General purpose ints for iterating */
554
569
  int i, j;
@@ -618,7 +633,7 @@ sim_init(PyObject *self, PyObject *args)
618
633
  &bound_py, /* 4. List: store final bound variables here */
619
634
  &literals, /* 5. List: literal constant values */
620
635
  &parameters, /* 6. List: parameter values */
621
- &protocols, /* 7. Event-based or fixed protocols */
636
+ &protocols, /* 7. Event-based or time series protocols */
622
637
  &log_dict, /* 8. DataLog */
623
638
  &log_interval, /* 9. Float: log interval, or 0 */
624
639
  &log_times, /* 10. List of logging times, or None */
@@ -955,6 +970,9 @@ sim_init(PyObject *self, PyObject *args)
955
970
  */
956
971
  n_pace = 0;
957
972
  if (protocols != Py_None) {
973
+ #ifdef MYOKIT_DEBUG_MESSAGES
974
+ printf("CM Initialising pacing systems\n");
975
+ #endif
958
976
  if (!PyList_Check(protocols)) {
959
977
  return sim_cleanx(PyExc_TypeError, "'protocols' must be a list.");
960
978
  }
@@ -980,43 +998,61 @@ sim_init(PyObject *self, PyObject *args)
980
998
  tnext = tmax;
981
999
 
982
1000
  /*
983
- * Set up event-based and/or fixed pacing.
1001
+ * Set up event-based and/or time-series pacing.
984
1002
  */
985
1003
  if (protocols != Py_None) {
986
1004
  for (int i = 0; i < PyList_Size(protocols); i++) {
987
1005
  PyObject *protocol = PyList_GetItem(protocols, i);
988
1006
  const char* protocol_type_name = Py_TYPE(protocol)->tp_name;
989
1007
  if (strcmp(protocol_type_name, "Protocol") == 0) {
990
- pacing_systems[i].event = ESys_Create(&flag_epacing);
991
- pacing_types[i] = EVENT;
992
- epacing = pacing_systems[i].event;
1008
+
1009
+ epacing = ESys_Create(tmin, &flag_epacing);
993
1010
  if (flag_epacing != ESys_OK) { ESys_SetPyErr(flag_epacing); return sim_clean(); }
1011
+ pacing_systems[i].esys = epacing;
1012
+ pacing_types[i] = ESys_TYPE;
1013
+
994
1014
  flag_epacing = ESys_Populate(epacing, protocol);
995
1015
  if (flag_epacing != ESys_OK) { ESys_SetPyErr(flag_epacing); return sim_clean(); }
1016
+
996
1017
  flag_epacing = ESys_AdvanceTime(epacing, tmin);
997
1018
  if (flag_epacing != ESys_OK) { ESys_SetPyErr(flag_epacing); return sim_clean(); }
1019
+
998
1020
  t_proposed = ESys_GetNextTime(epacing, &flag_epacing);
999
1021
  pacing[i] = ESys_GetLevel(epacing, &flag_epacing);
1000
1022
  tnext = fmin(t_proposed, tnext);
1001
1023
 
1002
- #ifdef MYOKIT_DEBUG_PROFILING
1024
+ #if defined(MYOKIT_DEBUG_PROFILING)
1003
1025
  benchmarker_print("CP Created event-based pacing system.");
1026
+ #elif defined(MYOKIT_DEBUG_MESSAGES)
1027
+ printf("CM Created an event-based pacing system\n");
1004
1028
  #endif
1029
+
1005
1030
  } else if (strcmp(protocol_type_name, "TimeSeriesProtocol") == 0) {
1006
- pacing_systems[i].fixed = FSys_Create(&flag_fpacing);
1007
- pacing_types[i] = FIXED;
1008
- fpacing = pacing_systems[i].fixed;
1009
- if (flag_fpacing != FSys_OK) { FSys_SetPyErr(flag_fpacing); return sim_clean(); }
1010
- flag_fpacing = FSys_Populate(fpacing, protocol);
1011
- if (flag_fpacing != FSys_OK) { FSys_SetPyErr(flag_fpacing); return sim_clean(); }
1012
1031
 
1032
+ fpacing = TSys_Create(&flag_fpacing);
1033
+ pacing_systems[i].tsys = fpacing;
1034
+ pacing_types[i] = TSys_TYPE;
1035
+
1036
+ if (flag_fpacing != TSys_OK) { TSys_SetPyErr(flag_fpacing); return sim_clean(); }
1037
+ flag_fpacing = TSys_Populate(fpacing, protocol);
1038
+ if (flag_fpacing != TSys_OK) { TSys_SetPyErr(flag_fpacing); return sim_clean(); }
1039
+ pacing[i] = 0;
1013
1040
 
1014
- #ifdef MYOKIT_DEBUG_PROFILING
1015
- benchmarker_print("CP Created fixed-form pacing system.");
1041
+ #if defined(MYOKIT_DEBUG_PROFILING)
1042
+ benchmarker_print("CP Created time-series pacing system.");
1043
+ #elif defined(MYOKIT_DEBUG_MESSAGES)
1044
+ printf("CM Added a time-series pacing system\n");
1016
1045
  #endif
1046
+
1017
1047
  } else {
1018
- printf("protocol_type_name: %s", protocol_type_name);
1019
- return sim_cleanx(PyExc_TypeError, "Item %d in 'protocols' is not a myokit.Protocol or myokit.TimeSeriesProtocol object.", i);
1048
+
1049
+ /* Pacing label defined but no protocol set. Usually happens through set_protocol(None). */
1050
+ #if defined(MYOKIT_DEBUG_MESSAGES)
1051
+ printf("CM Unsetting previously set protocol\n");
1052
+ #endif
1053
+
1054
+ pacing_types[i] = PSys_NOT_SET;
1055
+ pacing[i] = 0; /* See #320 and technical note on pacing */
1020
1056
  }
1021
1057
  }
1022
1058
  }
@@ -1284,6 +1320,9 @@ sim_step(PyObject *self, PyObject *args)
1284
1320
  int flag_root; /* Root finding flag */
1285
1321
  int flag_reinit = 0; /* Set if CVODE needs to be reset during a simulation step */
1286
1322
 
1323
+ /* Pacing */
1324
+ ESys epacing;
1325
+
1287
1326
  /* Multi-purpose ints for iterating */
1288
1327
  int i, j;
1289
1328
 
@@ -1521,12 +1560,10 @@ sim_step(PyObject *self, PyObject *args)
1521
1560
  */
1522
1561
  tnext = tmax;
1523
1562
  for (int i = 0; i < n_pace; i++) {
1524
- if (pacing_types[i] == EVENT) {
1525
- ESys epacing = pacing_systems[i].event;
1563
+ if (pacing_types[i] == ESys_TYPE) {
1564
+ epacing = pacing_systems[i].esys;
1526
1565
  flag_epacing = ESys_AdvanceTime(epacing, t);
1527
- if (flag_epacing != ESys_OK) {
1528
- ESys_SetPyErr(flag_epacing); return sim_clean();
1529
- }
1566
+ if (flag_epacing != ESys_OK) { ESys_SetPyErr(flag_epacing); return sim_clean(); }
1530
1567
  t_proposed = ESys_GetNextTime(epacing, NULL);
1531
1568
  tnext = fmin(tnext, t_proposed);
1532
1569
  pacing[i] = ESys_GetLevel(epacing, NULL);
myokit/_sim/cvodessim.py CHANGED
@@ -88,6 +88,19 @@ class Simulation(myokit.CModule):
88
88
 
89
89
  No variable labels are required for this simulation type.
90
90
 
91
+ **Multiple protocols or no protocol**
92
+
93
+ This simulation supports pacing with more than one protocol. To this end,
94
+ pass in a dictionary mapping pacing labels (bindings) to :class:`Protocol`
95
+ or :class:`TimeSeriesProtocol` objects, e.g.
96
+ ``protocol={'pace_1': protocol_1, 'pace_2': protocol_2}``.
97
+
98
+ For backwards compatibility, if no protocol is set and ``protocol=None``,
99
+ then the pacing label ``pace`` is still registered (allowing later calls to
100
+ add a protocol with ``set_protocol``. Alternatively, if ``protocol={}``
101
+ then no pacing labels will be registered, and any subsequent calls to
102
+ ``set_protocol`` will fail.
103
+
91
104
  **Storing and loading simulation objects**
92
105
 
93
106
  There are two ways to store Simulation objects to the file system: 1.
@@ -163,11 +176,13 @@ class Simulation(myokit.CModule):
163
176
  if isinstance(protocol, (myokit.Protocol, myokit.TimeSeriesProtocol)):
164
177
  protocol = {'pace': protocol}
165
178
  elif protocol is None:
166
- # TODO: This can be an empty dict once #320 is resolved
179
+ # For backwards compatibility, we still register 'pace'. This
180
+ # means users can call `set_protocol` at a later time to set a
181
+ # protocol.
167
182
  protocol = {'pace': None}
168
183
  for label, protocol in protocol.items():
184
+ self._protocols.append(None)
169
185
  self._pacing_labels.append(label)
170
- self._protocols.append(myokit.Protocol())
171
186
  self.set_protocol(protocol, label)
172
187
 
173
188
  # Generate C Model code, get sensitivity and constants info
@@ -1008,8 +1023,8 @@ class Simulation(myokit.CModule):
1008
1023
 
1009
1024
  def set_protocol(self, protocol, label='pace'):
1010
1025
  """
1011
- Set an event-based pacing :class:`Protocol` or a :class:`FixedProtocol`
1012
- for the given ``label``.
1026
+ Set an event-based pacing :class:`Protocol` or a
1027
+ :class:`TimeSeriesProtocol` for the given ``label``.
1013
1028
 
1014
1029
  To remove a previously set binding call this method with ``protocol =
1015
1030
  None``. In this case, the value of any variables bound to ``label``
@@ -1023,10 +1038,7 @@ class Simulation(myokit.CModule):
1023
1038
  raise ValueError('Unknown pacing label: ' + str(label))
1024
1039
 
1025
1040
  # Set new protocol
1026
- if protocol is None:
1027
- self._protocols[index] = myokit.Protocol()
1028
- else:
1029
- self._protocols[index] = protocol.clone()
1041
+ self._protocols[index] = None if protocol is None else protocol.clone()
1030
1042
 
1031
1043
  def __setstate__(self, state):
1032
1044
  """
@@ -500,7 +500,7 @@ sim_init(PyObject* self, PyObject* args)
500
500
  //
501
501
  // Set up pacing system
502
502
  //
503
- pacing = ESys_Create(&flag_pacing);
503
+ pacing = ESys_Create(tmin, &flag_pacing);
504
504
  if(flag_pacing!=ESys_OK) { ESys_SetPyErr(flag_pacing); return sim_clean(); }
505
505
  flag_pacing = ESys_Populate(pacing, protocol);
506
506
  if(flag_pacing!=ESys_OK) { ESys_SetPyErr(flag_pacing); return sim_clean(); }
@@ -56,11 +56,12 @@ class FiberTissueSimulation(myokit.CModule):
56
56
  ``time`` (global)
57
57
  The simulation time
58
58
  ``pace`` (per-cell)
59
- The pacing level, this is set if a protocol was passed in.
59
+ The pacing level, this is set if a protocol was passed in. Will be set
60
+ to 0 if no protocol is provided.
60
61
  ``diffusion_current`` (per-cell)
61
62
  The current flowing from the cell to its neighbors. This will be
62
63
  positive when the cell is acting as a source, negative when it is
63
- acting as a sink.
64
+ acting as a sink. Will be set to 0 if no connections are made.
64
65
 
65
66
  The variable ``time`` is set globally, meaning each cell uses the same
66
67
  value. The variables ``pace`` and ``diffusion_current`` have different
myokit/_sim/openclsim.c CHANGED
@@ -485,7 +485,7 @@ sim_init(PyObject* self, PyObject* args)
485
485
  //
486
486
  // Set up pacing system
487
487
  //
488
- pacing = ESys_Create(&flag_pacing);
488
+ pacing = ESys_Create(tmin, &flag_pacing);
489
489
  if(flag_pacing != ESys_OK) { ESys_SetPyErr(flag_pacing); return sim_clean(); }
490
490
  flag_pacing = ESys_Populate(pacing, protocol);
491
491
  if(flag_pacing != ESys_OK) { ESys_SetPyErr(flag_pacing); return sim_clean(); }
myokit/_sim/openclsim.py CHANGED
@@ -56,7 +56,8 @@ class SimulationOpenCL(myokit.CModule):
56
56
  ``time``
57
57
  The simulation time
58
58
  ``pace``
59
- The pacing level, this is set if a protocol was passed in.
59
+ The pacing level, this is set if a protocol was passed in. Will be set
60
+ to 0 if no protocol is provided.
60
61
  ``diffusion_current`` (if enabled)
61
62
  The current flowing from the cell to its neighbors. This will be
62
63
  positive when the cell is acting as a source, negative when it is
@@ -780,8 +781,8 @@ class SimulationOpenCL(myokit.CModule):
780
781
  (see the argument list below for the meaning of the variables).
781
782
 
782
783
  This can be equated to Myokit's diffusion current, but only if we
783
- assume **zero-flux boundary conditions**, a **regularly spaced grid**,
784
- and **no spatial heterogeneity in D** (or g).
784
+ assume *zero-flux boundary conditions*, a *regularly spaced grid*,
785
+ and *no spatial heterogeneity in D* (or g).
785
786
 
786
787
  With these assumptions, we can use finite differences to find::
787
788
 
@@ -791,13 +792,13 @@ class SimulationOpenCL(myokit.CModule):
791
792
  respect to unit membrane area.
792
793
  For models with currents normalized to area this is unproblematic, but
793
794
  to convert to models with unnormalized currents this means we have
794
- added the further assumption that **each node contains some fixed
795
- amount of membrane**, determined by an area A::
795
+ added the further assumption that *each node contains some fixed
796
+ amount of membrane*, determined by an area A::
796
797
 
797
798
  g = (1 / chi) * (k / (k + 1)) * D * (1 / dx^2) * A
798
799
 
799
800
  This equation can also be applied in two dimensions, but only if
800
- **we assume that the conductivity matrix is diagonal**, in which case::
801
+ *we assume that the conductivity matrix is diagonal*, in which case::
801
802
 
802
803
  gx = (1 / chi) * (k / (k + 1)) * Dx * (1 / dx^2) * A
803
804
  gy = (1 / chi) * (k / (k + 1)) * Dy * (1 / dy^2) * A
@@ -1708,11 +1709,7 @@ class SimulationOpenCL(myokit.CModule):
1708
1709
  self._step_size = step_size
1709
1710
 
1710
1711
  def set_time(self, time=0):
1711
- """
1712
- Sets the current simulation time.
1713
- """
1714
- if time < 0:
1715
- raise ValueError('Simulation time cannot be negative')
1712
+ """ Sets the current simulation time. """
1716
1713
  self._time = float(time)
1717
1714
 
1718
1715
  def shape(self):