797 lines
31 KiB
C++
797 lines
31 KiB
C++
#ifndef GHOST_ZONE_H
|
|
#define GHOST_ZONE_H
|
|
namespace AHFinderDirect
|
|
{
|
|
|
|
//*****************************************************************************
|
|
|
|
//
|
|
// ***** design notes for ghost zones *****
|
|
//
|
|
|
|
//
|
|
// A ghost_zone object describes a patch's ghost zone, and knows how
|
|
// to compute gridfns there (we usually speak of "synchronizing" the
|
|
// ghost zone or zones) based on either the patch system's symmetry
|
|
// or interpolation from a neighboring patch. ghost_zone is an abstract
|
|
// base class, from which we derive two concrete classes:
|
|
// * A symmetry_ghost_zone object describes a ghost zone which is a
|
|
// (discrete) symmetry of spacetime, either mirror-image or periodic.
|
|
// Such an object knows how to fill in ghost-zone gridfn data from
|
|
// the "other side" of the symmetry.
|
|
// * An interpatch_ghost_zone object describes a ghost zone which
|
|
// overlaps another patch. Such an object knows how to get ghost
|
|
// zone gridfn data from the other patch. More accurately, it gets
|
|
// the data by asking (calling) the appropriate one of the other
|
|
// patch's patch_interp objects.
|
|
// Every patch has (points to) 4 ghost_zone objects, one for each of
|
|
// the patch's sides. See the comments in "patch.hh" for a "big picture"
|
|
// discussion of patches, patch edges, ghost zones, and patch interpolators.
|
|
//
|
|
|
|
//
|
|
// There are some unobvious complications involved in synchronizing
|
|
// the ghost zone "corners", i.e. in ghost zone points that are outside
|
|
// the nominal grid in *both* coordinates. There are 3 basic cases here:
|
|
// * A corner between two symmetry ghost zones, for example the -x/-y
|
|
// corner in the example below. In this case it takes *two* sequential
|
|
// symmetry operations to get gridfn data in the corner from the
|
|
// nominal grid. Symmetry operations commute, so at each point we'll
|
|
// always get the same results independently of in which order we do
|
|
// the symmetry operations. Computationally, we actually do the operations
|
|
// in both orders, one order's results overwriting the other's, but
|
|
// this doesn't matter (because the results are the same).
|
|
// * A corner between two interpatch ghost zones, for example the +x/+y
|
|
// corner in the example below. In this case we could get the gridfn
|
|
// data by either of two distinct interpolation operations (presumably
|
|
// from two distinct patches), which would in general give slightly
|
|
// different results. In some ideal world we might do a centered
|
|
// interpolation using data from both patches, but this would be
|
|
// complicated:
|
|
// - it would require a 2-D interpolation
|
|
// - it would require bookkeeping for interpolating from multiple
|
|
// patches within the same ghost zone, indeed for the same ghost
|
|
// zone point
|
|
// At present, we follow a simpler approach: we split the corner down
|
|
// its diagonal,
|
|
// [for the points on the diagonal we make an arbitrary choice;
|
|
// at present this is that they belong to (and get their data via)
|
|
// the rho ghost zone.]
|
|
// and off-center the interpolation as necessary so each ghost-zone
|
|
// point gets data solely from the neighboring patch on its own side.
|
|
// * A corner between a symmetry and an interpatch ghost zone, for
|
|
// example the +x/-y or -x/+y corners in the example below. In this
|
|
// case we first do a symmetry operation in the neighboring patch,
|
|
// then a fully centered interpolation (using the data just obtained
|
|
// from a symmetry operation) to get data in the non-corner part of
|
|
// the interpatch ghost zone. After the interpatch interpolation,
|
|
// we do a final symmetry operation to get gridfn data in the corner.
|
|
//
|
|
// In general, then, a ghost zone is rhomboid-shaped: iperp lies in a
|
|
// fixed interval, while ipar lies in an interval which may depend on
|
|
// iperp. In general, this shape depends on the type (symmetry vs interpatch)
|
|
// of the adjacent ghost zones.
|
|
//
|
|
|
|
//
|
|
// To properly handle all the symmetry/interpatch cases described above,
|
|
// we use a 3-phase algorithm to synchronize ghost zones:
|
|
// Phase 1: Fill in gridfn data at all the non-corner points of symmetry
|
|
// ghost zones, by using the symmetries to get this data from
|
|
// its "home patch" nominal grids.
|
|
// Phase 2: Fill in gridfn data in all the interpatch ghost zones, by
|
|
// interpatch interpolating from neighboring patches as described
|
|
// above.
|
|
// Phase 3: Fill in gridfn data at all the corner points of symmetry
|
|
// ghost zones, by using the symmetries to get this data from
|
|
// its "home patch" nominal grids or ghost zones.
|
|
// Here a given ghost zone corner may be either a full rectangle (so any
|
|
// given point is a member of both adjacent corners), or split down its
|
|
// diagonal (so any given point is a member of only one corner). This
|
|
// 3-phase algorithm is actually implemented by
|
|
// patch_system::synchronize()
|
|
// which in turn calls
|
|
// symmetry_ghost_zone::synchronize()
|
|
// interpatch_ghost_zone::synchronize()
|
|
//
|
|
|
|
//
|
|
// For example, consider the +z patch in an octant patch system, with
|
|
// the ghost zones being 2 points wide. The following illustration is
|
|
// looking down the z axis, and uses (x,y) for the patch coordinates
|
|
// for simplicity:
|
|
//
|
|
// # //
|
|
// i+y i+y i+y i+y i+y i+y i+y //
|
|
// (-2,7) (-1,7) (0,7) (1,7) (2,7) (3,7) (4,7) (5,7) (6,7) (7,7)
|
|
// <s-x> <s-x> # /i+x
|
|
// # //
|
|
// i+y i+y i+y i+y i+y i+y //
|
|
// (-2,6) (-1,6) (0,6) (1,6) (2,6) (3,6) (4,6) (5,6) (6,6) (7,6)
|
|
// <s-x> <s-x> # /i+x i+x
|
|
// # //
|
|
// # //
|
|
// (-2,5) (-1,5) 2,5)--(1,5)--(2,5)--(3,5)--(4,5)--(5,5) (6,5) (7,5)
|
|
// s-x s-x # | i+x i+x
|
|
// # |
|
|
// # |
|
|
// (-2,4) (-1,4) (0,4) (1,4) (2,4) (3,4) (4,4) (5,4) (6,4) (7,4)
|
|
// s-x s-x # | i+x i+x
|
|
// # |
|
|
// # |
|
|
// (-2,3) (-1,3) (0,3) (1,3) (2,3) (3,3) (4,3) (5,3) (6,3) (7,3)
|
|
// s-x s-x # | i+x i+x
|
|
// # |
|
|
// # |
|
|
// (-2,2) (-1,2) (0,2) (1,2) (2,2) (3,2) (4,2) (5,2) (6,2) (7,2)
|
|
// s-x s-x # | i+x i+x
|
|
// # |
|
|
// # |
|
|
// (-2,1) (-1,1) (0,1) (1,1) (2,1) (3,1) (4,1) (5,1) (6,1) (7,1)
|
|
// s-x s-x # | i+x i+x
|
|
// # |
|
|
// # |
|
|
// #(-2,0)#(-1,0)##(0,0)##(1,0)##(2,0)##(3,0)##(4,0)##(5,0)##(6,0)##(7,0)
|
|
// s-x s-x # i+x i+x
|
|
// #
|
|
// <s-y> <s-y> s-y s-y s-y s-y s-y s-y <s-y> <s-y>
|
|
// (-2,-1)(-1,-1) (0,-1) (1,-1) (2,-1) (3,-1) (4,-1) (5,-1) (6,-1) (7,-1)
|
|
// <s-x> <s-x> #
|
|
// #
|
|
// <s-y> <s-y> s-y s-y s-y s-y s-y s-y <s-y> <s-y>
|
|
// (-2,-2)(-1,-2) (0,-2) (1,-2) (2,-2) (3,-2) (4,-2) (5,-2) (6,-2) (7,-2)
|
|
// <s-x> <s-x> #
|
|
// #
|
|
//
|
|
// For this example,
|
|
// * The xz plane and yz plane are marked with ### lines
|
|
// * The +z patch's nominal grid is ([0,5],[0,5]), i.e. 0 <= x,y <= 5;
|
|
// its boundary lines are shown with single lines --- and | .
|
|
// * The diagonal where we've split corners are marked with // lines.
|
|
// * The +z patch's ghost zones are
|
|
// -x: (-1,[-1,7]), (-2,[-2,7])
|
|
// +x: (6,[-2,6]), (7,[-2,7])
|
|
// -y: ([-2, 7],[-2,-1])
|
|
// +y: ([-2,5],6), ([-2,6],7)
|
|
// * The regions where we will interpolate data from the +z patch are
|
|
// +x: ([ 3,4],[-2,7])
|
|
// +y: ([-2,7],[ 3,4])
|
|
// Note that in both cases the interpolation region includes the points
|
|
// computed by symmetry (in phase 1 of our 3-phase algorithm) on the
|
|
// adjacent edges! There are no interpolation regions inside the -x or
|
|
// -y boundaries, since no interpolation is needed across those boundaries
|
|
// of this patch.
|
|
// The diagonal *** line shows the boundary between the +x and +y ghost
|
|
// zones.
|
|
//
|
|
// Our 3-phase algorithm described above thus becomes:
|
|
// Phase 1: Fill in gridfn values at points marked with "s-x" below or
|
|
// "s-y" above via symmetry mirroring across the -x boundary
|
|
// (yz plane) or -y boundary (xz plane), as described by the
|
|
// +z patch's -x or -y symmetry_ghost_zone object respectively.
|
|
// Phase 2: Fill in gridfn values at points marked with "i+x" below or
|
|
// "i+y" above via interpatch interpolation from the neighboring
|
|
// patch across the +z patch's +x or +y boundary, as described
|
|
// by the +z patch's +x or +y interpatch_ghost_zone object
|
|
// respectively.
|
|
// Phase 3: Fill in gridfn values at points marked with "<s-x>" below or
|
|
// "<s-y>" above via symmetry mirroring across the -x boundary
|
|
// (yz plane) or -y boundary (xz plane), as described by the
|
|
// +z patch's -x or -y symmetry_ghost_zone object respectively.
|
|
//
|
|
|
|
//*****************************************************************************
|
|
|
|
//
|
|
// ghost_zone - abstract base class to describe ghost zone of patch
|
|
//
|
|
// This is an abstract base class describing a generic patch ghost zone.
|
|
// This might represent either of:
|
|
// - a discrete symmetry of spacetime (derived class symmetry_ghost_zone)
|
|
// - an overlap with another patch (derived class interpatch_ghost_zone)
|
|
//
|
|
|
|
//
|
|
// N.b. const qualifiers in ghost_zone and its derived classes refer to
|
|
// the underlying gridfn data.
|
|
//
|
|
|
|
// forward declarations
|
|
class symmetry_ghost_zone;
|
|
class interpatch_ghost_zone;
|
|
class patch_system;
|
|
|
|
class ghost_zone
|
|
{
|
|
public:
|
|
//
|
|
// ***** main high-level client interface *****
|
|
//
|
|
// "synchronize" a ghost zone, i.e. update the ghost-zone values
|
|
// of the specified gridfns via the appropriate sequence of
|
|
// symmetry operations and interpatch interpolations
|
|
// (flags specify which part(s) of the ghost zone we want)
|
|
//
|
|
virtual void synchronize(int ghosted_min_gfn, int ghosted_max_gfn,
|
|
bool want_corners = true,
|
|
bool want_noncorner = true) = 0;
|
|
|
|
public:
|
|
//
|
|
// ***** Jacobian of synchronize() *****
|
|
//
|
|
// This function computes the Jacobian of the synchronize()
|
|
// operation into internal buffers; the following functions
|
|
// provide access to that Jacobian.
|
|
//
|
|
// FIXME: should these be moved out into a separate Jacobian
|
|
// object/class?
|
|
//
|
|
// Note that this function just computes the Jacobian of this
|
|
// ghost zone's synchronize() operation -- it does *NOT* take
|
|
// into account the 3-phase synchronization algorithm described
|
|
// in the header comments for this file. (That's done by
|
|
// patch_system::synchronize_Jacobian() and its subfunctions.)
|
|
//
|
|
// n.b. terminology is
|
|
// partial gridfn at x
|
|
// -------------------
|
|
// partial gridfn at y
|
|
//
|
|
virtual void compute_Jacobian(int ghosted_min_gfn, int ghosted_max_gfn,
|
|
bool want_corners = true,
|
|
bool want_noncorner = true)
|
|
const = 0;
|
|
|
|
//
|
|
// The API in the remaining functions implicitly assumes that
|
|
// the Jacobian is independent of ghosted_gfn , and also that
|
|
// the structure of the Jacobian is such that the set of y points
|
|
// on which a single ghost-zone point depends,
|
|
// - has a single yiperp value (depending on our iperp, of course)
|
|
// - have a contiguous interval of yipar (depending on our iperp
|
|
// and ipar, of course), whose size is
|
|
// [or can be taken to be without an unreasonable
|
|
// amount of zero-padding]
|
|
// independent of our iperp and ipar; we parameterize this
|
|
// interval as yipar = posn+m where posn is determined by
|
|
// our iperp and ipar, and m has a fixed range independent
|
|
// of our iperp and ipar
|
|
//
|
|
|
|
// what is the [min,max] range of m for this ghost zone?
|
|
virtual int Jacobian_min_y_ipar_m() const = 0;
|
|
virtual int Jacobian_max_y_ipar_m() const = 0;
|
|
|
|
// what is the iperp of the Jacobian y points in their (y) patch?
|
|
virtual int Jacobian_y_iperp(int x_iperp) const = 0;
|
|
|
|
// what is the posn value of the y points in this Jacobian row?
|
|
virtual int Jacobian_y_ipar_posn(int x_iperp, int x_ipar) const = 0;
|
|
|
|
// what is the Jacobian
|
|
// partial synchronize() px.gridfn(ghosted_gfn, x_iperp, x_ipar)
|
|
// -------------------------------------------------------------
|
|
// partial py.gridfn(ghosted_gfn, y_iperp, y_posn+y_ipar_m)
|
|
// where
|
|
// y_iperp = Jacobian_y_iperp(x_iperp)
|
|
// y_posn = Jacobian_y_ipar_posn(x_iperp, x_ipar)
|
|
virtual fp Jacobian(int x_iperp, int x_ipar, int y_ipar_m) const = 0;
|
|
|
|
public:
|
|
//
|
|
// ***** low-level client interface *****
|
|
//
|
|
|
|
// to which patch/edge do we belong?
|
|
patch &my_patch() const { return my_patch_; }
|
|
const patch_edge &my_edge() const { return my_edge_; }
|
|
|
|
// from which patch/edge do we get data?
|
|
patch &other_patch() const { return other_patch_; }
|
|
const patch_edge &other_edge() const { return other_edge_; }
|
|
|
|
// what type of ghost zone are we?
|
|
bool is_interpatch() const { return is_interpatch_; }
|
|
bool is_symmetry() const { return !is_interpatch_; }
|
|
|
|
// convenience forwarding functions down to patch_edge::
|
|
bool is_min() const { return my_edge().is_min(); }
|
|
bool is_rho() const { return my_edge().is_rho(); }
|
|
|
|
// min/max iperp of the ghost zone
|
|
int min_iperp() const
|
|
{
|
|
return my_patch()
|
|
.minmax_ang_ghost_zone__min_iperp(is_min(), is_rho());
|
|
}
|
|
int max_iperp() const
|
|
{
|
|
return my_patch()
|
|
.minmax_ang_ghost_zone__max_iperp(is_min(), is_rho());
|
|
}
|
|
|
|
// inner/outer iperp of the ghost zone wrt our patch
|
|
int inner_iperp() const { return is_min() ? max_iperp() : min_iperp(); }
|
|
int outer_iperp() const { return is_min() ? min_iperp() : max_iperp(); }
|
|
|
|
// extreme min/max ipar that might possibly be part of this ghost zone
|
|
// (derived classes may actually use a subset of this)
|
|
int extreme_min_ipar() const
|
|
{
|
|
return my_edge().min_ipar_with_corners();
|
|
}
|
|
int extreme_max_ipar() const
|
|
{
|
|
return my_edge().max_ipar_with_corners();
|
|
}
|
|
|
|
// actual min/max ipar in the ghost zone at a particular iperp
|
|
// (may depend on type of the adjacent ghost zones)
|
|
virtual int min_ipar(int iperp) const = 0;
|
|
virtual int max_ipar(int iperp) const = 0;
|
|
|
|
// point membership predicate
|
|
bool is_in_ghost_zone(int iperp, int ipar)
|
|
const
|
|
{
|
|
// n.b. don't test ipar until we're sure iperp is in range!
|
|
return (iperp >= min_iperp()) && (iperp <= max_iperp()) && (ipar >= min_ipar(iperp)) && (ipar <= max_ipar(iperp));
|
|
}
|
|
|
|
// adjacent ghost zones to our min/max corners
|
|
const ghost_zone &min_par_adjacent_ghost_zone() const
|
|
{
|
|
return my_patch()
|
|
.ghost_zone_on_edge(my_edge().min_par_adjacent_edge());
|
|
}
|
|
const ghost_zone &max_par_adjacent_ghost_zone() const
|
|
{
|
|
return my_patch()
|
|
.ghost_zone_on_edge(my_edge().max_par_adjacent_edge());
|
|
}
|
|
|
|
//
|
|
// ***** safely cast to derived classes *****
|
|
//
|
|
|
|
// assert that gz is of specified type,
|
|
// then static_cast to derive type
|
|
const symmetry_ghost_zone &cast_to_symmetry_ghost_zone() const;
|
|
symmetry_ghost_zone &cast_to_symmetry_ghost_zone();
|
|
const interpatch_ghost_zone &cast_to_interpatch_ghost_zone() const;
|
|
interpatch_ghost_zone &cast_to_interpatch_ghost_zone();
|
|
|
|
//
|
|
// ***** constructor, finish setup, destructor *****
|
|
//
|
|
protected:
|
|
// ... values for is_interpatch_in constructor argument
|
|
// FIXME: these should really be bool, but then we couldn't
|
|
// use the "enum hack" for in-class constants
|
|
enum
|
|
{
|
|
ghost_zone_is_symmetry = false,
|
|
ghost_zone_is_interpatch = true // no comma
|
|
};
|
|
|
|
// constructor
|
|
// ... only used in implementing our derived classes;
|
|
// the rest of the world constructs our derived classes instead
|
|
ghost_zone(const patch_edge &my_edge_in,
|
|
const patch_edge &other_edge_in,
|
|
bool is_interpatch_in)
|
|
: my_patch_(my_edge_in.my_patch()),
|
|
my_edge_(my_edge_in),
|
|
other_patch_(other_edge_in.my_patch()),
|
|
other_edge_(other_edge_in),
|
|
is_interpatch_(is_interpatch_in)
|
|
{
|
|
}
|
|
|
|
public:
|
|
// assert() that ghost zone is fully setup:
|
|
// defined here ==> no-op
|
|
// symmetry ghost zone ==> unchanged ==> no-op
|
|
// interpatch ghost zone ==> check consistency of this and the
|
|
// other patch's ghost zones and
|
|
// patch_interp objects
|
|
virtual void assert_fully_setup() const {}
|
|
|
|
// destructor must be virtual to allow destruction
|
|
// of derived classes via ptr/ref to this class
|
|
virtual ~ghost_zone() {}
|
|
|
|
private:
|
|
// we forbid copying and passing by value
|
|
// by declaring the copy constructor and assignment operator
|
|
// private, but never defining them (either here or in derived classes)
|
|
ghost_zone(const ghost_zone &rhs);
|
|
ghost_zone &operator=(const ghost_zone &rhs);
|
|
|
|
private:
|
|
patch &my_patch_;
|
|
const patch_edge &my_edge_;
|
|
patch &other_patch_;
|
|
const patch_edge &other_edge_;
|
|
const bool is_interpatch_;
|
|
};
|
|
|
|
//*****************************************************************************
|
|
|
|
//
|
|
// symmetry_ghost_zone - derived class for spacetime-symmetry ghost zone
|
|
//
|
|
// In practice, there are two types of spacetime symmetry ghost zone:
|
|
// mirror symmetry and periodic symmetry. However, it turns out that the
|
|
// code needed to handle periodic BCs is basically a superset of that
|
|
// needed to handle mirror symmetries, so this class represents a generic
|
|
// symmetry ghost zone which may be of either type, and once constructed
|
|
// doesn't distinguish between the two.
|
|
//
|
|
// In general, a symmetry ghost zone implies that there's a 1-1 mapping
|
|
// between ghost zone points of this patch, and (a subset of the) interior
|
|
// points of this or another patch. If tensors are involved (this isn't
|
|
// used at present in the horizon finder), there's also a corresponding
|
|
// 1-1 mapping between (angular) tensor components.
|
|
//
|
|
// A mirror-symmetry ghost zone is specified by (the constructor arguments)
|
|
// - a patch edge
|
|
// - the (fp) perp coordinate of the mirror plane
|
|
// The mapping of ghost zone points is thus "just" the mirror imaging of
|
|
// iperp across the symmetry plane within this same patch. (The mapping
|
|
// leaves ipar invariant.)
|
|
//
|
|
// A periodic-symmetry ghost zone is specified by (the constructor arguments)
|
|
// - a patch edge (specifies the ghost zone)
|
|
// - the patch edge to which the ghost zone is to be mapped
|
|
// - a pair of ipar coordinates, one on this edge and one on the other edge,
|
|
// which map into each other
|
|
// - the sign of the ipar mapping (does increasing ipar on this edge map to
|
|
// increasing or decreasing ipar on the other edge?)
|
|
// The mapping of ghost zone points is the periodic mapping; this may map
|
|
// the ghost zone points to interior points of either this same patch or a
|
|
// different one.
|
|
//
|
|
// In general, the symmetry mapping of ghost zone points is of the form
|
|
// (iperp, ipar) --> (const +/- iperp, const +/- ipar)
|
|
// The iperp mapping is always in the direction
|
|
// outside the patch --> inside the patch
|
|
// while the ipar mapping might have either sign.
|
|
// If there are tensors, the corresponding mapping of tensor components is
|
|
// (index_perp, index_par) --> (+/-) (+/-) (index_perp, index_par)
|
|
// (that is, the two +/- signs are multiplied).
|
|
|
|
//
|
|
// Since all the member functions are const , a symmetry_ghost_zone
|
|
// object is effectively always const .
|
|
//
|
|
class symmetry_ghost_zone
|
|
: public ghost_zone
|
|
{
|
|
public:
|
|
//
|
|
// ***** main high-level client interface *****
|
|
//
|
|
// "synchronize" a ghost zone, i.e. update the ghost-zone values
|
|
// of the specified gridfns via the appropriate symmetry operations
|
|
// (flags specify which part(s) of the ghost zone we want)
|
|
//
|
|
void synchronize(int ghosted_min_gfn, int ghosted_max_gfn,
|
|
bool want_corners = true,
|
|
bool want_noncorner = true);
|
|
|
|
//
|
|
// ***** Jacobian of synchronize() *****
|
|
//
|
|
// n.b. terminology is
|
|
// partial gridfn at x
|
|
// -------------------
|
|
// partial gridfn at y
|
|
//
|
|
|
|
// allocate internal buffers, compute Jacobian
|
|
// ... this function is a no-op in this class
|
|
void compute_Jacobian(int ghosted_min_gfn, int ghosted_max_gfn,
|
|
bool want_corners = true,
|
|
bool want_noncorner = true)
|
|
const
|
|
{
|
|
}
|
|
|
|
// what is the [min,max] range of m for this ghost zone?
|
|
int Jacobian_min_y_ipar_m() const { return 0; }
|
|
int Jacobian_max_y_ipar_m() const { return 0; }
|
|
|
|
// what is the oiperp of the Jacobian points (= iperp in their patch)?
|
|
virtual int Jacobian_y_iperp(int x_iperp) const
|
|
{
|
|
return iperp_map_of_iperp(x_iperp);
|
|
}
|
|
|
|
// what is the posn value of the points in this Jacobian row?
|
|
int Jacobian_y_ipar_posn(int x_iperp, int x_ipar) const
|
|
{
|
|
return ipar_map_of_ipar(x_ipar);
|
|
}
|
|
|
|
// what is the Jacobian
|
|
// partial synchronize() px.gridfn(ghosted_gfn, x_iperp, x_ipar)
|
|
// -------------------------------------------------------------
|
|
// partial py.gridfn(ghosted_gfn, y_iperp, y_posn+y_ipar_m)
|
|
// where
|
|
// y_iperp = Jacobian_y_iperp(x_iperp)
|
|
// y_posn = Jacobian_y_ipar_posn(x_iperp, x_ipar)
|
|
fp Jacobian(int x_iperp, int x_ipar, int y_ipar_m) const
|
|
{
|
|
return (y_ipar_m == 0) ? 1.0 : 0.0;
|
|
}
|
|
|
|
//
|
|
// ***** low-level client interface *****
|
|
//
|
|
|
|
// symmetry-map coordinates
|
|
int iperp_map_of_iperp(int iperp) const
|
|
{
|
|
return iperp_map_->map(iperp);
|
|
}
|
|
int ipar_map_of_ipar(int ipar) const
|
|
{
|
|
return ipar_map_->map(ipar);
|
|
}
|
|
fp fp_sign_of_iperp_map() const
|
|
{
|
|
return iperp_map_->fp_sign();
|
|
}
|
|
fp fp_sign_of_ipar_map() const
|
|
{
|
|
return ipar_map_->fp_sign();
|
|
}
|
|
|
|
// min/max ipar of the ghost zone
|
|
// ... we always include the corners
|
|
// (cf. the example at the start of this file)
|
|
int min_ipar(int iperp) const { return extreme_min_ipar(); }
|
|
int max_ipar(int iperp) const { return extreme_max_ipar(); }
|
|
|
|
//
|
|
// ***** constructors, destructor *****
|
|
//
|
|
public:
|
|
// constructor for mirror-symmetry ghost zone
|
|
symmetry_ghost_zone(const patch_edge &my_edge_in);
|
|
|
|
// constructor for periodic-symmetry ghost zone
|
|
// ... ipar mapping specified by giving sample point and mapping sign
|
|
symmetry_ghost_zone(const patch_edge &my_edge_in, const patch_edge &other_edge_in,
|
|
int my_edge_sample_ipar, int other_edge_sample_ipar,
|
|
bool ipar_map_is_plus);
|
|
|
|
~symmetry_ghost_zone();
|
|
|
|
private:
|
|
// we forbid copying and passing by value
|
|
// by declaring the copy constructor and assignment operator
|
|
// private, but never defining them
|
|
symmetry_ghost_zone(const symmetry_ghost_zone &rhs);
|
|
symmetry_ghost_zone &operator=(const symmetry_ghost_zone &rhs);
|
|
|
|
private:
|
|
// symmetry mappings for (iperp,ipar)
|
|
// ... we own these objects
|
|
const jtutil::cpm_map<fp> *iperp_map_;
|
|
const jtutil::cpm_map<fp> *ipar_map_;
|
|
};
|
|
|
|
//*****************************************************************************
|
|
|
|
//
|
|
// interpatch_ghost_zone - derived class for interpatch ghost zone of a patch
|
|
//
|
|
// A ghost_zone object maps (my_iperp,my_ipar) coordinates to the other
|
|
// patch's (other_iperp,other_par) coordinates, then calls the other patch's
|
|
// patch_interp object to interpolate the other patch's data to those
|
|
// coordinates.
|
|
//
|
|
// Note that as described in the "design notes for ghost zones"
|
|
// comments above, interpatch_ghost_zone objects are constructed in
|
|
// the 2nd and 3rd phase of the overall construction process described
|
|
// at the comments at the start of "patch.hh"
|
|
// [done by our constructor]
|
|
// - set up the object itslf and its links to/from the patches and
|
|
// their edges
|
|
// [done by finish_setup()]
|
|
// - set up the interpatch mapping information, data pointers, and
|
|
// interpolation result buffer
|
|
// - construct the patch_interp object to interpolate from the other
|
|
// patch, and save a pointer to it
|
|
//
|
|
|
|
class patch_interp;
|
|
|
|
class interpatch_ghost_zone
|
|
: public ghost_zone
|
|
{
|
|
public:
|
|
//
|
|
// ***** main high-level client interface *****
|
|
//
|
|
// "synchronize" a ghost zone, i.e. update the ghost-zone
|
|
// values of the specified gridfns via the appropriate
|
|
// interpatch interpolations
|
|
// (flags specify which part(s) of the ghost zone we want)
|
|
//
|
|
// ... the present implementation only supports the case where
|
|
// both flags are set
|
|
//
|
|
void synchronize(int ghosted_min_gfn, int ghosted_max_gfn,
|
|
bool want_corners = true,
|
|
bool want_noncorner = true);
|
|
|
|
//
|
|
// ***** Jacobian of synchronize() *****
|
|
//
|
|
// n.b. terminology is
|
|
// partial gridfn at x
|
|
// -------------------
|
|
// partial gridfn at y
|
|
//
|
|
|
|
// allocate internal buffers, compute Jacobian
|
|
//
|
|
// ... the present implementation only supports the case where
|
|
// both flags are set
|
|
//
|
|
void compute_Jacobian(int ghosted_min_gfn, int ghosted_max_gfn,
|
|
bool want_corners = true,
|
|
bool want_noncorner = true)
|
|
const;
|
|
|
|
// what is the [min,max] range of m for this ghost zone?
|
|
int Jacobian_min_y_ipar_m() const { return Jacobian_min_y_ipar_m_; }
|
|
int Jacobian_max_y_ipar_m() const { return Jacobian_max_y_ipar_m_; }
|
|
|
|
// what is the iperp of the Jacobian y points in their (y) patch?
|
|
// ... the ipar row of grid points is actually the same, so
|
|
// we just have to translate x_iperp to the y patch's coordinates
|
|
int Jacobian_y_iperp(int x_iperp) const { return other_iperp(x_iperp); }
|
|
|
|
// what is the posn value of the y points in this Jacobian row?
|
|
int Jacobian_y_ipar_posn(int x_iperp, int x_ipar) const
|
|
{
|
|
assert(Jacobian_y_ipar_posn_ != NULL);
|
|
const int y_iperp = Jacobian_y_iperp(x_iperp);
|
|
return (*Jacobian_y_ipar_posn_)(y_iperp, x_ipar);
|
|
}
|
|
|
|
// what is the Jacobian
|
|
// partial synchronize() px.gridfn(ghosted_gfn, x_iperp, x_ipar)
|
|
// -------------------------------------------------------------
|
|
// partial py.gridfn(ghosted_gfn, y_iperp, y_posn+y_ipar_m)
|
|
// where
|
|
// y_iperp = Jacobian_y_iperp(x_iperp)
|
|
// y_posn = Jacobian_y_ipar_posn(x_iperp, x_ipar)
|
|
fp Jacobian(int x_iperp, int x_ipar, int y_ipar_m) const
|
|
{
|
|
assert(Jacobian_buffer_ != NULL);
|
|
assert(y_ipar_m >= Jacobian_min_y_ipar_m_);
|
|
assert(y_ipar_m <= Jacobian_max_y_ipar_m_);
|
|
const int y_iperp = Jacobian_y_iperp(x_iperp);
|
|
return (*Jacobian_buffer_)(y_iperp, x_ipar, y_ipar_m);
|
|
}
|
|
|
|
//
|
|
// ***** low-level client interface *****
|
|
//
|
|
|
|
public:
|
|
// check consistency of this and the other patch's ghost zones
|
|
// and patch_interp objects
|
|
void assert_fully_setup() const;
|
|
|
|
// min/max ipar of the ghost zone for specified iperp
|
|
// with possibly "triangular" corners depending on the type
|
|
// (symmetry vs interpatch) of the adjacent ghost zones
|
|
// (cf. comments & example at the start of this file)
|
|
int min_ipar(int iperp) const;
|
|
int max_ipar(int iperp) const;
|
|
|
|
// convert our iperp --> other patch's iperp
|
|
int other_iperp(int iperp) const
|
|
{
|
|
assert(other_iperp_ != NULL);
|
|
return other_iperp_->map(iperp);
|
|
}
|
|
|
|
//
|
|
// ***** constructor, finish setup, destructor *****
|
|
//
|
|
public:
|
|
interpatch_ghost_zone(const patch_edge &my_edge_in,
|
|
const patch_edge &other_edge_in,
|
|
int patch_overlap_width);
|
|
|
|
// finish setup (requires adjacent-side ghost_zone objects
|
|
// to exist, though not to have finish_setup() called):
|
|
// - setup par coordinate mapping information
|
|
// - setup interpatch interpolator data pointers & result buffer
|
|
// - create patch_interp object to interpolate from *other* patch
|
|
void finish_setup(int interp_handle, int interp_par_table_handle);
|
|
|
|
~interpatch_ghost_zone();
|
|
|
|
private:
|
|
// we forbid copying and passing by value
|
|
// by declaring the copy constructor and assignment operator
|
|
// private, but never defining them
|
|
interpatch_ghost_zone(const interpatch_ghost_zone &rhs);
|
|
interpatch_ghost_zone &operator=(const interpatch_ghost_zone &rhs);
|
|
|
|
private:
|
|
//
|
|
// all the remaining pointers are initialized to NULL pointers
|
|
// in our constructor, then finally allocated and set up by
|
|
// finish_setup() or compute_Jacobian() as appropriate
|
|
//
|
|
// FIXME: should these be moved out into a separate object/class
|
|
// for the interp stuff and/or another one for the Jacobian?
|
|
//
|
|
|
|
// see comment in "patch_interp.hh" for why this is "const"
|
|
const patch_interp *other_patch_interp_;
|
|
|
|
// other patch's iperp coordinates of our ghost zone points
|
|
// ... maps my_iperp --> other_iperp
|
|
jtutil::cpm_map<fp> *other_iperp_;
|
|
|
|
// min/max values of other patch's iperp coordinates
|
|
// of our ghost zone points
|
|
int min_other_iperp_, max_other_iperp_;
|
|
|
|
// [min,max]_ipar used at each other_iperp
|
|
// ... we will pass these arrays by reference
|
|
// to the other patch's patch_interp object
|
|
// ... index is (other_iperp)
|
|
jtutil::array1d<int> *min_ipar_used_;
|
|
jtutil::array1d<int> *max_ipar_used_;
|
|
|
|
// other patch's (fp) parallel coordinates of our ghost zone points
|
|
// ... we will pass this array by reference
|
|
// to the other patch's patch_interp object
|
|
// using my_ipar as the patch_interp's parindex
|
|
// ... subscripts are (other_iperp, my_ipar)
|
|
jtutil::array2d<fp> *other_par_;
|
|
|
|
// buffer into which the other patch's patch_interp object
|
|
// will store the interpolated gridfn values
|
|
// ... we will pass this array by reference
|
|
// to the other patch's patch_interp object
|
|
// using my_ipar as the patch_interp's parindex
|
|
// ... subscripts are (gfn, other_iperp,my_ipar)
|
|
jtutil::array3d<fp> *interp_result_buffer_;
|
|
|
|
//
|
|
// stuff computed by compute_Jacobian()
|
|
//
|
|
// n.b. terminology is
|
|
// partial gridfn at x
|
|
// -------------------
|
|
// partial gridfn at y
|
|
//
|
|
mutable int Jacobian_min_y_ipar_m_, Jacobian_max_y_ipar_m_;
|
|
|
|
// other patch's y ipar posn for a Jacobian row
|
|
// ... subscripts are (oiperp, ipar)
|
|
mutable jtutil::array2d<CCTK_INT> *Jacobian_y_ipar_posn_;
|
|
|
|
// Jacobian values
|
|
// ... subscripts are (y_iperp, x_ipar, y_ipar_m)
|
|
mutable jtutil::array3d<fp> *Jacobian_buffer_;
|
|
};
|
|
|
|
//******************************************************************************
|
|
|
|
} // namespace AHFinderDirect
|
|
#endif /* GHOST_ZONE_H*/
|