myokit 1.33.9__py3-none-any.whl → 1.35.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 (229) hide show
  1. myokit/__init__.py +9 -36
  2. myokit/__main__.py +76 -142
  3. myokit/_aux.py +62 -16
  4. myokit/_bin/example.mmt +1 -2
  5. myokit/_bin/install-win/menu.json +7 -7
  6. myokit/_config.py +22 -31
  7. myokit/_datablock.py +30 -74
  8. myokit/_datalog.py +49 -72
  9. myokit/_err.py +25 -24
  10. myokit/_expressions.py +50 -68
  11. myokit/_io.py +15 -27
  12. myokit/_model_api.py +453 -249
  13. myokit/_myokit_version.py +1 -5
  14. myokit/_parsing.py +38 -44
  15. myokit/_progress.py +5 -8
  16. myokit/_protocol.py +99 -9
  17. myokit/_sim/__init__.py +7 -24
  18. myokit/_sim/cable.c +6 -8
  19. myokit/_sim/cable.py +6 -8
  20. myokit/_sim/cmodel.h +125 -70
  21. myokit/_sim/cmodel.py +12 -14
  22. myokit/_sim/compiler.py +1 -4
  23. myokit/_sim/cvodessim.c +196 -118
  24. myokit/_sim/cvodessim.py +130 -103
  25. myokit/_sim/differential.hpp +4 -4
  26. myokit/_sim/fiber_tissue.c +4 -8
  27. myokit/_sim/fiber_tissue.py +11 -13
  28. myokit/_sim/jacobian.cpp +2 -2
  29. myokit/_sim/jacobian.py +11 -8
  30. myokit/_sim/mcl.h +53 -55
  31. myokit/_sim/opencl.py +21 -27
  32. myokit/_sim/openclsim.c +3 -7
  33. myokit/_sim/openclsim.cl +3 -3
  34. myokit/_sim/openclsim.py +49 -40
  35. myokit/_sim/pacing.h +36 -16
  36. myokit/_sim/rhs.c +6 -13
  37. myokit/_sim/rhs.py +5 -14
  38. myokit/_sim/sundials.py +1 -4
  39. myokit/_system.py +10 -16
  40. myokit/_unit.py +4 -13
  41. myokit/float.py +0 -3
  42. myokit/formats/__init__.py +8 -10
  43. myokit/formats/ansic/__init__.py +0 -3
  44. myokit/formats/ansic/_ewriter.py +2 -4
  45. myokit/formats/ansic/_exporter.py +1 -4
  46. myokit/formats/ansic/template/cable.c +4 -4
  47. myokit/formats/ansic/template/euler.c +5 -5
  48. myokit/formats/ansic/template/sim.c +6 -6
  49. myokit/formats/axon/__init__.py +1 -3
  50. myokit/formats/axon/_abf.py +12 -17
  51. myokit/formats/axon/_atf.py +5 -6
  52. myokit/formats/axon/_importer.py +0 -3
  53. myokit/formats/cellml/__init__.py +0 -3
  54. myokit/formats/cellml/_ewriter.py +3 -6
  55. myokit/formats/cellml/_exporter.py +3 -6
  56. myokit/formats/cellml/_importer.py +1 -4
  57. myokit/formats/cellml/v1/__init__.py +0 -4
  58. myokit/formats/cellml/v1/_api.py +8 -11
  59. myokit/formats/cellml/v1/_parser.py +2 -5
  60. myokit/formats/cellml/v1/_writer.py +2 -11
  61. myokit/formats/cellml/v2/__init__.py +0 -3
  62. myokit/formats/cellml/v2/_api.py +8 -17
  63. myokit/formats/cellml/v2/_parser.py +2 -5
  64. myokit/formats/cellml/v2/_writer.py +1 -4
  65. myokit/formats/channelml/__init__.py +0 -3
  66. myokit/formats/channelml/_importer.py +11 -21
  67. myokit/formats/cpp/__init__.py +1 -3
  68. myokit/formats/cpp/_ewriter.py +0 -3
  69. myokit/formats/cuda/__init__.py +0 -3
  70. myokit/formats/cuda/_ewriter.py +2 -4
  71. myokit/formats/cuda/_exporter.py +0 -3
  72. myokit/formats/cuda/template/kernel.cu +8 -5
  73. myokit/formats/easyml/__init__.py +0 -3
  74. myokit/formats/easyml/_ewriter.py +9 -11
  75. myokit/formats/easyml/_exporter.py +2 -5
  76. myokit/formats/html/__init__.py +0 -3
  77. myokit/formats/html/_exporter.py +0 -3
  78. myokit/formats/html/_flatten.py +5 -21
  79. myokit/formats/latex/__init__.py +0 -3
  80. myokit/formats/latex/_ewriter.py +1 -4
  81. myokit/formats/latex/_exporter.py +4 -6
  82. myokit/formats/mathml/__init__.py +0 -3
  83. myokit/formats/mathml/_ewriter.py +2 -11
  84. myokit/formats/mathml/_parser.py +4 -6
  85. myokit/formats/matlab/__init__.py +0 -3
  86. myokit/formats/matlab/_ewriter.py +1 -4
  87. myokit/formats/matlab/_exporter.py +2 -5
  88. myokit/formats/matlab/template/main.m +3 -2
  89. myokit/formats/opencl/__init__.py +0 -3
  90. myokit/formats/opencl/_ewriter.py +2 -4
  91. myokit/formats/opencl/_exporter.py +2 -5
  92. myokit/formats/opencl/template/cable.c +10 -10
  93. myokit/formats/opencl/template/kernel.cl +1 -1
  94. myokit/formats/opencl/template/minilog.py +1 -1
  95. myokit/formats/python/__init__.py +0 -3
  96. myokit/formats/python/_ewriter.py +2 -5
  97. myokit/formats/python/_exporter.py +0 -3
  98. myokit/formats/python/template/sim.py +14 -14
  99. myokit/formats/sbml/__init__.py +0 -3
  100. myokit/formats/sbml/_api.py +50 -44
  101. myokit/formats/sbml/_importer.py +1 -4
  102. myokit/formats/sbml/_parser.py +2 -5
  103. myokit/formats/stan/__init__.py +0 -3
  104. myokit/formats/stan/_ewriter.py +2 -4
  105. myokit/formats/stan/_exporter.py +2 -5
  106. myokit/formats/stan/template/cell.stan +3 -3
  107. myokit/formats/sympy/__init__.py +0 -3
  108. myokit/formats/sympy/_ereader.py +1 -4
  109. myokit/formats/sympy/_ewriter.py +2 -5
  110. myokit/formats/wcp/__init__.py +0 -3
  111. myokit/formats/wcp/_wcp.py +2 -8
  112. myokit/formats/xml/__init__.py +0 -3
  113. myokit/formats/xml/_exporter.py +0 -3
  114. myokit/formats/xml/_split.py +0 -3
  115. myokit/gui/__init__.py +80 -246
  116. myokit/gui/datablock_viewer.py +103 -86
  117. myokit/gui/datalog_viewer.py +214 -66
  118. myokit/gui/explorer.py +15 -21
  119. myokit/gui/ide.py +171 -144
  120. myokit/gui/progress.py +9 -9
  121. myokit/gui/source.py +406 -375
  122. myokit/gui/vargrapher.py +2 -12
  123. myokit/lib/deps.py +12 -13
  124. myokit/lib/guess.py +3 -4
  125. myokit/lib/hh.py +20 -18
  126. myokit/lib/markov.py +21 -20
  127. myokit/lib/multi.py +1 -3
  128. myokit/lib/plots.py +20 -9
  129. myokit/pacing.py +0 -3
  130. myokit/pype.py +7 -18
  131. myokit/tests/__init__.py +3 -6
  132. myokit/tests/ansic_event_based_pacing.py +1 -4
  133. myokit/tests/ansic_fixed_form_pacing.py +3 -6
  134. myokit/tests/data/beeler-1977-model-compare-b.mmt +2 -2
  135. myokit/tests/data/clancy-1999-fitting.mmt +1 -0
  136. myokit/tests/test_aux.py +13 -28
  137. myokit/tests/test_cellml_v1_api.py +4 -19
  138. myokit/tests/test_cellml_v1_parser.py +0 -15
  139. myokit/tests/test_cellml_v1_writer.py +0 -9
  140. myokit/tests/test_cellml_v2_api.py +4 -19
  141. myokit/tests/test_cellml_v2_parser.py +0 -15
  142. myokit/tests/test_cellml_v2_writer.py +0 -9
  143. myokit/tests/test_cmodel.py +16 -22
  144. myokit/tests/test_compiler_detection.py +1 -11
  145. myokit/tests/test_component.py +108 -56
  146. myokit/tests/test_config.py +34 -67
  147. myokit/tests/test_datablock.py +1 -9
  148. myokit/tests/test_datalog.py +19 -24
  149. myokit/tests/test_dependency_checking.py +8 -23
  150. myokit/tests/test_expressions.py +0 -9
  151. myokit/tests/test_float.py +1 -5
  152. myokit/tests/test_formats.py +0 -9
  153. myokit/tests/test_formats_axon.py +1 -9
  154. myokit/tests/test_formats_cellml.py +0 -15
  155. myokit/tests/test_formats_channelml.py +0 -15
  156. myokit/tests/test_formats_easyml.py +0 -14
  157. myokit/tests/test_formats_exporters.py +1 -16
  158. myokit/tests/test_formats_expression_writers.py +1 -17
  159. myokit/tests/test_formats_html.py +0 -3
  160. myokit/tests/test_formats_importers.py +1 -16
  161. myokit/tests/test_formats_mathml_content.py +0 -9
  162. myokit/tests/test_formats_mathml_presentation.py +0 -9
  163. myokit/tests/test_formats_opencl.py +0 -10
  164. myokit/tests/test_formats_sbml.py +0 -15
  165. myokit/tests/test_formats_sympy.py +0 -9
  166. myokit/tests/test_formats_wcp.py +1 -3
  167. myokit/tests/test_io.py +27 -27
  168. myokit/tests/test_jacobian_calculator.py +6 -14
  169. myokit/tests/test_jacobian_tracer.py +0 -9
  170. myokit/tests/test_lib_deps.py +0 -9
  171. myokit/tests/test_lib_guess.py +0 -9
  172. myokit/tests/test_lib_hh.py +18 -12
  173. myokit/tests/test_lib_markov.py +21 -13
  174. myokit/tests/test_lib_multi.py +0 -9
  175. myokit/tests/test_lib_plots.py +13 -8
  176. myokit/tests/test_meta.py +0 -3
  177. myokit/tests/test_model.py +390 -96
  178. myokit/tests/test_model_building.py +44 -96
  179. myokit/tests/test_opencl_info.py +5 -14
  180. myokit/tests/test_pacing_factory.py +0 -3
  181. myokit/tests/test_pacing_system_c.py +1 -23
  182. myokit/tests/test_pacing_system_py.py +0 -9
  183. myokit/tests/test_parsing.py +139 -56
  184. myokit/tests/test_progress_reporters.py +0 -3
  185. myokit/tests/test_protocol.py +0 -9
  186. myokit/tests/test_protocol_floating_point.py +1 -10
  187. myokit/tests/test_protocol_time_series.py +82 -0
  188. myokit/tests/test_pype.py +0 -9
  189. myokit/tests/test_quantity.py +0 -9
  190. myokit/tests/test_rhs_benchmarker.py +1 -9
  191. myokit/tests/test_sbml_api.py +27 -42
  192. myokit/tests/test_sbml_parser.py +4 -19
  193. myokit/tests/test_simulation_1d.py +45 -25
  194. myokit/tests/test_simulation_cvodes.py +321 -55
  195. myokit/tests/test_simulation_cvodes_from_disk.py +0 -3
  196. myokit/tests/test_simulation_fiber_tissue.py +39 -12
  197. myokit/tests/test_simulation_log_interval.py +1 -431
  198. myokit/tests/test_simulation_opencl.py +69 -48
  199. myokit/tests/test_simulation_opencl_log_interval.py +1 -3
  200. myokit/tests/test_simulation_opencl_vs_cvode.py +1 -10
  201. myokit/tests/test_simulation_opencl_vs_sim1d.py +1 -10
  202. myokit/tests/test_system_info.py +1 -11
  203. myokit/tests/test_tools.py +0 -9
  204. myokit/tests/test_unit.py +1 -10
  205. myokit/tests/test_user_functions.py +0 -10
  206. myokit/tests/test_variable.py +231 -27
  207. myokit/tools.py +5 -21
  208. myokit/units.py +5 -3
  209. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/METADATA +12 -15
  210. myokit-1.35.0.dist-info/RECORD +391 -0
  211. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/WHEEL +1 -1
  212. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/entry_points.txt +0 -1
  213. myokit/_exec_new.py +0 -15
  214. myokit/_exec_old.py +0 -15
  215. myokit/_sim/cvodesim.c +0 -1551
  216. myokit/_sim/cvodesim.py +0 -674
  217. myokit/_sim/icsim.cpp +0 -563
  218. myokit/_sim/icsim.py +0 -363
  219. myokit/_sim/psim.cpp +0 -656
  220. myokit/_sim/psim.py +0 -493
  221. myokit/lib/common.py +0 -1094
  222. myokit/tests/test_lib_common.py +0 -130
  223. myokit/tests/test_simulation_cvode.py +0 -612
  224. myokit/tests/test_simulation_ic.py +0 -108
  225. myokit/tests/test_simulation_p.py +0 -223
  226. myokit-1.33.9.dist-info/RECORD +0 -403
  227. /myokit/formats/opencl/template/{test → test.sh} +0 -0
  228. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/LICENSE.txt +0 -0
  229. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/top_level.txt +0 -0
