openCARP
Doxygen code documentation for the open cardiac electrophysiology simulator openCARP
ION_IF.cc
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // openCARP is an open cardiac electrophysiology simulator.
3 //
4 // Copyright (C) 2020 openCARP project
5 //
6 // This program is licensed under the openCARP Academic Public License (APL)
7 // v1.0: You can use and redistribute it and/or modify it in non-commercial
8 // academic environments under the terms of APL as published by the openCARP
9 // project v1.0, or (at your option) any later version. Commercial use requires
10 // a commercial license (info@opencarp.org).
11 //
12 // This program is distributed without any warranty; see the openCARP APL for
13 // more details.
14 //
15 // You should have received a copy of the openCARP APL along with this program
16 // and can find it online: http://www.opencarp.org/license
17 // ----------------------------------------------------------------------------
18 
28 /*
29  * Specify an interface for an IMP
30  *
31  * To use an IMP by itself outside of MULTI_IF.h, one must \n
32  * -# alloc_IIF()
33  * -# set the rdata pointers in the ::IMPDataStruct
34  * -# initialize_params()
35  * -# initialize_IIF()
36  * -# call the compute() method of the IMP
37  *
38  * The IMP library tries to avoid undo computation by using lookup
39  * tables to avoid computation of costly mathematical functions.
40  * Tables indices may be computed based on vltage, calcium concentrations,
41  * or any other quantity.
42  *
43  * One common scenario is commonly encountered with first-order differential
44  * equations:
45  * \f[ \frac{dz}{dt} = \frac{z_{\infty}(x) - z}{\tau_z(x)} \f]
46  *
47  * Using an exponential solution assuming that
48  * \f$z_{\inf}(x)\f$ and \f$\tau_z(x)\f$ constant over a time step:
49  *
50  * \f[ \begin{split}
51  * z_{i+1} & = z_{\infty}(x) - (z_{\infty}(x)-z_i)
52  * \exp\Large (-\Delta t/\tau_z(x) \Large ) \\
53  * & = A(x) + B(x) z_i
54  * \end{split}\f]
55  * where A(x) and B(x) are entered into a look up table which is
56  * indexed by \em x:
57  * \f[ \begin{split}
58  * A(x) &= z_{\infty}(x)\Large ( 1 - \exp(-\Delta t/\tau_z(x)\Large ) \\
59  * &= -z_{\infty}(x) expm1\Large ( -\Delta t/\tau_z(x) \Large ) \\
60  * B(x) &= \exp(-\Delta t/\tau_z(x))
61  * \end{split}\f]
62  *
63  */
64 #include "limpet_types.h"
65 #include "ION_IF.h"
66 #include <stdarg.h>
67 
68 namespace limpet {
69 
73 using ::opencarp::Salt_list;
74 
75 #ifdef USE_HDF5
76 FILE_SPEC _nc_logf = (FILE_SPEC)calloc(1, sizeof(fileptr));
77 #else
79 #endif
80 
81 
82 char* tokstr_r(char *s1, const char *s2, char **lasts)
83 {
84  char *ret;
85 
86  if (s1 == NULL)
87  s1 = *lasts;
88  while(*s1 && strchr(s2, *s1))
89  ++s1;
90  if(*s1 == '\0')
91  return NULL;
92  ret = s1;
93  while(*s1 && !strchr(s2, *s1))
94  ++s1;
95  if(*s1)
96  *s1++ = '\0';
97  *lasts = s1;
98  return ret;
99 }
100 
101 
102 #ifndef offsetof
103 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
104 #endif
105 
106 // struct exception_context the_exception_context[1];
107 
109 
110 IonIfBase::IonIfBase(const IonType& type, Target target, int num_node, const std::vector<std::reference_wrapper<IonType>>& plugins)
111  : _type(type), _target(type.select_target(target)), _num_node(num_node) {
112  this->_tstp = {};
113  this->_reqdat = type.reqdat();
114  this->_moddat = type.moddat();
115  this->_plugins = {};
116 
117  for (const auto& plugin : plugins) {
118  auto child = plugin.get().make_ion_if(target, num_node, {});
119  child->_parent = this;
120  this->_plugins.push_back(child);
121 
122  this->_reqdat |= child->_reqdat;
123  this->_moddat |= child->_moddat;
124  }
125 }
126 
128  for (auto plugin : this->_plugins) {
129  plugin->get_type().destroy_ion_if(plugin);
130  }
131 
132  this->_type.destroy(*this);
133  if (this->_tables_d != nullptr) {
134  deallocate_on_target<LUT>(this->_target, this->_tables_d);
135  }
136 }
137 
138 const IonType& IonIfBase::get_type() const {
139  return this->_type;
140 }
141 
143  return this->_num_node;
144 }
145 
146 std::size_t IonIfBase::get_num_threads() const {
147  std::size_t num_threads = 0;
148  switch (this->_target) {
149  // CPU target threads number depends on wether OpenMP is enabled or not
150  case Target::CPU:
151  case Target::MLIR_CPU:
152 #ifdef _OPENMP
153  num_threads = omp_get_max_threads();
154 #else
155  num_threads = 1;
156 #endif
157  break;
158  // GPU targets threads numbers is the number of cells
159  case Target::MLIR_CUDA:
160  case Target::MLIR_ROCM:
161  num_threads = this->get_num_node();
162  break;
163  default:
164  throw std::logic_error("The IonIf execution target " + std::to_string(this->_target) + " is invalid");
165  break;
166  }
167  return num_threads;
168 }
169 
171  return this->_parent;
172 }
173 
175  this->_parent = parent;
176 }
177 
178 std::vector<IonIfBase*>& IonIfBase::plugins() {
179  return this->_plugins;
180 }
181 
182 uint32_t IonIfBase::get_reqdat() const {
183  return this->_reqdat;
184 }
185 
186 uint32_t IonIfBase::get_moddat() const {
187  return this->_moddat;
188 }
189 
190 void IonIfBase::set_moddat(uint32_t data) {
191  this->_moddat = data;
192 }
193 
194 float IonIfBase::get_dt() const {
195  return this->dt;
196 }
197 
198 void IonIfBase::set_dt(float dt) {
199  this->dt = dt;
200 }
201 
203  return this->_tstp;
204 }
205 
206 std::vector<LUT>& IonIfBase::tables() {
207  return this->_tables;
208 }
209 
211  return this->_n_tables_d;
212 }
213 
215  if (is_concrete(this->get_type().select_target(target))) {
216  this->_target = target;
217  }
218  else {
219  throw std::invalid_argument("new target set for IMP is unavailable or not concrete (AUTO, UNKNWOWN, ...)");
220  }
221 }
222 
224  this->_type.initialize_params(*this);
225 
226  for (auto& plugin : this->_plugins) {
227  plugin->initialize_params();
228  }
229 }
230 
231 void IonIfBase::initialize(double dt, GlobalData_t **impdat) {
238  this->dt = dt;
239 
240  if (this->_num_node) {
241  this->_type.construct_tables(*this);
242  // If this is a GPU target, we need to copy the LUT definitions to the
243  // device
244  if (is_gpu(this->get_target()) && this->tables().size() > 0) {
245  // Allocate on device and copy
246  this->_tables_d = allocate_on_target<LUT>(this->get_target(), this->tables().size());
247  // TODO replace this with some kind of memcpy (needs to be adjusted
248  // depending on CUDA / HIP
249  for (size_t i = 0; i < this->tables().size(); ++i) {
250  this->_tables_d[i] = this->tables()[i];
251  }
252  this->_n_tables_d = this->tables().size();
253  }
254  }
255 
256  this->_type.initialize_sv(*this, impdat);
257  this->ldata = impdat;
258 
259  for (auto& plugin : this->_plugins) {
260  plugin->initialize(dt, impdat);
261  }
262 }
263 
264 void IonIfBase::compute(int start, int end, GlobalData_t **data) {
265  this->_type.compute(this->_target, start, end, *this, data);
266 }
267 
268 char* IonIfBase::fill_buf(char *buf, int* n, opencarp::Salt_list *l) const {
269  int iif_sz = this->get_sv_size();
270 
271  for (auto& plugin : this->_plugins) {
272  iif_sz += plugin->get_sv_size();
273  }
274 
275  buf = (char *) realloc(buf, *n + l->nitems*iif_sz) + *n;
276  *n += l->nitems * iif_sz;
277 
278  return buf - *n + l->nitems * iif_sz;
279 }
280 
281 int IonIfBase::restore(FILE_SPEC in, int n, const int *pos, IIF_Mask_t *mask,
282  size_t *offset, IMPinfo *impinfo, const int* loc2canon) {
283  if( !impinfo->compatible )
284  return n;
285 
286  char *ptr = (char *)(this->get_sv_address());
287  long base = ftell(in->fd);
288  int mismatch = 0;
289 
290  // Restoration of the model data need to take care of the data layout
291  // optimization. (if it is disabled, the vector size should be 1 anyways
292  std::size_t vec_size = this->get_type().dlo_vector_size();
293  for( int i=0; i<n; i+=vec_size ) {
294  int index = i / vec_size;
295  int canon = loc2canon[pos[i]];
296  if(mask[canon] == this->miifIdx) {
297  fseek(in->fd, base+offset[canon], SEEK_SET);
298  fread(ptr+index*impinfo->sz, impinfo->sz, 1, in->fd);
299 
300  for(int j=0; j<impinfo->nplug; j++) {
301  if(impinfo->plug[j].compatible) {
302  fread((char*)(this->_plugins[impinfo->plug[j].map]->get_sv_address())+index*impinfo->plug[j].sz,
303  impinfo->plug[j].sz, 1, in->fd);
304  }
305  else
306  fseek(in->fd, impinfo->plug[j].sz, SEEK_CUR);
307  }
308  }
309  else
310  mismatch++;
311  }
312  return mismatch;
313 }
314 
315 int IonIfBase::dump_luts(bool zipped) {
316  std::string name;
317  std::string ext = zipped ? ".gz" : "";
318 
319  int dcnt = 0;
320  for (int i=0; i < this->_tables.size(); i++) {
321  // determine file name
322  if (strcmp(this->_tables[i].name, ""))
323  name = this->_type.get_name() + "_LUT_" + this->_tables[i].name + ".bin" + ext;
324  else
325  name = this->_type.get_name() + "_LUT_" + std::to_string(i) + ".bin" + ext;
326 
327  // dump table
328  int err = LUT_dump(&this->_tables[i], name.c_str());
329  if (!err) dcnt++;
330  }
331  return dcnt;
332 }
333 
335  for (auto& lut : this->_tables) {
336  destroy_lut(&lut, this->_target);
337  }
338 
339  std::vector<LUT>().swap(this->_tables);
340 }
341 
342 void IonIfBase::tune(const char *im_par, const char *plugs, const char *plug_par) {
343  char *opar, *oplg, *plg, *nplg, *parlst, *nparlst;
344 
345  if( im_par && *im_par != '\0' ) {
346  log_msg( _nc_logf, 0, 0, "Ionic model: %s", this->_type.get_name().c_str());
347  this->_type.tune(*this, im_par);
348  }
349  opar = parlst = dupstr(plug_par);
350  oplg = plg = dupstr(plugs);
351 
352  while( plg!=NULL && *plg!='\0' ) {
353  nparlst = get_next_list( parlst, ':' );
354  nplg = get_next_list( plg, ':' );
355 
356  log_msg( _nc_logf, 0, 0, "Plug-in: %s", plg );
357 
358  if( parlst==NULL || *parlst=='\0' ) {
359  parlst = nparlst;
360  plg = nplg;
361  continue;
362  }
363 
364  // find apropriate plugin
365  IonType* plugin = get_ion_type(plg);
366  int j;
367 
368  for(j = 0; j < this->_plugins.size(); j++) {
369  if(this->_plugins[j]->get_type() == *plugin) break;
370  }
371 
372  if(j == this->_plugins.size()) {
373  log_msg( _nc_logf, 2, 0, "Plugin %s not used with IM", plg );
374  plg = nplg;
375  parlst = nparlst;
376  continue;
377  }
378 
379  plugin->tune(*this->_plugins[j], parlst);
380  plg = nplg;
381  parlst = nparlst;
382  }
383 
384  log_msg( _nc_logf, 0, 0, "" );
385  free( opar );
386  free( oplg );
387 }
388 
389 int IonIfBase::read_svs(FILE* file) {
390  if(this->get_num_node())
391  return this->_type.read_svs(*this, file);
392  else
393  return 1;
394 }
395 
396 int IonIfBase::write_svs(FILE* file, int node) {
397  return this->_type.write_svs(*this, file, node);
398 }
399 
401  this->_plugins = {};
402 
403  for (auto& plugin : other.plugins()) {
404  auto copy = plugin->get_type().make_ion_if(this->_target, plugin->get_num_node(), {});
405  copy->_parent = this;
406  this->_plugins.push_back(copy);
407  copy->copy_SVs_from(*plugin, true);
408  }
409 }
410 
411 void IonIfBase::for_each(const std::function<void(IonIfBase&)>& consumer) {
412  consumer(*this);
413 
414  for (auto& plugin : this->_plugins) {
415  consumer(*plugin);
416  }
417 }
418 
430 // Is this ever used?
431 void initialize_ts(Target target, ts *tstp, int ng, int *skp, double dt)
432 {
433  // initialize step counter that will run linearly as long the
434  // simulation goes
435  tstp->cnt = -1;
436  tstp->ng = ng;
437  tstp->tcg = allocate_on_target<tc_grp>(target, ng);
438 
439  // initialize time constant grouping
440  // fast variables, use dt=dt
441  tstp->tcg[0].skp = 1;
442  tstp->tcg[0].dt = dt;
443  tstp->tcg[0].update = 1;
444 
445  for (int i=1;i<ng;i++) {
446  tstp->tcg[i].skp = skp[i];
447  tstp->tcg[i].dt = tstp->tcg[i].skp*dt;
448  }
449 }
450 
451 
460 void update_ts(ts *ptstp)
461 {
462  ptstp->cnt++;
463 
464  tc_grp *ptcg = ptstp->tcg;
465  for (int i=1;i<ptstp->ng;i++)
466  ptcg[i].update = !(ptstp->cnt%ptcg[i].skp);
467 }
468 
469 
470 #ifndef _GNU_SOURCE
480 void *memmem( void *haystack, int sz_hay, void *needle, int sz_n )
481 {
482  if( !sz_n ) return NULL;
483 
484  char *h = (char *)haystack;
485 
486  for( int i=0; i>sz_hay-sz_n+1; i++, h++ )
487  if( !memcmp( h, needle, sz_n ) )
488  return (void *)h;
489  break;
490 
491  return NULL;
492 }
493 #endif
494 
495 
506 char *get_next_list( char *lst, char delimiter )
507 {
508  if( lst == NULL || *lst == '\0' )
509  return NULL;
510 
511  while( *lst != delimiter && *lst != '\0' )
512  lst++;
513 
514  if( *lst != '\0' ) {
515  *lst = '\0';
516  return lst+1;
517  } else
518  return lst;
519 }
520 
521 
529  bool verify_flags( const char *flags, const char* given )
530  {
531  char *flag, *ptr, *gvn_cp = dupstr( given );
532 
533  flag = tokstr_r( gvn_cp, "|", &ptr );
534  while( flag ) {
535  if( !flag_set( flags, flag ) )
536  break;
537  flag = tokstr_r( NULL, "|", &ptr );
538  }
539 
540  free( gvn_cp );
541  return flag ? false : true;
542 }
543 
551 bool flag_set( const char *flags, const char *target )
552 {
553  if( !flags ) return false;
554 
555  char *f = dupstr( flags ), *last, *pos;
556 
557  pos = tokstr_r( f, "|", &last );
558  while( pos && strcmp( pos, target ) ) {
559  pos = tokstr_r( NULL, "|", &last );
560  }
561  free( f );
562 
563  return pos? true : false;
564 }
565 
566 
567 #ifdef USE_CVODE
568 #include <nvector/nvector_serial.h>
569 #include <cvode/cvode.h>
570 #include <cvode/cvode_diag.h>
571 
572 
575 void __bogus_function_for_cvode() {
576 
577  int flag;
578  int N_CVODE = 1;
579 #if SUNDIALS_VERSION_MAJOR >= 4
580  void* cvode_mem = CVodeCreate(CV_BDF);
581 #else
582  void* cvode_mem = CVodeCreate(CV_BDF, CV_NEWTON);
583 #endif
584  assert(cvode_mem != NULL);
585  N_Vector cvode_y = N_VNew_Serial(N_CVODE);
586  flag = CVodeInit(cvode_mem, NULL, 0, cvode_y);
587  assert(flag == CV_SUCCESS);
588  N_VDestroy(cvode_y);
589 
590  flag = CVodeSStolerances(cvode_mem, 1e-5, 1e-6);
591  assert(flag == CV_SUCCESS);
592  flag = CVodeSetMaxStep(cvode_mem, 1);
593  assert(flag == CV_SUCCESS);
594  flag = CVodeSetUserData(cvode_mem, NULL);
595  assert(flag == CV_SUCCESS);
596  flag = CVDiag(cvode_mem);
597  assert(flag == CV_SUCCESS);
598 
599  NV_Ith_S(cvode_y,0) = 0;
600 
601  {
602  int CVODE_flag;
603  CVODE_flag = CVodeReInit(NULL, 1, NULL);
604  assert(CVODE_flag == CV_SUCCESS);
605  CVODE_flag = CVodeSetInitStep(NULL, 1);
606  assert(CVODE_flag == CV_SUCCESS);
607  realtype tret;
608  CVODE_flag = CVode(cvode_mem, 1, NULL, &tret, CV_NORMAL);
609  assert(CVODE_flag == CV_SUCCESS);
610  }
611 }
612 #endif
613 
614 } // namespace limpet
Represents the ionic model and plug-in (IMP) data structure.
Definition: ION_IF.h:138
virtual void initialize(double dt, GlobalData_t **impdat)
Initializes lookup table and state variable tables.
Definition: ION_IF.cc:231
void tune(const char *im_par, const char *plugs, const char *plug_par)
Tunes specific IMP parameters from files.
Definition: ION_IF.cc:342
Target _target
execution target for this IMP
Definition: ION_IF.h:151
void set_parent(IonIfBase *parent)
Definition: ION_IF.cc:174
IonIfBase(const IonType &type, Target target, int num_node, const std::vector< std::reference_wrapper< IonType >> &plugins)
Constructor for IonIfBase.
Definition: ION_IF.cc:110
virtual void set_target(Target target)
Definition: ION_IF.cc:214
const IonType & get_type() const
Gets this IMP's model type.
Definition: ION_IF.cc:138
int restore(opencarp::FILE_SPEC in, int n, const int *pos, IIF_Mask_t *mask, size_t *offset, IMPinfo *impinfo, const int *loc2canon)
Reads in the state variables for an IMP.
Definition: ION_IF.cc:281
char * fill_buf(char *buf, int *n, opencarp::Salt_list *l) const
Appends the state variables to a buffer.
Definition: ION_IF.cc:268
float get_dt() const
Gets the basic integration time step.
Definition: ION_IF.cc:194
int read_svs(FILE *file)
Reads state variable values for one cell from a file.
Definition: ION_IF.cc:389
std::vector< LUT > & tables()
Gets the array of state variables.
Definition: ION_IF.cc:206
ts _tstp
control time stepping
Definition: ION_IF.h:153
std::vector< IonIfBase * > & plugins()
Returns a vector containing the plugins of this IMP.
Definition: ION_IF.cc:178
int dump_luts(bool zipped)
Dumps array of LUTs to file.
Definition: ION_IF.cc:315
int write_svs(FILE *file, int node)
Definition: ION_IF.cc:396
virtual ~IonIfBase()
Virtual destructor declaration.
Definition: ION_IF.cc:127
void set_moddat(uint32_t data)
Set the data flag for this IMP's modified data.
Definition: ION_IF.cc:190
void set_dt(float dt)
Sets the basic integration time step.
Definition: ION_IF.cc:198
ts & get_tstp()
Gets the time stepper.
Definition: ION_IF.cc:202
void for_each(const std::function< void(IonIfBase &)> &consumer)
Executes the consumer functions on this IMP and each of its plugins.
Definition: ION_IF.cc:411
size_t get_n_tables_d() const
Gets the size of the array returned by IonIf::tables_d.
Definition: ION_IF.cc:210
IonIfBase * parent() const
Gets the parent IMP.
Definition: ION_IF.cc:170
void destroy_luts()
Destroys array of LUTs.
Definition: ION_IF.cc:334
Target get_target() const
Definition: ION_IF.h:338
int get_num_node() const
Gets the number of nodes handled by this IMP.
Definition: ION_IF.cc:142
uint32_t get_moddat() const
Gets the data flags for this IMP's modified data.
Definition: ION_IF.cc:186
int miifIdx
imp index within miif
Definition: ION_IF.h:150
virtual std::size_t get_sv_size() const =0
Gets the size of the structure this IMP uses for state variables.
void copy_plugins_from(IonIfBase &other)
Copies the plugins of an IMP.
Definition: ION_IF.cc:400
virtual void * get_sv_address()=0
Gets the raw address of the state variables for this IMP.
void compute(int start, int end, GlobalData_t **data)
Perform ionic model computation for 1 time step.
Definition: ION_IF.cc:264
void initialize_params()
Initializes user modifiable parameters with default values defined in the respective ionic models.
Definition: ION_IF.cc:223
uint32_t get_reqdat() const
Gets the data flags for this IMP's required data.
Definition: ION_IF.cc:182
std::size_t get_num_threads() const
Gets the number of threads used for running this IMP.
Definition: ION_IF.cc:146
Abstract class representing an ionic model type.
Definition: ion_type.h:59
const std::string & get_name() const
Gets the model name.
Definition: ion_type.cc:23
virtual void initialize_params(IonIfBase &imp) const =0
Initializes the parameters in the given IMP.
virtual void compute(Target target, int start, int end, IonIfBase &imp, GlobalData_t **data) const =0
Performs computation for 1 time step.
virtual uint32_t moddat() const =0
Gets data flags for this IMP's modified data.
virtual int read_svs(IonIfBase &imp, FILE *file) const =0
Reads state variable values for one cell from a file.
virtual void tune(IonIfBase &imp, const char *im_par) const =0
Handles setting of this model's parameters.
virtual void initialize_sv(IonIfBase &imp, GlobalData_t **data) const =0
Initializes the state variables of the given IMP.
virtual int write_svs(IonIfBase &imp, FILE *file, int node) const =0
Write state variable values for one cell to a file/.
virtual uint32_t reqdat() const =0
Gets data flags for this IMP's required data.
virtual void construct_tables(IonIfBase &imp) const =0
Contructs lookup tables.
virtual void destroy(IonIfBase &imp) const =0
Destroys the given IMP.
virtual size_t dlo_vector_size() const =0
Gets the vector size when using data layout optimization (DLO).
int * curr_node_list
needed to determine the global node number
Definition: ION_IF.cc:108
bool is_concrete(Target const target)
Checks if target is a real, concrete target.
Definition: target.cc:68
Target
enum that represents different targets to run ionic models on.
Definition: target.h:45
@ MLIR_ROCM
ROCM code for AMD GPUs generated with MLIR.
Definition: target.h:50
@ CPU
baseline CPU model generated with the original opencarp code generator
Definition: target.h:48
@ MLIR_CUDA
CUDA code for NVIDIA GPUs generated with MLIR.
Definition: target.h:51
@ MLIR_CPU
vectorized CPU code generated with MLIR
Definition: target.h:49
bool flag_set(const char *flags, const char *target)
Definition: ION_IF.cc:551
IonType * get_ion_type(const std::string &name)
SF_real GlobalData_t
Definition: limpet_types.h:27
int LUT_dump(LUT *plut, const char *fname)
Definition: LUT.cc:74
bool is_gpu(Target const target)
Checks if this is a GPU target.
Definition: target.cc:64
bool verify_flags(const char *flags, const char *given)
Definition: ION_IF.cc:529
FILE_SPEC _nc_logf
Definition: ION_IF.cc:78
void destroy_lut(LUT *plut, Target target)
Definition: LUT.cc:101
void update_ts(ts *ptstp)
Definition: ION_IF.cc:460
char * get_next_list(char *lst, char delimiter)
Definition: ION_IF.cc:506
char IIF_Mask_t
Definition: ion_type.h:50
void * memmem(void *haystack, int sz_hay, void *needle, int sz_n)
Definition: ION_IF.cc:480
char * tokstr_r(char *s1, const char *s2, char **lasts)
Definition: ION_IF.cc:82
void initialize_ts(Target target, ts *tstp, int ng, int *skp, double dt)
Definition: ION_IF.cc:431
char * dupstr(const char *old_str)
Definition: basics.cc:44
void log_msg(FILE_SPEC out, int level, unsigned char flag, const char *fmt,...)
Definition: basics.cc:72
file_desc * FILE_SPEC
Definition: basics.h:138
int sz
storage required
Definition: ION_IF.h:127
int map
which plugin does this IMO match
Definition: ION_IF.h:131
IMPinfo * plug
plugins
Definition: ION_IF.h:129
bool compatible
does IM match stored IM
Definition: ION_IF.h:130
int nplug
number of plugins
Definition: ION_IF.h:128
time constant groups
Definition: ION_IF.h:74
int update
Definition: ION_IF.h:78
float dt
Definition: ION_IF.h:75
time stepper
Definition: ION_IF.h:85
int cnt
Definition: ION_IF.h:86
int ng
Definition: ION_IF.h:87
tc_grp * tcg
Definition: ION_IF.h:88
saltatory list – memory is allocated in chunks
Definition: basics.h:57
int nitems
number of items
Definition: basics.h:60