spherical.h

📄 View in API Reference (Doxygen) · View on basilisk.fr

Test cases (1): lonlat

Examples (4): global-tides, held-suarez, tohoku, tsunami

Spherical coordinates

The default radius of the sphere is set to one.

double Radius = 1.;

Rather than the classical mathematical convention, we use the geographic convention: *x* is the longitude within $[-180:180]$ degrees and *y* is the latitude within $[-90:90]$ degrees. $\Delta$ is the characteristic cell length expressed in the same units as *Radius*.

macro VARIABLES (Point point = point, int _ig = ig, int _jg = jg, int _kg = kg)
{
  VARIABLES (point, _ig, _jg, _kg);
  x = x < -180. ? x + 360. : x > 180. ? x - 360. : x;
  Delta_x = Delta_y = Delta;
  Delta *= Radius*pi/180.;
}

On trees we need to define consistent refinement functions. The default restriction functions (averages) are already consistent (i.e. they preserve volume and length integrals).

Mathematically we have

$$ cm = fm.y = \cos(\phi) $$
$$ fm.x = 1 $$

with $\phi$ the latitude in radians. This is the definition we use for the length scale factors *fm*.

For the volume scale factor *cm*, more care needs to be taken to guarantee discrete volume conservation i.e.

$$ \sum_{children}cm_{child} = 4cm_{parent} $$

This is achieved by computing the exact area of a cell i.e.

$$ \Delta^2 \int^{\phi + d\phi/2}_{\phi - d \phi/2} \cos\phi d\phi = \Delta^2 (\sin(\phi + d\phi/2) - \sin(\phi - d\phi/2)) = \Delta^2 cm $$
#if TREE
static void refine_cm_lonlat (Point point, scalar cm)
{
  double phi = y*pi/180., dphi = Delta/(2.*Radius);
  double sinphi = sin(phi);
  fine(cm,0,0) = fine(cm,1,0) = (sinphi - sin(phi - dphi))/dphi;
  fine(cm,0,1) = fine(cm,1,1) = (sin(phi + dphi) - sinphi)/dphi;
}

static void refine_face_x_lonlat (Point point, scalar fm)
{
  if (!is_refined(neighbor(-1)))
    fine(fm,0,0) = fine(fm,0,1) = 1.;
  if (!is_refined(neighbor(1)) && neighbor(1).neighbors)
    fine(fm,2,0) = fine(fm,2,1) = 1.;
  fine(fm,1,0) = fine(fm,1,1) = 1.;
}

static void refine_face_y_lonlat (Point point, scalar fm)
{
  double phi = y*pi/180., dphi = Delta/(2.*Radius);
  if (!is_refined(neighbor(0,-1)))
    fine(fm,0,0) = fine(fm,1,0) = cos(phi - dphi);
  if (!is_refined(neighbor(0,1)) && neighbor(0,1).neighbors)
    fine(fm,0,2) = fine(fm,1,2) = cos(phi + dphi);
  fine(fm,0,1) = fine(fm,1,1) = cos(phi);
}
#endif

event metric (i = 0) {

We initialise the scale factors, taking care to first allocate the fields if they are still constant.

if (is_constant(cm)) {
    scalar * l = list_copy (all);
    cm = new scalar;
    free (all);
    all = list_concat ({cm}, l);
    free (l);
  }
  scalar cmv = cm;
  foreach() {
    double phi = y*pi/180., dphi = Delta/(2.*Radius);
    cmv[] = (sin(phi + dphi) - sin(phi - dphi))/(2.*dphi);
  }

  if (is_constant(fm.x)) {
    scalar * l = list_copy (all);
    fm = new face vector;
    free (all);
    all = list_concat ((scalar *){fm}, l);
    free (l);
  }
  face vector fmv = fm;
  foreach_face(x)
    fmv.x[] = 1.;
  foreach_face(y)
    fmv.y[] = cos(y*pi/180.);

We set our refinement/prolongation functions on trees.

#if TREE
  cm.refine = cm.prolongation = refine_cm_lonlat;
  fm.x.prolongation = refine_face_x_lonlat;
  fm.y.prolongation = refine_face_y_lonlat;
#endif
}