An Example of Use of the Gerris Object System

From Gerris

Revision as of 22:12, 3 November 2007; view current revision
←Older revision | Newer revision→
Jump to: navigation, search

Contents

Session 2

Answer to the exercise

Here is an example class written by Daniel Fuster. The beginning looks like the tutorial and the class generated by gtstemplate, but there is a difference. Can you see it ?

/* Daniel: Header */
#include <math.h>
#include <stdlib.h>
#include <gfs.h>
 
typedef struct _Daniel Daniel;
 
struct _Daniel {
/*< private >*/
GfsGenericInit parent;
 
/*< public >*/
gdouble m;
};
 
typedef struct _DanielClass DanielClass;
 
struct _DanielClass {
 
GfsGenericInitClass parent_class;
/*< public >*/
/* add extra methods here */
};
 
#define DANIEL(obj) GTS_OBJECT_CAST (obj,\
Daniel,\
daniel_class ())
#define DANIEL_CLASS(klass) GTS_OBJECT_CLASS_CAST (klass,\
DanielClass,\
daniel_class())
#define IS_DANIEL(obj) (gts_object_is_from_class (obj,\
daniel_class ()))
 
DanielClass * daniel_class (void);

Did you notice the change ? We are using GfsGenericInit as the parent class of Daniel, not GfsInit. Can you explain why ? Now we define the read, write etc.. functions

/* Daniel: Object */
 
static void daniel_read (GtsObject ** o, GtsFile * fp)
{
/* call read method of parent */
if (GTS_OBJECT_CLASS (daniel_class ())->parent_class->read)
(* GTS_OBJECT_CLASS (daniel_class ())->parent_class->read)
(o, fp);
if (fp->type == GTS_ERROR)
return;
 
if (fp->type != GTS_INT && fp->type != GTS_FLOAT) {
gts_file_error (fp, "expecting a number (m)");
return;
}
DANIEL (*o)->m = atof (fp->token->str);
 
/* do not forget to prepare for next read */
gts_file_next_token (fp);
 
}
 
static void daniel_write (GtsObject * o, FILE * fp)
{
/* call write method of parent */
if (GTS_OBJECT_CLASS (daniel_class ())->parent_class->write)
(* GTS_OBJECT_CLASS (daniel_class ())->parent_class->write)
(o, fp);
 
fprintf (fp, " %g", DANIEL (o)->m);
 
/* do object specific write here */
}

Now we have a problem following the tutorial. The tutorial says:

static void init_velocity (FttCell * cell,
InitPeriodic * init)
{
FttVector pos;
 
ftt_cell_pos (cell, &pos);
GFS_STATE (cell)->u =
- cos (2.*init->m*M_PI*pos.x)*sin (2.*init->m*M_PI*pos.y);
GFS_STATE (cell)->v =
sin (2.*init->m*M_PI*pos.x)*cos (2.*init->m*M_PI*pos.y);
}
 
static gboolean init_periodic_event (GfsEvent * event, GfsSimulation * sim)
{
if ((* GFS_EVENT_CLASS (GTS_OBJECT_CLASS (init_periodic_class ())\
->parent_class)->event) (event, sim)) {
gfs_domain_cell_traverse (GFS_DOMAIN (sim),
FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
(FttCellTraverseFunc) init_velocity,
event);
return TRUE;
}
return FALSE;
}

but when we try to do something similar it does not work. The problem is with passing the event object to the init_velocity function. Daniel fixed it as follows:

static void init_velocity (FttCell * cell,
gpointer * data )
{
FttVector pos;
GfsVariable * v;
gdouble * m = data[0];
 
ftt_cell_pos (cell, &pos);
v = data[1];
GFS_VARIABLE (cell, v->i) = sin (2.*(*m)*M_PI*pos.x)*cos (2.*(*m)*M_PI*pos.y);
}
 