myokit/_sim/mcl.h CHANGED
@@ -67,7 +67,6 @@ static int
67
67
  mcl_flag2(const char* msg, const cl_int flag)
68
68
  {
69
69
  char sub[1024];
70
- char err[2048];
71
70
 
72
71
  if(flag == CL_SUCCESS) {
73
72
  return 0;
@@ -82,164 +81,163 @@ mcl_flag2(const char* msg, const cl_int flag)
82
81
  switch(flag) {
83
82
  // OpenCL 1.0 Errors
84
83
  case CL_DEVICE_NOT_FOUND:
85
- sprintf(err, "OpenCL error%s: CL_DEVICE_NOT_FOUND", sub);
84
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_DEVICE_NOT_FOUND", sub);
86
85
  break;
87
86
  case CL_DEVICE_NOT_AVAILABLE:
88
- sprintf(err, "OpenCL error%s: CL_DEVICE_NOT_AVAILABLE", sub);
87
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_DEVICE_NOT_AVAILABLE", sub);
89
88
  break;
90
89
  case CL_COMPILER_NOT_AVAILABLE:
91
- sprintf(err, "OpenCL error%s: CL_COMPILER_NOT_AVAILABLE", sub);
90
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_COMPILER_NOT_AVAILABLE", sub);
92
91
  break;
93
92
  case CL_MEM_OBJECT_ALLOCATION_FAILURE:
94
- sprintf(err, "OpenCL error%s: CL_MEM_OBJECT_ALLOCATION_FAILURE", sub);
93
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_MEM_OBJECT_ALLOCATION_FAILURE", sub);
95
94
  break;
96
95
  case CL_OUT_OF_RESOURCES:
97
- sprintf(err, "OpenCL error%s: CL_OUT_OF_RESOURCES", sub);
96
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_OUT_OF_RESOURCES", sub);
98
97
  break;
99
98
  case CL_OUT_OF_HOST_MEMORY:
100
- sprintf(err, "OpenCL error%s: CL_OUT_OF_HOST_MEMORY", sub);
99
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_OUT_OF_HOST_MEMORY", sub);
101
100
  break;
102
101
  case CL_PROFILING_INFO_NOT_AVAILABLE:
103
- sprintf(err, "OpenCL error%s: CL_PROFILING_INFO_NOT_AVAILABLE", sub);
102
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_PROFILING_INFO_NOT_AVAILABLE", sub);
104
103
  break;
105
104
  case CL_MEM_COPY_OVERLAP:
106
- sprintf(err, "OpenCL error%s: CL_MEM_COPY_OVERLAP", sub);
105
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_MEM_COPY_OVERLAP", sub);
107
106
  break;
108
107
  case CL_IMAGE_FORMAT_MISMATCH:
109
- sprintf(err, "OpenCL error%s: CL_IMAGE_FORMAT_MISMATCH", sub);
108
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_IMAGE_FORMAT_MISMATCH", sub);
110
109
  break;
111
110
  case CL_IMAGE_FORMAT_NOT_SUPPORTED:
112
- sprintf(err, "OpenCL error%s: CL_IMAGE_FORMAT_NOT_SUPPORTED", sub);
111
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_IMAGE_FORMAT_NOT_SUPPORTED", sub);
113
112
  break;
114
113
  case CL_BUILD_PROGRAM_FAILURE:
115
- sprintf(err, "OpenCL error%s: CL_BUILD_PROGRAM_FAILURE", sub);
114
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_BUILD_PROGRAM_FAILURE", sub);
116
115
  break;
117
116
  case CL_MAP_FAILURE:
118
- sprintf(err, "OpenCL error%s: CL_MAP_FAILURE", sub);
117
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_MAP_FAILURE", sub);
119
118
  break;
120
119
  case CL_MISALIGNED_SUB_BUFFER_OFFSET:
121
- sprintf(err, "OpenCL error%s: CL_MISALIGNED_SUB_BUFFER_OFFSET", sub);
120
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_MISALIGNED_SUB_BUFFER_OFFSET", sub);
122
121
  break;
123
122
  case CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST:
124
- sprintf(err, "OpenCL error%s: CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST", sub);
123
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST", sub);
125
124
  break;
126
125
  case CL_INVALID_VALUE:
127
- sprintf(err, "OpenCL error%s: CL_INVALID_VALUE", sub);
126
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_VALUE", sub);
128
127
  break;
129
128
  case CL_INVALID_DEVICE_TYPE:
130
- sprintf(err, "OpenCL error%s: CL_INVALID_DEVICE_TYPE", sub);
129
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_DEVICE_TYPE", sub);
131
130
  break;
132
131
  case CL_INVALID_PLATFORM:
133
- sprintf(err, "OpenCL error%s: CL_INVALID_PLATFORM", sub);
132
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_PLATFORM", sub);
134
133
  break;
135
134
  case CL_INVALID_DEVICE:
136
- sprintf(err, "OpenCL error%s: CL_INVALID_DEVICE", sub);
135
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_DEVICE", sub);
137
136
  break;
138
137
  case CL_INVALID_CONTEXT:
139
- sprintf(err, "OpenCL error%s: CL_INVALID_CONTEXT", sub);
138
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_CONTEXT", sub);
140
139
  break;
141
140
  case CL_INVALID_QUEUE_PROPERTIES:
142
- sprintf(err, "OpenCL error%s: CL_INVALID_QUEUE_PROPERTIES", sub);
141
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_QUEUE_PROPERTIES", sub);
143
142
  break;
144
143
  case CL_INVALID_COMMAND_QUEUE:
145
- sprintf(err, "OpenCL error%s: CL_INVALID_COMMAND_QUEUE", sub);
144
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_COMMAND_QUEUE", sub);
146
145
  break;
147
146
  case CL_INVALID_HOST_PTR:
148
- sprintf(err, "OpenCL error%s: CL_INVALID_HOST_PTR", sub);
147
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_HOST_PTR", sub);
149
148
  break;
150
149
  case CL_INVALID_MEM_OBJECT:
151
- sprintf(err, "OpenCL error%s: CL_INVALID_MEM_OBJECT", sub);
150
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_MEM_OBJECT", sub);
152
151
  break;
153
152
  case CL_INVALID_IMAGE_FORMAT_DESCRIPTOR:
154
- sprintf(err, "OpenCL error%s: CL_INVALID_IMAGE_FORMAT_DESCRIPTOR", sub);
153
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_IMAGE_FORMAT_DESCRIPTOR", sub);
155
154
  break;
156
155
  case CL_INVALID_IMAGE_SIZE:
157
- sprintf(err, "OpenCL error%s: CL_INVALID_IMAGE_SIZE", sub);
156
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_IMAGE_SIZE", sub);
158
157
  break;
159
158
  case CL_INVALID_SAMPLER:
160
- sprintf(err, "OpenCL error%s: CL_INVALID_SAMPLER", sub);
159
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_SAMPLER", sub);
161
160
  break;
162
161
  case CL_INVALID_BINARY:
163
- sprintf(err, "OpenCL error%s: CL_INVALID_BINARY", sub);
162
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_BINARY", sub);
164
163
  break;
165
164
  case CL_INVALID_BUILD_OPTIONS:
166
- sprintf(err, "OpenCL error%s: CL_INVALID_BUILD_OPTIONS", sub);
165
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_BUILD_OPTIONS", sub);
167
166
  break;
168
167
  case CL_INVALID_PROGRAM:
169
- sprintf(err, "OpenCL error%s: CL_INVALID_PROGRAM", sub);
168
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_PROGRAM", sub);
170
169
  break;
171
170
  case CL_INVALID_PROGRAM_EXECUTABLE:
172
- sprintf(err, "OpenCL error%s: CL_INVALID_PROGRAM_EXECUTABLE", sub);
171
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_PROGRAM_EXECUTABLE", sub);
173
172
  break;
174
173
  case CL_INVALID_KERNEL_NAME:
175
- sprintf(err, "OpenCL error%s: CL_INVALID_KERNEL_NAME", sub);
174
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_KERNEL_NAME", sub);
176
175
  break;
177
176
  case CL_INVALID_KERNEL_DEFINITION:
178
- sprintf(err, "OpenCL error%s: CL_INVALID_KERNEL_DEFINITION", sub);
177
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_KERNEL_DEFINITION", sub);
179
178
  break;
180
179
  case CL_INVALID_KERNEL:
181
- sprintf(err, "OpenCL error%s: CL_INVALID_KERNEL", sub);
180
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_KERNEL", sub);
182
181
  break;
183
182
  case CL_INVALID_ARG_INDEX:
184
- sprintf(err, "OpenCL error%s: CL_INVALID_ARG_INDEX", sub);
183
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_ARG_INDEX", sub);
185
184
  break;
186
185
  case CL_INVALID_ARG_VALUE:
187
- sprintf(err, "OpenCL error%s: CL_INVALID_ARG_VALUE", sub);
186
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_ARG_VALUE", sub);
188
187
  break;
189
188
  case CL_INVALID_ARG_SIZE:
190
- sprintf(err, "OpenCL error%s: CL_INVALID_ARG_SIZE", sub);
189
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_ARG_SIZE", sub);
191
190
  break;
192
191
  case CL_INVALID_KERNEL_ARGS:
193
- sprintf(err, "OpenCL error%s: CL_INVALID_KERNEL_ARGS", sub);
192
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_KERNEL_ARGS", sub);
194
193
  break;
195
194
  case CL_INVALID_WORK_DIMENSION:
196
- sprintf(err, "OpenCL error%s: CL_INVALID_WORK_DIMENSION", sub);
195
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_WORK_DIMENSION", sub);
197
196
  break;
198
197
  case CL_INVALID_WORK_GROUP_SIZE:
199
- sprintf(err, "OpenCL error%s: CL_INVALID_WORK_GROUP_SIZE", sub);
198
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_WORK_GROUP_SIZE", sub);
200
199
  break;
201
200
  case CL_INVALID_WORK_ITEM_SIZE:
202
- sprintf(err, "OpenCL error%s: CL_INVALID_WORK_ITEM_SIZE", sub);
201
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_WORK_ITEM_SIZE", sub);
203
202
  break;
204
203
  case CL_INVALID_GLOBAL_OFFSET:
205
- sprintf(err, "OpenCL error%s: CL_INVALID_GLOBAL_OFFSET", sub);
204
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_GLOBAL_OFFSET", sub);
206
205
  break;
207
206
  case CL_INVALID_EVENT_WAIT_LIST:
208
- sprintf(err, "OpenCL error%s: CL_INVALID_EVENT_WAIT_LIST", sub);
207
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_EVENT_WAIT_LIST", sub);
209
208
  break;
210
209
  case CL_INVALID_EVENT:
211
- sprintf(err, "OpenCL error%s: CL_INVALID_EVENT", sub);
210
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_EVENT", sub);
212
211
  break;
213
212
  case CL_INVALID_OPERATION:
214
- sprintf(err, "OpenCL error%s: CL_INVALID_OPERATION", sub);
213
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_OPERATION", sub);
215
214
  break;
216
215
  case CL_INVALID_GL_OBJECT:
217
- sprintf(err, "OpenCL error%s: CL_INVALID_GL_OBJECT", sub);
216
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_GL_OBJECT", sub);
218
217
  break;
219
218
  case CL_INVALID_BUFFER_SIZE:
220
- sprintf(err, "OpenCL error%s: CL_INVALID_BUFFER_SIZE", sub);
219
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_BUFFER_SIZE", sub);
221
220
  break;
222
221
  case CL_INVALID_MIP_LEVEL:
223
- sprintf(err, "OpenCL error%s: CL_INVALID_MIP_LEVEL", sub);
222
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_MIP_LEVEL", sub);
224
223
  break;
225
224
  case CL_INVALID_GLOBAL_WORK_SIZE:
226
- sprintf(err, "OpenCL error%s: CL_INVALID_GLOBAL_WORK_SIZE", sub);
225
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_GLOBAL_WORK_SIZE", sub);
227
226
  break;
228
227
  case CL_INVALID_PROPERTY:
229
- sprintf(err, "OpenCL error%s: CL_INVALID_PROPERTY", sub);
228
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_INVALID_PROPERTY", sub);
230
229
  break;
231
230
  // OpenCL 1.1 etc. codes can not be assumed to be defined
232
231
  // Might be good to have ifdefs or something
233
232
  // OpenCL extensions
234
233
  case -1001: // CL_PLATFORM_NOT_FOUND_KHR
235
- sprintf(err, "OpenCL error%s: CL_PLATFORM_NOT_FOUND_KHR", sub);
234
+ PyErr_Format(PyExc_Exception, "OpenCL error%s: CL_PLATFORM_NOT_FOUND_KHR", sub);
236
235
  break;
237
236
  // Unknown error
238
237
  default:
239
- sprintf(err, "Unknown OpenCL error%s: %i", sub, (int)flag);
238
+ PyErr_Format(PyExc_Exception, "Unknown OpenCL error%s: %i", sub, (int)flag);
240
239
  break;
241
240
  };
242
- PyErr_SetString(PyExc_Exception, err);
243
241
  return 1;
244
242
  }
245
243
 
@@ -522,7 +520,7 @@ int mcl_platform_supports_extension(cl_platform_id platform_id, const char* exte
522
520
  *
523
521
  * Arguments:
524
522
  * cl_platform_id platform_id The id of the platform to query
525
- * char* buffer An initialised string buffer, that can be used
523
+ * char* buffer An initialized string buffer, that can be used
526
524
  * to read and write string information.
527
525
  *
528
526
  * Returns NULL and sets an error message if any exception occurs.
@@ -611,7 +609,7 @@ PyObject* mcl_info_platform_dict(cl_platform_id platform_id, size_t bufsize, cha
611
609
  * Arguments:
612
610
  * cl_device_id device_id The id of the device to query.
613
611
  * size_t bufsize Size of the string `buffer`.
614
- * char* buffer An initialised string buffer, that can be used to
612
+ * char* buffer An initialized string buffer, that can be used to
615
613
  * read and write string information.
616
614
  *
617
615
  * Returns NULL and sets an error message if any exception occurs.
myokit/_sim/opencl.py CHANGED
@@ -4,24 +4,14 @@
4
4
  # This file is part of Myokit.
5
5
  # See http://myokit.org for copyright, sharing, and licensing details.
6
6
  #
7
- from __future__ import absolute_import, division
8
- from __future__ import print_function, unicode_literals
9
-
7
+ import configparser
10
8
  import os
11
- import myokit
12
-
13
- try:
14
- # Python2
15
- from ConfigParser import ConfigParser
16
- except ImportError:
17
- # Python 3
18
- from configparser import RawConfigParser as ConfigParser
19
9
 
10
+ import myokit
20
11
 
21
12
  # Settings file
22
13
  SETTINGS_FILE = os.path.join(myokit.DIR_USER, 'preferred-opencl-device.ini')
23
14
 
24
-
25
15
  # Location of C source for OpenCL info module
26
16
  SOURCE_FILE = 'opencl.c'
27
17
 
@@ -41,7 +31,7 @@ class OpenCL(myokit.CModule):
41
31
  _message = None
42
32
 
43
33
  def __init__(self):
44
- super(OpenCL, self).__init__()
34
+ super().__init__()
45
35
  # Create back-end and cache it
46
36
  OpenCL._index += 1
47
37
  mname = 'myokit_opencl_info_' + str(OpenCL._index)
@@ -165,11 +155,8 @@ class OpenCL(myokit.CModule):
165
155
  # Read ini file
166
156
  inifile = os.path.expanduser(SETTINGS_FILE)
167
157
  if os.path.isfile(inifile):
168
- config = ConfigParser()
169
- try:
170
- config.read(inifile, encoding='ascii') # Python 3
171
- except TypeError: # pragma: no python 3 cover
172
- config.read(inifile)
158
+ config = configparser.RawConfigParser()
159
+ config.read(inifile, encoding='ascii')
173
160
 
174
161
  def get(section, option):
175
162
  x = None
@@ -205,7 +192,7 @@ class OpenCL(myokit.CModule):
205
192
  device = device.encode('ascii').decode('ascii')
206
193
 
207
194
  # Create configuration
208
- config = ConfigParser()
195
+ config = configparser.RawConfigParser()
209
196
  config.add_section('selection')
210
197
  if platform:
211
198
  config.set('selection', 'platform', platform)
@@ -271,7 +258,7 @@ class OpenCL(myokit.CModule):
271
258
  '''
272
259
 
273
260
 
274
- class OpenCLInfo(object):
261
+ class OpenCLInfo:
275
262
  """
276
263
  Represents information about the available OpenCL platforms and devices.
277
264
 
@@ -288,14 +275,18 @@ class OpenCLInfo(object):
288
275
  """
289
276
  Returns a formatted string version of this object's information.
290
277
  """
278
+ selected_platform, selected_device = myokit.OpenCL.load_selection()
291
279
  b = []
292
280
  for i, platform in enumerate(self.platforms):
293
- b.append('Platform ' + str(i))
294
- platform._format(b, pre=' ')
281
+ pname = 'Platform ' + str(i)
282
+ if platform.name == selected_platform: # pragma: no cover
283
+ pname += ' <-- Selected platform in myokit.ini'
284
+ b.append(pname)
285
+ platform._format(b, selected_platform, selected_device, pre=' ')
295
286
  return '\n'.join(b)
296
287
 
297
288
 
298
- class OpenCLPlatformInfo(object):
289
+ class OpenCLPlatformInfo:
299
290
  """
300
291
  Represents information about an OpenCL platform.
301
292
 
@@ -342,10 +333,10 @@ class OpenCLPlatformInfo(object):
342
333
  """
343
334
  b = []
344
335
  b.append('Platform: ' + self.name)
345
- self._format(b, ' ', name=False)
336
+ self._format(b, pre=' ', name=False)
346
337
  return '\n'.join(b)
347
338
 
348
- def _format(self, b, pre='', name=True):
339
+ def _format(self, b, p_selected=None, d_selected=None, pre='', name=True):
349
340
  """
350
341
  Formats the information in this object and adds it to the list ``b``.
351
342
  """
@@ -358,7 +349,10 @@ class OpenCLPlatformInfo(object):
358
349
  if self.devices is not None:
359
350
  b.append(pre + 'Devices:')
360
351
  for j, device in enumerate(self.devices):
361
- b.append(pre + ' Device ' + str(j))
352
+ dname = pre + ' Device ' + str(j)
353
+ if self.name == p_selected and device.name == d_selected: # pragma: no cover # noqa
354
+ dname += ' <-- Selected device in myokit.ini'
355
+ b.append(dname)
362
356
  device._format(b, pre + ' ')
363
357
 
364
358
  if self.device is not None:
@@ -369,7 +363,7 @@ class OpenCLPlatformInfo(object):
369
363
  return extension in self.extensions
370
364
 
371
365
 
372
- class OpenCLDeviceInfo(object):
366
+ class OpenCLDeviceInfo:
373
367
  """
374
368
  Represents information about an OpenCL device.
375
369
 
myokit/_sim/openclsim.c CHANGED
@@ -515,9 +515,7 @@ sim_init(PyObject* self, PyObject* args)
515
515
  for(i=0; i<nx * ny * n_state; i++) {
516
516
  flt = PyList_GetItem(state_in, (Py_ssize_t)i); // Don't decref!
517
517
  if(!PyFloat_Check(flt)) {
518
- char errstr[200];
519
- sprintf(errstr, "Item %u in state vector is not a float.", (unsigned int)i);
520
- PyErr_SetString(PyExc_Exception, errstr);
518
+ PyErr_Format(PyExc_Exception, "Item %u in state vector is not a float.", (unsigned int)i);
521
519
  return sim_clean();
522
520
  }
523
521
  rvec_state[i] = (Real)PyFloat_AsDouble(flt);
@@ -552,9 +550,7 @@ sim_init(PyObject* self, PyObject* args)
552
550
  for(i=0; i<n_field_data; i++) {
553
551
  flt = PyList_GetItem(field_data, (Py_ssize_t)i); // No need to decref
554
552
  if(!PyFloat_Check(flt)) {
555
- char errstr[200];
556
- sprintf(errstr, "Item %u in field data is not a float.", (unsigned int)i);
557
- PyErr_SetString(PyExc_Exception, errstr);
553
+ PyErr_Format(PyExc_Exception, "Item %u in field data is not a float.", (unsigned int)i);
558
554
  return sim_clean();
559
555
  }
560
556
  rvec_field_data[i] = (Real)PyFloat_AsDouble(flt);
@@ -975,7 +971,7 @@ for var in model.states():
975
971
  print(3*tab + 'sprintf(log_var_name, "%u.' + var.qname() + '", (unsigned int)j);')
976
972
  else:
977
973
  print(3*tab + 'sprintf(log_var_name, "%u.%u.' + var.qname() + '", (unsigned int)j, (unsigned int)i);' )
978
- print(3*tab + 'if(log_add(log_dict, logs, vars, k_vars, log_var_name, &rvec_state[(i*nx+j)*n_state+' + str(var.indice()) + '])) {')
974
+ print(3*tab + 'if(log_add(log_dict, logs, vars, k_vars, log_var_name, &rvec_state[(i*nx+j)*n_state+' + str(var.index()) + '])) {')
979
975
  print(4*tab + 'logging_states = 1;')
980
976
  print(4*tab + 'k_vars++;')
981
977
  print(3*tab + '}')
myokit/_sim/openclsim.cl CHANGED
@@ -152,8 +152,8 @@ if precision == myokit.DOUBLE_PRECISION:
152
152
 
153
153
  <?
154
154
  if diffusion:
155
- print('/* Indice of membrane potential in state vector */')
156
- print('#define i_vm ' + str(model.label('membrane_potential').indice()))
155
+ print('/* Index of membrane potential in state vector */')
156
+ print('#define i_vm ' + str(model.label('membrane_potential').index()))
157
157
 
158
158
  if precision == myokit.SINGLE_PRECISION:
159
159
  print('/* Using single precision floats */')
@@ -188,7 +188,7 @@ for group in equations.values():
188
188
  print('')
189
189
  print('/* Aliases of state variables. */')
190
190
  for var in model.states():
191
- print('#define ' + v(var) + ' state[of1 + ' + str(var.indice()) + ']')
191
+ print('#define ' + v(var) + ' state[of1 + ' + str(var.index()) + ']')
192
192
 
193
193
  print('')
194
194
  print('/* Aliases of logged intermediary variables. */')
myokit/_sim/openclsim.py CHANGED
@@ -5,15 +5,15 @@
5
5
  # This file is part of Myokit.
6
6
  # See http://myokit.org for copyright, sharing, and licensing details.
7
7
  #
8
- from __future__ import absolute_import, division
9
- from __future__ import print_function, unicode_literals
10
-
11
8
  import os
12
- import myokit
13
- import numpy as np
14
9
  import platform
10
+
15
11
  from collections import OrderedDict
16
12
 
13
+ import numpy as np
14
+
15
+ import myokit
16
+
17
17
 
18
18
  # Location of C and OpenCL sources
19
19
  SOURCE_FILE = 'openclsim.c'
@@ -58,7 +58,7 @@ class SimulationOpenCL(myokit.CModule):
58
58
  ``pace``
59
59
  The pacing level, this is set if a protocol was passed in.
60
60
  ``diffusion_current`` (if enabled)
61
- The current flowing from the cell to its neighbours. This will be
61
+ The current flowing from the cell to its neighbors. This will be
62
62
  positive when the cell is acting as a source, negative when it is
63
63
  acting as a sink.
64
64
 
@@ -103,7 +103,7 @@ class SimulationOpenCL(myokit.CModule):
103
103
  cells ``i - 1`` and ``i + 1`` (except at the boundaries).
104
104
  Similarly, if ``ncells`` is a 2-dimensional tuple ``(nx, ny)`` a grid will
105
105
  be assumed so that each (non-boundary) cell is connected to four
106
- neighbours.
106
+ neighbors.
107
107
  Finally, arbitrary geometries can be used by passing a scalar to ``ncells``
108
108
  and specifying the connections with :meth:`set_connections`.
109
109
 
@@ -148,7 +148,7 @@ class SimulationOpenCL(myokit.CModule):
148
148
  def __init__(
149
149
  self, model, protocol=None, ncells=256, diffusion=True,
150
150
  precision=myokit.SINGLE_PRECISION, native_maths=False, rl=False):
151
- super(SimulationOpenCL, self).__init__()
151
+ super().__init__()
152
152
 
153
153
  # Require a valid model
154
154
  if not model.is_valid():
@@ -272,7 +272,7 @@ class SimulationOpenCL(myokit.CModule):
272
272
  self._nstate = self._model.count_states()
273
273
 
274
274
  # Set state and default state
275
- self._state = self._model.state() * self._ntotal
275
+ self._state = self._model.initial_values(True) * self._ntotal
276
276
  self._default_state = list(self._state)
277
277
 
278
278
  # List of globally logged inputs
@@ -283,7 +283,7 @@ class SimulationOpenCL(myokit.CModule):
283
283
  inputs = {'time': 'time', 'pace': 'pace'}
284
284
  if self._diffusion_enabled:
285
285
  inputs['diffusion_current'] = 'idiff'
286
- self._bound_variables = self._model.prepare_bindings(inputs)
286
+ self._bound_variables = myokit._prepare_bindings(self._model, inputs)
287
287
 
288
288
  # Create unique names
289
289
  self._model.create_unique_names()
@@ -657,7 +657,7 @@ class SimulationOpenCL(myokit.CModule):
657
657
 
658
658
  # Get value causing error
659
659
  if var.is_state():
660
- value = states[1 if ifirst > 0 else 0][var.indice()]
660
+ value = states[1 if ifirst > 0 else 0][var.index()]
661
661
  else: # pragma: no cover
662
662
  value = bounds[1 if ifirst > 0 else 0][var.qname()]
663
663
  var = var.qname()
@@ -765,9 +765,9 @@ class SimulationOpenCL(myokit.CModule):
765
765
 
766
766
  I_diff[i] = sum[g[i,j] * (V[i] - V[j])]
767
767
 
768
- in which the sum is over all neighbours ``j`` of cell ``i``.
768
+ in which the sum is over all neighbors ``j`` of cell ``i``.
769
769
 
770
- Alternatively, with capacitance and currents normalised to membrane
770
+ Alternatively, with capacitance and currents normalized to membrane
771
771
  area, we can write::
772
772
 
773
773
  dV/dt = -1/cm (i_ion + i_stim + i_diff)
@@ -787,10 +787,10 @@ class SimulationOpenCL(myokit.CModule):
787
787
 
788
788
  g_bar = (1 / chi) * (k / (k + 1)) * D * (1 / dx^2)
789
789
 
790
- where ``g_bar`` is the cell-to-cell conductance, but normalised with
790
+ where ``g_bar`` is the cell-to-cell conductance, but normalized with
791
791
  respect to unit membrane area.
792
- For models with currents normalised to area this is unproblematic, but
793
- to convert to models with unnormalised currents this means we have
792
+ For models with currents normalized to area this is unproblematic, but
793
+ to convert to models with unnormalized currents this means we have
794
794
  added the further assumption that **each node contains some fixed
795
795
  amount of membrane**, determined by an area A::
796
796
 
@@ -825,9 +825,9 @@ class SimulationOpenCL(myokit.CModule):
825
825
  """
826
826
  return D * A * k / ((k + 1) * chi * dx * dx)
827
827
 
828
- def neighbours(self, x, y=None):
828
+ def neighbors(self, x, y=None):
829
829
  """
830
- Returns a list of indices specifying the neighbours of the cell at
830
+ Returns a list of indices specifying the neighbors of the cell at
831
831
  index ``x`` (or index ``(x, y)`` for 2d simulations).
832
832
 
833
833
  Indices are given either as integers (1d or arbitrary geometry) or as
@@ -857,29 +857,38 @@ class SimulationOpenCL(myokit.CModule):
857
857
 
858
858
  # User-specified connections (always 1d)
859
859
  if self._connections is not None:
860
- neighbours = []
860
+ neighbors = []
861
861
  for i, j, c in self._connections:
862
862
  if i == x:
863
- neighbours.append(j)
863
+ neighbors.append(j)
864
864
  elif j == x:
865
- neighbours.append(i)
866
- return neighbours
865
+ neighbors.append(i)
866
+ return neighbors
867
867
 
868
- # Left and right neighbours
869
- neighbours = []
868
+ # Left and right neighbors
869
+ neighbors = []
870
870
  if x > 0:
871
- neighbours.append(x - 1)
871
+ neighbors.append(x - 1)
872
872
  if x + 1 < self._dims[0]:
873
- neighbours.append(x + 1)
873
+ neighbors.append(x + 1)
874
874
 
875
- # Top and bottom neighbours
875
+ # Top and bottom neighbors
876
876
  if len(self._dims) == 2:
877
- neighbours = [(i, y) for i in neighbours]
877
+ neighbors = [(i, y) for i in neighbors]
878
878
  if y > 0:
879
- neighbours.append((x, y - 1))
879
+ neighbors.append((x, y - 1))
880
880
  if y + 1 < self._dims[1]:
881
- neighbours.append((x, y + 1))
882
- return neighbours
881
+ neighbors.append((x, y + 1))
882
+ return neighbors
883
+
884
+ def neighbours(self, x, y=None):
885
+ """ Alias of :meth:`neighbors`. """
886
+ # Deprecated since 2023-06-07
887
+ import warnings
888
+ warnings.warn('The method SimulationOpenCL.neighbours() is deprecated.'
889
+ ' Please use neighbors() instead.')
890
+
891
+ return self.neighbors(x, y)
883
892
 
884
893
  def pre(self, duration, report_nan=True, progress=None,
885
894
  msg='Pre-pacing SimulationOpenCL'):
@@ -1165,14 +1174,14 @@ class SimulationOpenCL(myokit.CModule):
1165
1174
  vdiff = self._model.binding('diffusion_current')
1166
1175
  vdiff = None if vdiff is None else vdiff.qname()
1167
1176
 
1168
- # List all neighbours for this cell
1169
- neighbours = self.neighbours(*icell)
1177
+ # List all neighbors for this cell
1178
+ neighbors = self.neighbors(*icell)
1170
1179
  if len(self._dims) == 2:
1171
- neighbours_str = ', '.join(
1180
+ neighbors_str = ', '.join(
1172
1181
  ('(' + ','.join([str(i) for i in j]) + ')')
1173
- for j in neighbours)
1182
+ for j in neighbors)
1174
1183
  else:
1175
- neighbours_str = ', '.join(str(j) for j in neighbours)
1184
+ neighbors_str = ', '.join(str(j) for j in neighbors)
1176
1185
 
1177
1186
  # Show final state
1178
1187
  txt.append('State during:')
@@ -1192,8 +1201,8 @@ class SimulationOpenCL(myokit.CModule):
1192
1201
  txt.append(
1193
1202
  ' Diffusion current: ' + myokit.float.str(
1194
1203
  bounds[0][vdiff], precision=self._precision))
1195
- if neighbours:
1196
- txt.append(' Connected cells: ' + neighbours_str)
1204
+ if neighbors:
1205
+ txt.append(' Connected cells: ' + neighbors_str)
1197
1206
 
1198
1207
  # Show previous state (and derivatives)
1199
1208
  n_states = len(states)
@@ -1239,8 +1248,8 @@ class SimulationOpenCL(myokit.CModule):
1239
1248
  txt.append(
1240
1249
  ' Diffusion current: ' + myokit.float.str(
1241
1250
  bounds[1][vdiff], precision=self._precision))
1242
- if neighbours:
1243
- txt.append(' Connected cells: ' + neighbours_str)
1251
+ if neighbors:
1252
+ txt.append(' Connected cells: ' + neighbors_str)
1244
1253
 
1245
1254
  # Show all variables with non-finite values
1246
1255
  txt.append(