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.
- myokit/__init__.py +11 -14
- myokit/__main__.py +0 -3
- myokit/_config.py +1 -3
- myokit/_datablock.py +914 -12
- myokit/_model_api.py +1 -3
- myokit/_myokit_version.py +1 -1
- myokit/_protocol.py +14 -28
- myokit/_sim/cable.c +1 -1
- myokit/_sim/cable.py +3 -2
- myokit/_sim/cmodel.h +1 -0
- myokit/_sim/cvodessim.c +79 -42
- myokit/_sim/cvodessim.py +20 -8
- myokit/_sim/fiber_tissue.c +1 -1
- myokit/_sim/fiber_tissue.py +3 -2
- myokit/_sim/openclsim.c +1 -1
- myokit/_sim/openclsim.py +8 -11
- myokit/_sim/pacing.h +121 -106
- myokit/_unit.py +1 -1
- myokit/formats/__init__.py +178 -0
- myokit/formats/axon/_abf.py +911 -841
- myokit/formats/axon/_atf.py +62 -59
- myokit/formats/axon/_importer.py +2 -2
- myokit/formats/heka/__init__.py +38 -0
- myokit/formats/heka/_importer.py +39 -0
- myokit/formats/heka/_patchmaster.py +2512 -0
- myokit/formats/wcp/_wcp.py +318 -133
- myokit/gui/datablock_viewer.py +144 -77
- myokit/gui/datalog_viewer.py +212 -231
- myokit/tests/ansic_event_based_pacing.py +3 -3
- myokit/tests/{ansic_fixed_form_pacing.py → ansic_time_series_pacing.py} +6 -6
- myokit/tests/data/formats/abf-v2.abf +0 -0
- myokit/tests/test_datablock.py +84 -0
- myokit/tests/test_datalog.py +2 -1
- myokit/tests/test_formats_axon.py +589 -136
- myokit/tests/test_formats_wcp.py +191 -22
- myokit/tests/test_pacing_system_c.py +51 -23
- myokit/tests/test_pacing_system_py.py +18 -0
- myokit/tests/test_simulation_1d.py +62 -22
- myokit/tests/test_simulation_cvodes.py +52 -3
- myokit/tests/test_simulation_fiber_tissue.py +35 -4
- myokit/tests/test_simulation_opencl.py +28 -4
- {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/LICENSE.txt +1 -1
- {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/METADATA +1 -1
- {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/RECORD +47 -44
- {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/WHEEL +0 -0
- {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/entry_points.txt +0 -0
- {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,
|
|
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.
|
|
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 =
|
|
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
|
|
769
|
-
self.advance(
|
|
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
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
|
-
|
|
346
|
+
TSys_Flag flag_fpacing;
|
|
355
347
|
UserData fdata;
|
|
356
348
|
int i;
|
|
357
349
|
|
|
358
|
-
/*
|
|
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] ==
|
|
361
|
-
pacing[i] =
|
|
362
|
-
if (flag_fpacing !=
|
|
363
|
-
|
|
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
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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
|
-
|
|
562
|
+
TSys_Flag flag_fpacing;
|
|
548
563
|
|
|
549
564
|
/* Pacing systems */
|
|
550
565
|
ESys epacing;
|
|
551
|
-
|
|
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
|
¶meters, /* 6. List: parameter values */
|
|
621
|
-
&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
|
|
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
|
-
|
|
991
|
-
|
|
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
|
-
#
|
|
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
|
-
#
|
|
1015
|
-
benchmarker_print("CP Created
|
|
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
|
-
|
|
1019
|
-
|
|
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] ==
|
|
1525
|
-
|
|
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
|
-
#
|
|
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
|
|
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
|
"""
|
myokit/_sim/fiber_tissue.c
CHANGED
|
@@ -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(); }
|
myokit/_sim/fiber_tissue.py
CHANGED
|
@@ -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
|
|
784
|
-
and
|
|
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
|
|
795
|
-
amount of membrane
|
|
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
|
-
|
|
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):
|