With this preliminary overall understanding of what the template does,
we are ready to define our new initialisation class. What we want to
do is initialise the 2D velocity field using the following functions:
u(x, y) | = | -cos(2m![]() ![]() |
(1) |
v(x, y) | = | sin(2m![]() ![]() |
(2) |
We need to store the value of m
We now need to specify the value of m
Now that we have written the data associated with the parent class, we
can write our own data like this:
We now need to define a ``symmetrical'' read method which will
read the value of m
We can now create, read and write our new object. However we are not doing
anything with it yet. Like for any other GfsEvent, the action
performed is controlled by the event method. If we look at the
init_periodic_event function, we see that it returns a gboolean. If TRUE this return value means that the event took
place. We then call the event method of the parent class and check its
return value. If TRUE we do something and return TRUE. We
know that the parent class is GfsInit, an event which takes
place once at the start of the simulation. What we want to do is
initialise the velocity field using our formula. We can do that like this:
We also pass an extra parameter to init_velocity: event. If you now look at init_velocity you see that this extra
argument is our object: InitPeriodic * init. What we have done
here is an implicit cast of GfsEvent * event to InitPeriodic * init. We know it is valid because init_periodic_event is a method of our object class.
What happens in init_velocity is straightforward. We first get
the position of the center of the cell using ftt_cell_pos and
then assign the values of the two components of the velocities using
our formula and the m
We have now almost all that is needed for our new object. How do we
use this new piece of code with gerris? What we want to do is to
create a dynamically loadable module. First of all we need to
check that we can compile the code. There are a few missing headers we
need to add (at the top of the file):
To make a proper module we also need to add the following at the end
of the file:
We are now all set to create our new module. Sadly, dynamically
loadable module creation is not a standardised process and the
command-line arguments vary from compiler to compiler and from system
to system. In the following, I will assume that you use gcc on a
linux box. If you are using another system supporting dynamically
loadable modules, you will need to read your local manual. On a linux
box:
Our module is now ready to use. We just need to install it in a
directory where it will be found by gerris. If you installed gerris in
/home/joe/local just type:
struct _InitPeriodic {
/*< private >*/
GfsInit parent;
/*< public >*/
gdouble m;
};
Note that the GfsInit parent; data must be the first data
in the structure. The whole object-inheritance mechanism in C relies
on this data alignment.
static void init_periodic_init (InitPeriodic * object)
{
object->m = 1.;
}
Our default value for m
/* call write method of parent */
if (GTS_OBJECT_CLASS (init_periodic_class ())->parent_class->write)
(* GTS_OBJECT_CLASS (init_periodic_class ())->parent_class->write)
(o, fp);
The parent class is given by the
parent_class field of any GtsObjectClass. The write
method might not be defined for the parent class, so it is always a
good (safe) idea to first test that it is indeed defined.
static void init_periodic_write (GtsObject * o, FILE * fp)
{
/* call write method of parent */
if (GTS_OBJECT_CLASS (init_periodic_class ())->parent_class->write)
(* GTS_OBJECT_CLASS (init_periodic_class ())->parent_class->write)
(o, fp);
fprintf (fp, " %g", INIT_PERIODIC (o)->m);
}
Note that we used a space to separate this new data from the data of
the parent class and that we didn't add anything after our own data
(no space or newline). This is so that we can eventually extend
(through inheritance) this class by adding more data in exactly the
same way.
if (fp->type == GTS_ERROR)
return;
just checks if an error occurred while reading parameters for the
parent class, in which case the method returns prematurely. To read
our parameter we are going to use functions associated with the GtsFile data type (have a look at the GTS reference manual
for
details):
static void init_periodic_read (GtsObject ** o, GtsFile * fp)
{
/* call read method of parent */
if (GTS_OBJECT_CLASS (init_periodic_class ())->parent_class->read)
(* GTS_OBJECT_CLASS (init_periodic_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;
}
INIT_PERIODIC (*o)->m = atof (fp->token->str);
/* do not forget to prepare for next read */
gts_file_next_token (fp);
}
We first check that the current token is an integer (GTS_INT) or a floating point number (GTS_FLOAT). If it is not
we set an error message describing the problem and return
prematurely. If it is, we convert the string describing the current
token (fp->token->str) into a floating point number (using the
standard C atof function) and assign it to m
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;
}
The gfs_domain_cell_traverse function traverses the cell tree
and calls the init_velocity function for each leaf cell (FTT_TRAVERSE_LEAFS). A GfsSimulation is an object derived from
GfsDomain which justifies the GFS_DOMAIN (sim) type
casting. Have a look in the reference manual
if you want to know more
about these structures and functions.
5.2.1 Creating a module
#include <math.h>
#include <stdlib.h>
#include <gfs.h>
The gfs.h header contains all the function declarations for the
gerris library. We can now try to compile using for example:
% cc `gfs-config --2D --cflags` -Wall -g -O2 -c init_periodic.c
where gfs-config -2D -cflags (quoted using inverted quotes)
defines the compilation flags needed to use the 2D version of the
gerris library.
/* Initialize module */
const gchar * g_module_check_init (void);
const gchar * g_module_check_init (void)
{
init_periodic_class ();
return NULL;
}
This just tells gerris how to initialise the module after it has been
loaded. The g_module_check_init function
will be called. In our case, it does only one thing which is to instantiate our new class: this is necessary so that gerris registers
how to handle it.
% cc `gfs-config --2D --cflags` -Wall -g -O2 -c -fPIC init_periodic.c
% cc -shared init_periodic.o -lc -o libinit_periodic2D.so
should work. Note that we add the 2D extension to indicate that
this module uses the 2D gerris library. We could have built both a 2D
and a 3D version of the same module. At runtime the gerris executable
uses this extension to check which version to load.
% cp libinit_periodic2D.so /home/joe/local/lib/gerris
We can now use it directly in a parameter file, for example:
1 2 GfsSimulation GfsBox GfsGEdge {} {
GfsTime { end = 50 }
GfsRefine 6
GModule init_periodic
InitPeriodic {} 2
GfsOutputTime { istep = 10 } stdout
GfsOutputProjectionStats { istep = 10 } stdout
GfsOutputPPM { step = 0.1 } vorticity-%4.2f.ppm { v = Vorticity }
}
GfsBox {}
1 1 right
1 1 top
The first part of the object definition: InitPeriodic {} is the
generic GfsEvent definition, the second part: 2 is the
parameter m
gerris: file `periodic.gfs' is not a valid simulation file
periodic.gfs:5:18: expecting a number (m)
which tells you where the error occurred (in file periodic.gfs,
line 5, character 18) together with the error message which we
specified in the read method of our class.
Next: 5.3 Outputs
Up: 5 Writing new objects
Previous: 5.1 A template for
Contents