Mesh and Fields#
Mesh#
The mesh consists of NX cells in X (hence azimuth in cylindrical and
spherical geometries), NY+2*NGHY cells in Y (radius in cylindrical and
spherical geometries) and NZ+2*NGHZ cells in Z (colatitude in
spherical geometry). Here NGHY and NGHZ stand for the number of ghost
or buffer zones next to the active mesh. If a direction is not
included in the setup (for instance Z in the 2D polar fargo
setup), the corresponding value of NGHY/Z is set to 0.
The variables NX, NY, and NZ are defined in the parameter file (they
default to 1, so there is no need to define, for instance, NZ in a 2D
setup such as fargo
, or NX in the 2D setup otvortex
, which
corresponds to the Orszag-Tang vortex problem in Y and Z).
In practice, this mesh is split among processors, and locally (within the scope of a given process) the submesh considered has size Nx, Ny+2*NGHY and Nz+2*NGHZ.
The information about cells coordinates is stored in 1D arrays
[xyz]min(index)
[xyz]med(index)
where min refers to the inner edge of a zone (in x, y or z) whereas med refers to the center of a zone (in x, y or z). This notation should look familiar to former FARGO users.
Warning
[xyz][min/max](index) are not vectors, they are macrocommands. They must be invoked with (), not with [].
NGHY and NGHZ are preprocessor variables, defined in the file src/define.h
.
Because we have a multi-geometry code, another set of secondary geometrical variables is defined (surfaces, volumes). See the end of this section for details.
Fields#
Fields are structures, and they can be seen as cubes of cells, of size
equal to the mesh size. The location at which a given variable is defined is [xyz]med if
the field is [xyz]-centered, or [xyz]min if the field is
[xyz]-staggered. You can find a comprehensive list of the fields in
src/global.h
. The place where the fields are created is in
CreateFields()
, inside src/LowTasks.c
.
Internally, all fields are cubes written as 1D-arrays. So we need
indices to work with the 3D-data. We have a set of helpers defined in
src/define.h
. They are:
l
: The index of the current zone.lxp
,lxm
: lxplus/lxminnus, the right/left x-neighborlyp
,lym
: lyplus/lyminnus, the right/left y-neighborlzp
,lzm
: lzplus/lzminnus, the right/left z-neighbor
These helpers must be used with the proper loop indices:
int i,j,k;
for (k=z_lower_bound; k<z_upper_bound; k++) {
for (j=y_lower_bound; j<y_upper_bound; j++) {
for (i=x_lower_bound; i<x_upper_bound; i++) {
field[l] = 3.0;
field2[l] = (field1[lxp]-field1[l])/(xmed(ixp)-xmed(i)); //obviously some gradient calculation...
}
}
}
where [kji] always means [zyx]-direction.
Warning
Do not change the order of the indices! The definition of l
,
lxp
, lxm
, etc. assumes the following correspondence:
i->x, j->y, k->z
These helpers are extremely useful. No explicit algebra has to be
performed on the indices within a loop (but never use or define a
variable called l
or lxp
!…). Besides, the definition of
l
is also correct within GPU kernels (for which the indices
algebra is slightly different owing to memory alignment
considerations), and this is totally transparent to the user who
should never have to worry about this.
In practice, a loop is similar to (isothermal equation of state):
int i,j,k;
for (k=0; k<Nz+2*NGHZ; k++) {
for (j=0; j<Ny+2*NGHY; j++) {
for (i=0; i<Nx; i++ ) {
pres[l] = dens[l]*cs[l]*cs[l];
}
}
}
Note
Note that the lines of code above do not evaluate, nor define
l
, which is used straight out of the box, since it is a
preprocessor macrocommand.
Working with fields#
A field structure is defined as follows (in src/structs.h
):
struct field {
char *name;
real *field_cpu;
real *field_gpu;
};
where we have stripped the definition of all extra lines not relevant
at this stage. The name
is a string that is used to determine the
name of output files. field_cpu
is a pointer to a double or float
1D array which has been duly allocated on the RAM prior to any
invocation.
Similarly field_gpu
is a pointer to a double or float 1D array
which has been duly allocated on the Video RAM prior to any
invocation. The user should never have to invoke directly this
field. Rather, C files will always make use of the field_cpu
,
which will be automatically translated to field_gpu
as needed
during the C to CUDA conversion.
Acceding a field value is generally done as follows:
struct Field *Density; // Definition at the beginning of a function
real *density; // real is either double or float.
density = Density->field_cpu;
...
later on in a loop:
...
density[l] = ....;
Note
Note that we define an “array of reals” straight away and subsequently only refer to it to manipulate cell values. In order to avoid confusion, it is a good idea to have an upper case for the initial of Fields*, and lower case for the corresponding real arrays.
Fields on the GPU#
Similar techniques are used on the GPU, but we have made it totally transparent to the user, so unless you want to program your CUDA kernels directly, you should never to worry about this.
Useful variables#
For the handling of the mesh, a set of useful variables and macrocommands has been defined. An extensive list with a description is given below:
Indices:
l
: The index of the current cell. It is a function of (i
,``j``,``k``,pitch
&stride
).lxp
: The index of the “right” neighbor in x of the current cell. It is a function ofl
.lxm
: The index of the “left” neighbor in x of the current cell. It is a function ofl
.lyp
: The index of the “right” neighbor in y of the current cell. It is a function ofl
.lym
: The index of the “left” neighbor in y of the current cell. It is a function ofl
.lzp
: The index of the “right” neighbor in z of the current cell. It is a function ofl
.lzm
: The index of the “left” neighbor in z of the current cell. It is a function ofl
.l2D
: The current index in a 2D field (eg: vmean). It is a function of (j
,``k``).l2D_int
: The current index in a 2D integer field (eg: a field of shifts). It is a function if (j
,``k``).ixm
:i
-index of the “left” neighbor in x of the current cell, taking periodicity into account.ixp
:i
-index of the “right” neighbor in x of the current cell, taking periodicity into account.
Coordinates:
XC
: center of the current cell in X. It is a function of the indices; must to be used inside a loop.YC
: center of the current cell in Y. It is a function of the indices; must to be used inside a loop.ZC
: center of the current cell in Z. It is a function of the indices; must to be used inside a loop.xmin(i)
: The lower x-bound of a cell.xmed(i)
: The x-center of a cell, same as XC but can be used outside a loop.ymin(j)
: The lower y-bound of a cell.ymed(j)
: The y-center of a cell, same as YC but can be used outside a loop.zmin(k)
: The lower z-bound of a cell.zmed(k)
: The z-center of a cell, same as ZC but can be used outside a loop.
Length:
zone_size_x(j,k)
: Face to face distance in the x direction.zone_size_y(j,k)
: Face to face distance in the y direction.zone_size_z(j,k)
: Face to face distance in the z direction.edge_size_x(j,k)
: The same as zone_size_x, but measured on the lower x-border.edge_size_y(j,k)
: The same as zone_size_y, but measured on the lower y-border.edge_size_z(j,k)
: The same as zone_size_z, but measured on the lower z-border.edge_size_x_middlez_lowy(j,k)
: The same as edge_size_x but measured half a cell above in z.edge_size_x_middley_lowz(j,k)
: The same as edge_size_x but measured half a cell above in y.
Surfaces:
SurfX(j,k)
: The lower surface of a cell at x=cte.SurfY(j,k)
: The lower surface of a cell at y=cte.SurfZ(j,k)
: The lower surface of a cell at z=cte.
Volumes:
Vol(j,k)
: The volume of the current cell.InvVol(j,k)
: The inverse of the current cell’s volume.
You can see examples on how to use these variables in src/
. They
are widely used in many routines.