src/grid/events.h
typedef struct _Event Event;
typedef int (* Expr) (int *, double *, Event *);
struct _Event {
int last, nexpr;
int (* action) (const int, const double, Event *);
Expr expr[3];
int * arrayi;
double * arrayt;
char * file;
int line;
char * name;
double t;
int i, a;
void * data;
Event * next;
};
static Event * Events = NULL; // all events
int iter = 0, inext = 0; // current step and step of next event
double t = 0, tnext = 0; // current time and time of next event
void init_events (void);
void event_register (Event event);
static void _init_solver (void);
#define INIT ev->expr[0]
#define COND ev->expr[1]
#define INC ev->expr[2]
static int END_EVENT = 1234567890;
static double TEND_EVENT = 1234567890;
static double TEPS = 1e-9;
static void event_error (Event * ev, const char * s)
{
fprintf (stderr, "%s:%d: error: %s\n", ev->file, ev->line, s);
exit (1);
}
static void init_event (Event * ev)
{
if (ev->arrayi || ev->arrayt) {
ev->i = -1; ev->t = - TEND_EVENT;
if (ev->arrayi)
ev->i = ev->arrayi[0];
else
ev->t = ev->arrayt[0];
ev->a = 1;
ev->expr[1] = NULL;
}
else {
if (ev->nexpr > 0) {
Expr init = NULL, cond = NULL, inc = NULL;
for (int j = 0; j < ev->nexpr; j++) {
int i = -123456; double t = - TEND_EVENT;
(* ev->expr[j]) (&i, &t, ev);
if (i == -123456 && t == - TEND_EVENT) {
/* nothing done to i and t: this must be the condition */
if (cond)
event_error (ev, "events can only use a single condition");
cond = ev->expr[j];
}
else {
/* this is either an initialisation or an increment */
int i1 = i; double t1 = t;
(* ev->expr[j]) (&i1, &t1, ev);
if (i1 == i && t1 == t) {
/* applying twice does not change anything: this is an
initialisation */
if (init)
event_error (ev, "events can only use a single initialisation");
init = ev->expr[j];
}
else {
/* this is the increment */
if (inc)
event_error (ev, "events can only use a single increment");
inc = ev->expr[j];
}
}
}
INIT = init;
COND = cond;
INC = inc;
ev->nexpr = 0;
}
ev->i = -1; ev->t = - TEND_EVENT;
if (INIT) {
(* INIT) (&ev->i, &ev->t, ev);
if (ev->i == END_EVENT || ev->t == TEND_EVENT) {
ev->i = END_EVENT; ev->t = - TEND_EVENT;
}
}
else if (INC) {
(* INC) (&ev->i, &ev->t, ev);
if (ev->i != -1)
ev->i = 0;
if (ev->t != - TEND_EVENT)
ev->t = 0;
}
}
}
enum { event_done, event_alive, event_stop };
static int event_finished (Event * ev)
{
ev->i = -1; ev->t = - TEND_EVENT;
return event_done;
}
void event_register (Event event) {
assert (Events);
assert (!event.last);
int n = 0, parent = -1;
for (Event * ev = Events; !ev->last; ev++) {
if (!strcmp (event.name, ev->name)) {
assert (parent < 0);
parent = n;
}
n++;
}
if (parent < 0) {
qrealloc (Events, n + 2, Event);
Events[n] = event;
Events[n].next = NULL;
Events[n + 1].last = true;
init_event (&Events[n]);
}
else {
Event * ev = qcalloc (1, Event);
*ev = Events[parent];
Events[parent] = event;
Events[parent].next = ev;
init_event (&Events[parent]);
}
}
static int event_cond (Event * ev, int i, double t)
{
if (!COND)
return true;
return (* COND) (&i, &t, ev);
}
#if DEBUG_EVENTS
static void event_print (Event * ev, FILE * fp)
{
char * root = strstr (ev->file, BASILISK);
fprintf (fp, " %-25s %s%s:%d\n", ev->name,
root ? "src" : "",
root ? &ev->file[strlen(BASILISK)] : ev->file,
ev->line);
}
#endif
The interpreter overloads the function below to control (i.e. shorten) the events loop.
static bool overload_event() { return true; }
static int event_do (Event * ev, bool action)
{
if ((iter > ev->i && t > ev->t) || !event_cond (ev, iter, t))
return event_finished (ev);
if (!overload_event() || iter == ev->i || fabs (t - ev->t) <= TEPS*t) {
if (action) {
bool finished = false;
for (Event * e = ev; e; e = e->next) {
#if DEBUG_EVENTS
event_print (e, stderr);
#endif
if ((* e->action) (iter, t, e))
finished = true;
}
if (finished) {
event_finished (ev);
return event_stop;
}
}
if (ev->arrayi) { /* i = {...} */
ev->i = ev->arrayi[ev->a++];
if (ev->i < 0)
return event_finished (ev);
}
if (ev->arrayt) { /* t = {...} */
ev->t = ev->arrayt[ev->a++];
if (ev->t < 0)
return event_finished (ev);
}
else if (INC) {
int i0 = ev->i;
(* INC) (&ev->i, &ev->t, ev);
if (i0 == -1 && ev->i != i0)
ev->i += iter + 1;
if (!event_cond (ev, iter + 1, ev->t))
return event_finished (ev);
}
else if (INIT && !COND)
return event_finished (ev);
}
return event_alive;
}
static void end_event_do (bool action)
{
#if DEBUG_EVENTS
if (action)
fprintf (stderr, "\nend events (i = %d, t = %g)\n", iter, t);
#endif
for (Event * ev = Events; !ev->last; ev++)
if (ev->i == END_EVENT && action)
for (Event * e = ev; e; e = e->next) {
#if DEBUG_EVENTS
event_print (e, stderr);
#endif
e->action (iter, t, e);
}
}
int events (bool action)
{
#if DEBUG_EVENTS
if (action)
fprintf (stderr, "\nevents (i = %d, t = %g)\n", iter, t);
#endif
if (iter == 0)
for (Event * ev = Events; !ev->last; ev++)
init_event (ev);
int cond = 0, cond1 = 0;
inext = END_EVENT; tnext = HUGE;
for (Event * ev = Events; !ev->last && !cond; ev++)
if (ev->i != END_EVENT &&
(COND || (INIT && !COND && !INC) || ev->arrayi || ev->arrayt))
cond = 1;
for (Event * ev = Events; !ev->last; ev++) {
int status = event_do (ev, action);
if (status == event_stop) {
end_event_do (action);
return 0;
}
if (status == event_alive && ev->i != END_EVENT &&
(COND || (INIT && !COND && !INC) || ev->arrayi || ev->arrayt))
cond1 = 1;
if (ev->t > t && ev->t < tnext)
tnext = ev->t;
if (ev->i > iter && ev->i < inext)
inext = ev->i;
}
if (overload_event() && (!cond || cond1) && (tnext != HUGE || inext != END_EVENT)) {
inext = iter + 1;
return 1;
}
end_event_do (action);
return 0;
}
void event (const char * name)
{
for (Event * ev = Events; !ev->last; ev++)
if (!strcmp (ev->name, name))
for (Event * e = ev; e; e = e->next) {
#if DEBUG_EVENTS
event_print (e, stderr);
#endif
(* e->action) (0, 0, e);
}
}
double dtnext (double dt)
{
if (tnext != HUGE && tnext > t) {
assert (dt > 0.);
unsigned int n = (tnext - t)/dt;
assert (n < INT_MAX); // check that dt is not too small
if (n == 0)
dt = tnext - t;
else {
double dt1 = (tnext - t)/n;
if (dt1 > dt*(1. + TEPS))
dt = (tnext - t)/(n + 1);
else if (dt1 < dt)
dt = dt1;
tnext = t + dt;
}
}
else
tnext = t + dt;
return dt;
}
void init_solver()
{
Events = malloc (sizeof (Event));
Events[0].last = 1;
_attribute = calloc (datasize/sizeof(real), sizeof (_Attributes));
int n = datasize/sizeof(real);
all = (scalar *) malloc (sizeof (scalar)*(n + 1));
baseblock = (scalar *) malloc (sizeof (scalar)*(n + 1));
for (int i = 0; i < n; i++)
baseblock[i].i = all[i].i = i;
baseblock[n].i = all[n].i = -1;
#if _CADNA
cadna_init (-1);
#endif
#if _MPI
mpi_init();
#elif MTRACE == 1
char * etrace = getenv ("MTRACE");
pmtrace.fp = fopen (etrace ? etrace : "mtrace", "w");
pmtrace.fname = systrdup (etrace ? etrace : "mtrace");
#endif
}