static gboolean daniel_event (GfsEvent * event, GfsSimulation * sim)
{
gpointer data[2];
Daniel * inputdata = DANIEL (event);
 
if ((* GFS_EVENT_CLASS (GTS_OBJECT_CLASS (daniel_class ())->parent_class)->event) (event, sim)) {
/* do object-specific event here */
 
GfsDomain * domain = GFS_DOMAIN (sim);
 
data[0] = &(inputdata->m);
 
data[1] = gfs_variable_from_name (domain->variables, "U");
 
gfs_domain_cell_traverse (GFS_DOMAIN (sim),
FTT_PRE_ORDER, FTT_TRAVERSE_LEAFS, -1,
(FttCellTraverseFunc) init_velocity,
data);
return TRUE;
}
return FALSE;
}

This works, and is the way things are usually written in Gerris.

static void daniel_class_init (DanielClass * klass)
{
/* define new methods and overload inherited methods here */
 
GFS_EVENT_CLASS (klass)->event = daniel_event;
GTS_OBJECT_CLASS (klass)->read = daniel_read;
GTS_OBJECT_CLASS (klass)->write = daniel_write;
}
 
static void daniel_init (Daniel * object)
{
/* initialize object here */
object->m = 1.;
}
 
DanielClass * daniel_class (void)
{
static DanielClass * klass = NULL;
 
if (klass == NULL) {
GtsObjectClassInfo daniel_info = {
"Daniel",
sizeof (Daniel),
sizeof (DanielClass),
(GtsObjectClassInitFunc) daniel_class_init,
(GtsObjectInitFunc) daniel_init,
(GtsArgSetFunc) NULL,
(GtsArgGetFunc) NULL
};
klass = gts_object_class_new (GTS_OBJECT_CLASS (gfs_init_class ()),
&daniel_info);
}
 
return klass;
}
/* Initialize module */
 
const gchar * g_module_check_init (void)
{
daniel_class ();
return NULL;
}

Various Gerris techniques

Cast to the parent class

The example shows how one can extract information from an object if one knows in which parent it is stored. For example, if we want to use the information of the event variable which came from the GfsEvent class, we can have access to it by means of the following variable "parentdata":

GfsEvent * parentdata = GFS_EVENT (event);

In this way, we load the information of the "event" object coming from its parent GfsEvent into this variable and we can either use it or manipulate if we want. For example parentdata->step will give us the temporal step which have been read in the input.

GFS_VARIABLE macro

This macro is used everywhere in the code to access the value of a GFS_VARIABLE in a cell. An example of its use is found in the Daniel object above:

GFS_VARIABLE (cell, v->i) = sin (2.*(*m)*M_PI*pos.x)*cos (2.*(*m)*M_PI*pos.y);

This expression replaces

GFS_STATE (cell)->v = sin (2.*init->m*M_PI*pos.x)*cos (2.*init->m*M_PI*pos.y);

which was used in the tutorial.

To use this macro, there are two points of view.

  • In the "magical" approach we just now that GFS_VARIABLE(somecell,somevariable->i)allows us to write to the variable somevariablein the cell somecell. For us dummies a question may arise: what value should the index i have ? Is it the cell number ? Well i is not an index: it is a member of each GfsVariable. We do not have to set it: it is already there in the variable.
  • In the rational approach, we would like to read the code and understand the definition of GFS_VARIABLE. However the definition of the GFS_VARIABLE macro is a bit baffling. It can be found in fluid.h. (It is a good idea to try the following: find any place in the code where the GFS_VARIABLE macro is used ---that should be easy--- and then click on it. Type M-. in emacs ---or the equivalent in vim--- and get to the definition.)
#define GFS_VARIABLE(cell, index)     ((&GFS_STATE (cell)->place_holder)[index])

Notice that we have a special member of the cell state that tells us where the variable is stored. The macro GFS_STATE is defined by

#define GFS_STATE(cell)               ((GfsStateVector *) (cell)->data)

The FttCell class

This class is defined as follows:

struct _FttCell {
/*< public >*/
guint flags;
gpointer data;
 
/*< private >*/
struct _FttOct * parent, * children;
};

The data member of this class is a gpointer. Remember from last session that it is an untyped pointer, so there is no difficulty to cast to a GfsStateVector by using

struct _GfsStateVector {
/* temporary face variables */
GfsFaceStateVector f[FTT_NEIGHBORS];
 
/* solid boundaries */
GfsSolidVector * solid;
 
gdouble place_holder;
};
Personal tools
communication