Files
AMSS-NCKU/AMSS_NCKU_source/patch.h
2026-01-13 15:01:15 +08:00

1151 lines
40 KiB
C++

#ifndef TPATCH_H
#define TPATCH_H
namespace AHFinderDirect
{
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//
// ***** how patch boundaries are handled *****
//
//
// Basically, we handle patch boundaries using the usual "ghost zone"
// technique, interpolating values from neighboring patches as necessary.
//
// In more detail, we use the following interrelated types of objects
// to handle patch boundaries:
//
// A patch_edge object represents the basic geometry of a min/max
// rho/sigma side of a patch, i.e. it provides which-side-am-I predicates,
// coordinate conversions between (perp,par) and (rho,sigma), etc.
// Every patch has (points to) 4 patch_edge objects, one for each of
// the patch's sides.
//
// A ghost_zone object describes a patch's ghost zone, and knows how
// to fill in gridfns there 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 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.
//
// A patch_interp object does the actual interpolation of data from
// within a patch (for filling in data in another patch's ghost zone).
// A patch_interp object points to the patch and patch_edge where it
// will be interpolating.
//
// For example, suppose we have two patches p and q with a common
// angular boundary. Then the desired network of pointers looks like
// this (omitting the patch_edge objects for simplicity):
//
// +-----+ +-----+
// | | <--> p.interpatch_ghost_zone ---> q.patch_interp ---> | |
// | p | | q |
// | | <--- p.patch_interp <--- q.interpatch_ghost_zone <--> | |
// +-----+ +-----+
//
// Because of the mutual pointers, we can't easily construct (say) p's
// interpatch_ghost_zone until after q itself has been constructed, and
// vice versa. Moreover, the patch_interp:: constructor needs the
// adjacent-side ghost_zone objects to already exist, and it needs to
// know the iperp range of the interpolation region, which can only be
// computed from the adjacent-patch interpatch_ghost_zone object.
//
// The solution adopted here is to use a 3-phase algorithm, ultimately
// driven by the patch_system constructor:
// * The patch constructors themselves construct the patch_edge objects
// and links them to/from the patches.
// * The patch_system constructor calls the appropriate functions
// patch::create_mirror_symmetry_ghost_zone()
// patch::create_periodic_symmetry_ghost_zone()
// patch::create_interpatch_ghost_zone()
// to construct the ghost_zone objects and link them to/from the
// patches.
// * The patch_system constructor calls the functions
// interpatch_ghost_zone::finish_setup()
// to finish setting up the interpatch_ghost_zone objects, construct
// the other patch's patch_interp objects, and finish linking the
// interpatch_ghost_zone objects to the patch_interp objects.
//
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//
// patch - abstract base class to describe a generic coordinate/grid patch
//
//
// There are 3 types of patches, z, x, and y. Each type uses two of
// (mu,nu,phi) as its angular coordinates (rho,sigma); the remaining
// "unused" one of (mu,nu,phi) is tau.
//
// z patch ==> (rho,sigma) = (mu,nu) tau = phi
// x patch ==> (rho,sigma) = (nu,phi) tau = mu
// y patch ==> (rho,sigma) = (mu,phi) tau = nu
//
// forward declarations
class patch_edge;
class ghost_zone;
class symmetry_ghost_zone;
class interpatch_ghost_zone;
class patch_interp;
class patch_system;
//
// const qualifiers refer to the gridfn values
//
class patch
: public fd_grid
{
//
// ***** patch system, type, and coordinate metadata *****
//
public:
// to which patch system do we belong?
patch_system &my_patch_system() const
{
return my_patch_system_;
}
// each patch has a unique 0-origin small-integer patch number,
// usually denoted pn
int patch_number() const { return patch_number_; }
// each patch has a unique human-readable patch name for debugging etc
const char *name() const { return name_; } // typically "+z" etc
// are we a +[xyz] or -[xyz] patch?
bool is_plus() const { return is_plus_; }
// ... values for the is_plus_in constructor argument
// FIXME: these should really be bool, but then we couldn't
// use the "enum hack" for in-class constants
enum
{
patch_is_plus = true,
patch_is_minus = false
};
// are we a (+/-) x or y or z patch?
// ... n.b. type is `char' because this is handy for both
// switch() and human-readable printing
char ctype() const { return ctype_; } // 'z' or 'x' or 'y'
// are two patches really the same patch?
// n.b. this does *not* compare any of the gridfn data!
bool operator==(const patch &other_patch) const
{
return this == &other_patch;
}
bool operator!=(const patch &other_patch) const
{
return !operator==(other_patch);
}
// (rho,sigma,tau) coordinates as singleton coordinate sets
local_coords::coords_set coords_set_rho() const
{
return coords_set_rho_;
}
local_coords::coords_set coords_set_sigma() const
{
return coords_set_sigma_;
}
local_coords::coords_set coords_set_tau() const
{
return coords_set_tau_;
}
// {rho,sigma} coordinate set
local_coords::coords_set coords_set_rho_sigma() const
{
return coords_set_rho() | coords_set_sigma();
}
// (rho,sigma) coordinates as human-readable character strings
// (for labelling output files etc)
virtual const char *name_of_rho() const = 0;
virtual const char *name_of_sigma() const = 0;
//
// ***** (rho,sigma,tau) coordinates *****
//
public:
// convert (rho,sigma) --> tau
virtual fp tau_of_rho_sigma(fp rho, fp sigma) const = 0;
// convert (rho,sigma) --> (mu,nu,phi)
virtual fp mu_of_rho_sigma(fp rho, fp sigma) const = 0;
virtual fp nu_of_rho_sigma(fp rho, fp sigma) const = 0;
virtual fp phi_of_rho_sigma(fp rho, fp sigma) const = 0;
// convert (rho,sigma) <--> usual polar spherical (theta,phi)
virtual void theta_phi_of_rho_sigma(fp rho, fp sigma,
fp &ps_theta, fp &ps_phi)
const = 0;
virtual void rho_sigma_of_theta_phi(fp ps_theta, fp ps_phi,
fp &rho, fp &sigma)
const = 0;
// convert (r,rho,sigma) <--> local (x,y,z)
virtual void xyz_of_r_rho_sigma(fp r, fp rho, fp sigma,
fp &x, fp &y, fp &z)
const = 0;
virtual fp rho_of_xyz(fp x, fp y, fp z) const = 0;
virtual fp sigma_of_xyz(fp x, fp y, fp z) const = 0;
// convert (rho,sigma) --> direction cosines (xcos,ycos,zcos)
// with respect to the local coordinate system
virtual void xyzcos_of_rho_sigma(fp rho, fp sigma,
fp &xcos, fp &ycos, fp &zcos)
const = 0;
// partial (x,y,z) / partial (rho,sigma)
virtual void partial_xyz_wrt_r_rho_sigma(fp r, fp rho, fp sigma,
fp &partial_x_wrt_r, fp &partial_x_wrt_rho, fp &partial_x_wrt_sigma,
fp &partial_y_wrt_r, fp &partial_y_wrt_rho, fp &partial_y_wrt_sigma,
fp &partial_z_wrt_r, fp &partial_z_wrt_rho, fp &partial_z_wrt_sigma)
const = 0;
// partial (rho,sigma) / partial (x,y,z)
virtual fp partial_rho_wrt_x(fp x, fp y, fp z) const = 0;
virtual fp partial_rho_wrt_y(fp x, fp y, fp z) const = 0;
virtual fp partial_rho_wrt_z(fp x, fp y, fp z) const = 0;
virtual fp partial_sigma_wrt_x(fp x, fp y, fp z) const = 0;
virtual fp partial_sigma_wrt_y(fp x, fp y, fp z) const = 0;
virtual fp partial_sigma_wrt_z(fp x, fp y, fp z) const = 0;
// partial^2 (rho,sigma) / partial (xx,xy,xz,yy,yz)
virtual fp partial2_rho_wrt_xx(fp x, fp y, fp z) const = 0;
virtual fp partial2_rho_wrt_xy(fp x, fp y, fp z) const = 0;
virtual fp partial2_rho_wrt_xz(fp x, fp y, fp z) const = 0;
virtual fp partial2_rho_wrt_yy(fp x, fp y, fp z) const = 0;
virtual fp partial2_rho_wrt_yz(fp x, fp y, fp z) const = 0;
virtual fp partial2_rho_wrt_zz(fp x, fp y, fp z) const = 0;
virtual fp partial2_sigma_wrt_xx(fp x, fp y, fp z) const = 0;
virtual fp partial2_sigma_wrt_xy(fp x, fp y, fp z) const = 0;
virtual fp partial2_sigma_wrt_xz(fp x, fp y, fp z) const = 0;
virtual fp partial2_sigma_wrt_yy(fp x, fp y, fp z) const = 0;
virtual fp partial2_sigma_wrt_yz(fp x, fp y, fp z) const = 0;
virtual fp partial2_sigma_wrt_zz(fp x, fp y, fp z) const = 0;
// compute (rho,sigma) 2-D induced metric from 3-D xyz metric
// as per p.33 of my apparent horizon finding notes
// ... returns Jacobian of (rho,sigma) 2-D induced metric
fp rho_sigma_metric(fp r, fp rho, fp sigma,
fp partial_surface_r_wrt_rho,
fp partial_surface_r_wrt_sigma,
fp g_xx, fp g_xy, fp g_xz,
fp g_yy, fp g_yz,
fp g_zz,
fp &g_rho_rho, fp &g_rho_sigma,
fp &g_sigma_sigma)
const;
// plotting coordinates (dpx,dpy)
// ... character string describing how (dpx,dpy) are
// defined in terms of (mu,nu,phi), eg "90 - drho = 90 - dphi"
// (used for labelling output files)
virtual const char *name_of_dpx() const = 0;
virtual const char *name_of_dpy() const = 0;
// ... (irho,isimga) --> (px,py)
virtual fp dpx_of_rho_sigma(fp rho, fp sigma) const = 0;
virtual fp dpy_of_rho_sigma(fp rho, fp sigma) const = 0;
//
// ***** line/surface integrals *****
//
public:
//
// The following enum describes the integration methods supported
// by integrate_gridfn() .
//
// For convenience of exposition we describe the methods as if for
// 1-D integration, but integrate_gridfn() actually does 2-D
// (surface) integration over the patch.
//
// Suppose we're computing $\int_{x_0}^{x^N} f(x) \, dx$, using the
// equally spaced integration points $f_0$, $f_1$, \dots, $f_N$,
// spaced $\Delta x$ apart. Then the integration methods are as
// follows, with the convention that $\langle X \rangle$ denotes
// indefinite repetition of the "X" terms, depending on N:
//
enum integration_method
{
// Trapezoid rule
// ... character-string name "trapezoid" or "trapezoid rule"
// ... 2nd order accurate for smooth functions
// ... requires N >= 1
// $$
// \Delta x \left[
// \half f_0
// + \langle
// f_k
// \rangle
// + \half f_N
// \right]
// $$
integration_method__trapezoid,
// Simpson's rule
// ... character-string name "Simpson" or "Simpson's rule"
// ... 4th order accurate for smooth functions
// ... requires N >= 2 and N even
// $$
// \Delta x \left[
// \frac{1}{3} f_0
// + \frac{4}{3} f_1
// + \langle
// \frac{2}{3} f_{2k} + \frac{4}{3} f_{2k+1}
// \rangle
// + \frac{1}{3} f_N
// \right]
// $$
integration_method__Simpson,
// Simpson's rule, variant form
// ... characgter-string name "Simpson (variant)"
// or "Simpson's rule (variant)"
// ... described in Numerical Recipes 1st edition (4.1.14)
// ... 4th order accurate for smooth functions
// ... requires N >= 7
// $$
// \Delta x \left[
// \frac{17}{48} f_0
// + \frac{59}{48} f_1
// + \frac{43}{48} f_2
// + \frac{49}{48} f_3
// + \langle
// f_k
// \rangle
// + \frac{49}{48} f_{N-3}
// + \frac{43}{48} f_{N-2}
// + \frac{59}{48} f_{N-1}
// + \frac{17}{48} f_N
// \right]
// $$
integration_method__Simpson_variant,
// automatic choice of the "best" one of the above methods:
// ... i.e. choose Simpson's rule or variant if applicable,
// otherwise trapezoid rule
// N == 2 Simpson's rule
// N == 3 trapezoid rule
// N == 4 Simpson's rule
// N == 5 trapezoid rule
// N == 6 Simpson's rule
// N >= 7 Simpson's rule, variant form
integration_method__automatic_choice // no comma here!
};
// decode character string name into internal enum
static enum integration_method
decode_integration_method(const char method_string[]);
// compute the arc length of a surface in the specified plane
// (must be one of "xy", "xz", or "yz") over the patch's nominal bounds
// ... error_exit() if plane is invalid and/or
// the patch doesn't contain that coordinate plane
virtual fp plane_arc_length(const char plane[],
int ghosted_radius_gfn,
int g_xx_gfn, int g_xy_gfn, int g_xz_gfn,
int g_yy_gfn, int g_yz_gfn,
int g_zz_gfn,
enum integration_method method)
const = 0;
// ... along the rho direction (i.e. in a dsigma=constant plane
// where dsigma is a multiple of 90 degrees)
fp rho_arc_length(int ghosted_radius_gfn,
int g_xx_gfn, int g_xy_gfn, int g_xz_gfn,
int g_yy_gfn, int g_yz_gfn,
int g_zz_gfn,
enum integration_method method)
const;
// ... along the sigma direction (i.e. in a drho=constant plane
// where drho is a multiple of 90 degrees)
fp sigma_arc_length(int ghosted_radius_gfn,
int g_xx_gfn, int g_xy_gfn, int g_xz_gfn,
int g_yy_gfn, int g_yz_gfn,
int g_zz_gfn,
enum integration_method method)
const;
// compute the surface integral of a gridfn over the patch's
// nominal area,
// $\int f(\rho,\sigma) \, dA$
// = \int f(\rho,\sigma) \sqrt{|J|} \, d\rho \, d\sigma$
// where $J$ is the Jacobian of $(x,y,z)$ with respect to $(rho,sigma)
// ... integration method selected by method argument
// ... src gridfn may be either nominal-grid or ghosted-grid
// (n.b. in the latter case the integral is still done
// only over the patch's nominal area)
fp integrate_gridfn(int unknown_src_gfn,
int ghosted_radius_gfn,
int g_xx_gfn, int g_xy_gfn, int g_xz_gfn,
int g_yy_gfn, int g_yz_gfn,
int g_zz_gfn,
enum integration_method method)
const;
// compute integration coefficient $c_i$ where
// $\int_{x_0}^{x_N} f(x) \, dx
// \approx \Delta x \, \sum_{i=0}^N c_i f(x_i)$
private:
static fp integration_coeff(enum integration_method method, int N, int i);
//
// ***** patch edges ****
//
public:
const patch_edge &min_rho_patch_edge() const
{
return min_rho_patch_edge_;
}
const patch_edge &max_rho_patch_edge() const
{
return max_rho_patch_edge_;
}
const patch_edge &min_sigma_patch_edge() const
{
return min_sigma_patch_edge_;
}
const patch_edge &max_sigma_patch_edge() const
{
return max_sigma_patch_edge_;
}
const patch_edge &minmax_ang_patch_edge(bool want_min, bool want_rho)
const
{
return want_min ? (want_rho ? min_rho_patch_edge()
: min_sigma_patch_edge())
: (want_rho ? max_rho_patch_edge()
: max_sigma_patch_edge());
}
// find which patch edge is adjacent to neighboring patch q,
// or error_exit() if it's not actually a neighboring patch
// ... computation done using only (rho,sigma) coordinate sets
// and min/max dang bounds ==> ok to use in setting up ghost zones
// ... patch_overlap_width = number of grid points (grid spacings
// in the perpendicular direction) these patches' nominal grids
// overlap,
// ... if this is nonzero, then these patches must have
// the *same* grid spacing in the perpendicular direction
// ... e.g. delta_dang = 5, this patch max_dang = 50,
// other patch min_dang = 40 ==> patch_overlap_width = 3
// p p p p p
// q q q q q
const patch_edge &edge_adjacent_to_patch(const patch &q,
int patch_overlap_width = 0)
const;
//
// ***** ghost zones *****
//
public:
ghost_zone &min_rho_ghost_zone() const
{
assert(min_rho_ghost_zone_ != NULL);
return *min_rho_ghost_zone_;
}
ghost_zone &max_rho_ghost_zone() const
{
assert(max_rho_ghost_zone_ != NULL);
return *max_rho_ghost_zone_;
}
ghost_zone &min_sigma_ghost_zone() const
{
assert(min_sigma_ghost_zone_ != NULL);
return *min_sigma_ghost_zone_;
}
ghost_zone &max_sigma_ghost_zone() const
{
assert(max_sigma_ghost_zone_ != NULL);
return *max_sigma_ghost_zone_;
}
ghost_zone &minmax_rho_ghost_zone(bool want_min)
const
{
return want_min ? min_rho_ghost_zone()
: max_rho_ghost_zone();
}
ghost_zone &minmax_sigma_ghost_zone(bool want_min)
const
{
return want_min ? min_sigma_ghost_zone()
: max_sigma_ghost_zone();
}
ghost_zone &minmax_ang_ghost_zone(bool want_min, bool want_rho)
const
{
return want_rho ? minmax_rho_ghost_zone(want_min)
: minmax_sigma_ghost_zone(want_min);
}
ghost_zone &ghost_zone_on_edge(const patch_edge &e) const;
// which of the two ghost zones at a specified corner,
// contains a specified point?
ghost_zone &corner_ghost_zone_containing_point(bool rho_is_min, bool sigma_is_min, // specifies corner
int irho, int isigma) // specifies point
const;
// which ghost zone contains a specified noncorner point?
ghost_zone &ghost_zone_containing_noncorner_point(int irho, int isigma)
const;
//
// ***** set up ghost zones
//
public:
// assert() that this ghost zone hasn't been set up yet,
// then set it up as mirror-symmetry
void create_mirror_symmetry_ghost_zone(const patch_edge &edge);
// assert() that this ghost zone hasn't been set up yet,
// then set it up as periodic-symmetry
void create_periodic_symmetry_ghost_zone(const patch_edge &my_edge, const patch_edge &other_edge,
bool ipar_map_is_plus);
// assert() that this ghost zone hasn't been set up yet,
// then set it up as interpatch
// ... this only sets up ghost zone in skeletal form; use
// interpatch_ghost_zone::finish_setup() to complete
// the setup process
void create_interpatch_ghost_zone(const patch_edge &my_edge, const patch_edge &other_edge,
int patch_overlap_width);
// assert() that all ghost zones
// are fully setup
void assert_all_ghost_zones_fully_setup() const;
private:
// helper function for setup_*_ghost_zone():
// assert() that ghost zone pointer on specified edge is NULL
// (i.e. that we haven't already setup this ghost zone),
// then assign new value to it
void set_ghost_zone(const patch_edge &edge, ghost_zone *gzp);
//
// ***** constructor, destructor, et al *****
//
protected:
// ... used only from derived classes
// ... doesn't set up ghost zone info, since this depends on
// knowing our neighbouring patches, which might not exist yet
// ... saves a pointer to name_in[], so this should have a
// lifetime at least as long as that of this object
patch(patch_system &my_patch_system_in, int patch_number_in,
const char name_in[], bool is_plus_in, char ctype_in,
local_coords::coords_set coords_set_rho_in,
local_coords::coords_set coords_set_sigma_in,
local_coords::coords_set coords_set_tau_in,
const grid_arrays::grid_array_pars &grid_array_pars_in,
const grid::grid_pars &grid_pars_in);
public:
// destructor must be virtual to allow destruction
// of derived classes via ptr/ref to this class
virtual ~patch();
private:
// we forbid copying and passing by value
// by declaring the copy constructor and assignment operator
// private, but never defining them
patch(const patch &rhs);
patch &operator=(const patch &rhs);
//
// ***** data members *****
//
private:
// type/coordinate metadata
patch_system &my_patch_system_;
const int patch_number_;
const char *name_;
const bool is_plus_;
const char ctype_;
const local_coords::coords_set coords_set_rho_,
coords_set_sigma_,
coords_set_tau_;
// edges
const patch_edge &min_rho_patch_edge_;
const patch_edge &max_rho_patch_edge_;
const patch_edge &min_sigma_patch_edge_;
const patch_edge &max_sigma_patch_edge_;
// ghost zones
// ... pointers are set to NULL by ctor,
// reset to non-NULL by set_ghost_zone(), which is called by
// create_mirror_symmetry_ghost_zone()
// create_periodic_symmetry_ghost_zone()
// create_interpatch_ghost_zone()
ghost_zone *min_rho_ghost_zone_;
ghost_zone *max_rho_ghost_zone_;
ghost_zone *min_sigma_ghost_zone_;
ghost_zone *max_sigma_ghost_zone_;
};
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//
// This class describes a +/- z patch. It doesn't define any new
// functions not already present in class patch ; it "just" defines
// non-virtual versions of all the pure virtual functions defined there.
//
// z patch ==> (rho,sigma) = (mu,nu) tau = phi
//
class z_patch
: public patch
{
public:
// human-readable names of (rho,sigma)
const char *name_of_rho() const { return "mu"; }
const char *name_of_sigma() const { return "nu"; }
// convert (rho,sigma) --> tau
fp tau_of_rho_sigma(fp rho, fp sigma) const
{
return local_coords::phi_of_mu_nu(rho, sigma);
}
// convert (rho,sigma) --> (mu,nu,phi)
fp mu_of_rho_sigma(fp rho, fp sigma) const { return rho; }
fp nu_of_rho_sigma(fp rho, fp sigma) const { return sigma; }
fp phi_of_rho_sigma(fp rho, fp sigma) const
{
return local_coords::phi_of_mu_nu(rho, sigma);
}
// convert (rho,sigma) <--> usual polar spherical (theta,phi)
void theta_phi_of_rho_sigma(fp rho, fp sigma, fp &ps_theta, fp &ps_phi)
const
{
local_coords::theta_phi_of_mu_nu(rho, sigma, ps_theta, ps_phi);
}
void rho_sigma_of_theta_phi(fp ps_theta, fp ps_phi, fp &rho, fp &sigma)
const
{
local_coords::mu_nu_of_theta_phi(ps_theta, ps_phi, rho, sigma);
}
// convert (r,rho,sigma) <--> (x,y,z)
void xyz_of_r_rho_sigma(fp r, fp rho, fp sigma, fp &x, fp &y, fp &z)
const
{
local_coords::xyz_of_r_mu_nu(r, rho, sigma, x, y, z);
}
fp rho_of_xyz(fp x, fp y, fp z) const
{
return modulo_reduce_rho(local_coords::mu_of_yz(y, z));
}
fp sigma_of_xyz(fp x, fp y, fp z) const
{
return modulo_reduce_sigma(local_coords::nu_of_xz(x, z));
}
// convert (rho,sigma) --> direction cosines (xcos,ycos,zcos)
// with respect to the local coordinate system
void xyzcos_of_rho_sigma(fp rho, fp sigma,
fp &xcos, fp &ycos, fp &zcos)
const
{
local_coords::xyzcos_of_mu_nu(rho, sigma, xcos, ycos, zcos);
}
// partial (x,y,z) / partial (rho,sigma)
void partial_xyz_wrt_r_rho_sigma(fp r, fp rho, fp sigma,
fp &partial_x_wrt_r, fp &partial_x_wrt_rho, fp &partial_x_wrt_sigma,
fp &partial_y_wrt_r, fp &partial_y_wrt_rho, fp &partial_y_wrt_sigma,
fp &partial_z_wrt_r, fp &partial_z_wrt_rho, fp &partial_z_wrt_sigma)
const
{
local_coords::partial_xyz_wrt_r_mu_nu(r, rho, sigma,
partial_x_wrt_r, partial_x_wrt_rho, partial_x_wrt_sigma,
partial_y_wrt_r, partial_y_wrt_rho, partial_y_wrt_sigma,
partial_z_wrt_r, partial_z_wrt_rho, partial_z_wrt_sigma);
}
// partial (rho,sigma) / partial (x,y,z)
fp partial_rho_wrt_x(fp x, fp y, fp z) const { return 0.0; }
fp partial_rho_wrt_y(fp x, fp y, fp z) const
{
return local_coords::partial_mu_wrt_y(y, z);
}
fp partial_rho_wrt_z(fp x, fp y, fp z) const
{
return local_coords::partial_mu_wrt_z(y, z);
}
fp partial_sigma_wrt_x(fp x, fp y, fp z) const
{
return local_coords::partial_nu_wrt_x(x, z);
}
fp partial_sigma_wrt_y(fp x, fp y, fp z) const { return 0.0; }
fp partial_sigma_wrt_z(fp x, fp y, fp z) const
{
return local_coords::partial_nu_wrt_z(x, z);
}
// partial^2 (rho,sigma) / partial (xx,xy,xz,yy,yz)
fp partial2_rho_wrt_xx(fp x, fp y, fp z) const { return 0.0; }
fp partial2_rho_wrt_xy(fp x, fp y, fp z) const { return 0.0; }
fp partial2_rho_wrt_xz(fp x, fp y, fp z) const { return 0.0; }
fp partial2_rho_wrt_yy(fp x, fp y, fp z) const
{
return local_coords::partial2_mu_wrt_yy(y, z);
}
fp partial2_rho_wrt_yz(fp x, fp y, fp z) const
{
return local_coords::partial2_mu_wrt_yz(y, z);
}
fp partial2_rho_wrt_zz(fp x, fp y, fp z) const
{
return local_coords::partial2_mu_wrt_zz(y, z);
}
fp partial2_sigma_wrt_xx(fp x, fp y, fp z) const
{
return local_coords::partial2_nu_wrt_xx(x, z);
}
fp partial2_sigma_wrt_xy(fp x, fp y, fp z) const { return 0.0; }
fp partial2_sigma_wrt_xz(fp x, fp y, fp z) const
{
return local_coords::partial2_nu_wrt_xz(x, z);
}
fp partial2_sigma_wrt_yy(fp x, fp y, fp z) const { return 0.0; }
fp partial2_sigma_wrt_yz(fp x, fp y, fp z) const { return 0.0; }
fp partial2_sigma_wrt_zz(fp x, fp y, fp z) const
{
return local_coords::partial2_nu_wrt_zz(x, z);
}
// plotting coordinates (px,py)
// ... character string describing how (dpx,dpy) are
// defined in terms of (mu,nu,phi), eg "90 - drho = 90 - dphi"
// (used for labelling output files)
const char *name_of_dpx() const
{
return "dsigma = dnu";
}
const char *name_of_dpy() const
{
return is_plus() ? "drho = dmu" : "180 - drho = 180 - dmu";
}
// ... (irho,isimga) --> (px,py)
fp dpx_of_rho_sigma(fp rho, fp sigma) const
{
return jtutil::degrees_of_radians(sigma);
}
fp dpy_of_rho_sigma(fp rho, fp sigma) const
{
const fp drho = jtutil::degrees_of_radians(rho);
return is_plus() ? drho : 180.0 - drho;
}
// compute the arc length of a surface in the specified plane
// (must be one of "xz" or "yz") over the patch's nominal bounds
// ... error_exit() if plane is invalid
fp plane_arc_length(const char plane[],
int ghosted_radius_gfn,
int g_xx_gfn, int g_xy_gfn, int g_xz_gfn,
int g_yy_gfn, int g_yz_gfn,
int g_zz_gfn,
enum integration_method method)
const;
// constructor, destructor
z_patch(patch_system &my_patch_system_in, int patch_number_in,
const char *name_in, bool is_plus_in,
const grid_arrays::grid_array_pars &grid_array_pars_in,
const grid::grid_pars &grid_pars_in);
~z_patch() {}
private:
// we forbid copying and passing by value
// by declaring the copy constructor and assignment operator
// private, but never defining them
z_patch(const z_patch &rhs);
z_patch &operator=(const z_patch &rhs);
};
//*****************************************************************************
//
// This class describes a +/- x patch. It doesn't define any new
// functions not already present in class patch ; it "just" defines
// non-virtual versions of all the pure virtual functions defined there.
//
// x patch ==> (rho,sigma) = (nu,phi) tau = mu
//
class x_patch
: public patch
{
public:
// human-readable names of (rho,sigma)
const char *name_of_rho() const { return "nu"; }
const char *name_of_sigma() const { return "phi"; }
// convert (rho,sigma) --> tau
fp tau_of_rho_sigma(fp rho, fp sigma) const
{
return local_coords::mu_of_nu_phi(rho, sigma);
}
// convert (rho,sigma) --> (mu,nu,phi)
fp nu_of_rho_sigma(fp rho, fp sigma) const { return rho; }
fp phi_of_rho_sigma(fp rho, fp sigma) const { return sigma; }
fp mu_of_rho_sigma(fp rho, fp sigma) const
{
return local_coords::mu_of_nu_phi(rho, sigma);
}
// convert (rho,sigma) <--> usual polar spherical (theta,phi)
void theta_phi_of_rho_sigma(fp rho, fp sigma, fp &ps_theta, fp &ps_phi)
const
{
local_coords::theta_phi_of_nu_phi(rho, sigma, ps_theta, ps_phi);
}
void rho_sigma_of_theta_phi(fp ps_theta, fp ps_phi, fp &rho, fp &sigma)
const
{
local_coords::nu_phi_of_theta_phi(ps_theta, ps_phi, rho, sigma);
}
// convert (r,rho,sigma) <--> (x,y,z)
void xyz_of_r_rho_sigma(fp r, fp rho, fp sigma, fp &x, fp &y, fp &z)
const
{
local_coords::xyz_of_r_nu_phi(r, rho, sigma, x, y, z);
}
fp rho_of_xyz(fp x, fp y, fp z) const
{
return modulo_reduce_rho(local_coords::nu_of_xz(x, z));
}
fp sigma_of_xyz(fp x, fp y, fp z) const
{
return modulo_reduce_sigma(local_coords::phi_of_xy(x, y));
}
// convert (rho,sigma) --> direction cosines (xcos,ycos,zcos)
// with respect to the local coordinate system
void xyzcos_of_rho_sigma(fp rho, fp sigma,
fp &xcos, fp &ycos, fp &zcos)
const
{
local_coords::xyzcos_of_nu_phi(rho, sigma, xcos, ycos, zcos);
}
// partial (x,y,z) / partial (rho,sigma)
void partial_xyz_wrt_r_rho_sigma(fp r, fp rho, fp sigma,
fp &partial_x_wrt_r, fp &partial_x_wrt_rho, fp &partial_x_wrt_sigma,
fp &partial_y_wrt_r, fp &partial_y_wrt_rho, fp &partial_y_wrt_sigma,
fp &partial_z_wrt_r, fp &partial_z_wrt_rho, fp &partial_z_wrt_sigma)
const
{
local_coords::partial_xyz_wrt_r_nu_phi(r, rho, sigma,
partial_x_wrt_r, partial_x_wrt_rho, partial_x_wrt_sigma,
partial_y_wrt_r, partial_y_wrt_rho, partial_y_wrt_sigma,
partial_z_wrt_r, partial_z_wrt_rho, partial_z_wrt_sigma);
}
// partial (rho,sigma) / partial (x,y,z)
fp partial_rho_wrt_x(fp x, fp y, fp z) const
{
return local_coords::partial_nu_wrt_x(x, z);
}
fp partial_rho_wrt_y(fp x, fp y, fp z) const { return 0.0; }
fp partial_rho_wrt_z(fp x, fp y, fp z) const
{
return local_coords::partial_nu_wrt_z(x, z);
}
fp partial_sigma_wrt_x(fp x, fp y, fp z) const
{
return local_coords::partial_phi_wrt_x(x, y);
}
fp partial_sigma_wrt_y(fp x, fp y, fp z) const
{
return local_coords::partial_phi_wrt_y(x, y);
}
fp partial_sigma_wrt_z(fp x, fp y, fp z) const { return 0.0; }
// partial^2 (rho,sigma) / partial (xx,xy,xz,yy,yz)
fp partial2_rho_wrt_xx(fp x, fp y, fp z) const
{
return local_coords::partial2_nu_wrt_xx(x, z);
}
fp partial2_rho_wrt_xy(fp x, fp y, fp z) const { return 0.0; }
fp partial2_rho_wrt_xz(fp x, fp y, fp z) const
{
return local_coords::partial2_nu_wrt_xz(x, z);
}
fp partial2_rho_wrt_yy(fp x, fp y, fp z) const { return 0.0; }
fp partial2_rho_wrt_yz(fp x, fp y, fp z) const { return 0.0; }
fp partial2_rho_wrt_zz(fp x, fp y, fp z) const
{
return local_coords::partial2_nu_wrt_zz(x, z);
}
fp partial2_sigma_wrt_xx(fp x, fp y, fp z) const
{
return local_coords::partial2_phi_wrt_xx(x, y);
}
fp partial2_sigma_wrt_xy(fp x, fp y, fp z) const
{
return local_coords::partial2_phi_wrt_xy(x, y);
}
fp partial2_sigma_wrt_xz(fp x, fp y, fp z) const { return 0.0; }
fp partial2_sigma_wrt_yy(fp x, fp y, fp z) const
{
return local_coords::partial2_phi_wrt_yy(x, y);
}
fp partial2_sigma_wrt_yz(fp x, fp y, fp z) const { return 0.0; }
fp partial2_sigma_wrt_zz(fp x, fp y, fp z) const { return 0.0; }
// plotting coordinates (px,py)
// ... character string describing how (dpx,dpy) are
// defined in terms of (mu,nu,phi), eg "90 - drho = 90 - dphi"
// (used for labelling output files)
const char *name_of_dpx() const { return "drho = dnu"; }
const char *name_of_dpy() const
{
return is_plus() ? "dsigma = dphi"
: "180 - dsigma = 180 - dphi";
}
// ... (irho,isimga) --> (px,py)
fp dpx_of_rho_sigma(fp rho, fp sigma) const
{
return jtutil::degrees_of_radians(rho);
}
fp dpy_of_rho_sigma(fp rho, fp sigma) const
{
const fp dsigma = jtutil::degrees_of_radians(sigma);
return is_plus() ? dsigma : 180.0 - dsigma;
}
// compute the arc length of a surface in the specified plane
// (must be one of "xy" or "xz") over the patch's nominal bounds
// ... error_exit() if plane is invalid
fp plane_arc_length(const char plane[],
int ghosted_radius_gfn,
int g_xx_gfn, int g_xy_gfn, int g_xz_gfn,
int g_yy_gfn, int g_yz_gfn,
int g_zz_gfn,
enum integration_method method)
const;
// constructor, destructor
x_patch(patch_system &my_patch_system_in, int patch_number_in,
const char *name_in, bool is_plus_in,
const grid_arrays::grid_array_pars &grid_array_pars_in,
const grid::grid_pars &grid_pars_in);
~x_patch() {}
private:
// we forbid copying and passing by value
// by declaring the copy constructor and assignment operator
// private, but never defining them
x_patch(const x_patch &rhs);
x_patch &operator=(const x_patch &rhs);
};
//*****************************************************************************
//
// This class describes a +/- y patch. It doesn't define any new
// functions not already present in class patch ; it "just" defines
// non-virtual versions of all the pure virtual functions defined there.
//
// y patch ==> (rho,sigma) = (mu,phi) tau = nu
//
class y_patch
: public patch
{
public:
// human-readable names of (rho,sigma)
const char *name_of_rho() const { return "mu"; }
const char *name_of_sigma() const { return "phi"; }
// convert (rho,sigma) --> tau
fp tau_of_rho_sigma(fp rho, fp sigma) const
{
return local_coords::nu_of_mu_phi(rho, sigma);
}
// convert (rho,sigma) --> (mu,nu,phi)
fp mu_of_rho_sigma(fp rho, fp sigma) const { return rho; }
fp phi_of_rho_sigma(fp rho, fp sigma) const { return sigma; }
fp nu_of_rho_sigma(fp rho, fp sigma) const
{
return local_coords::nu_of_mu_phi(rho, sigma);
}
// convert (rho,sigma) <--> usual polar spherical (theta,phi)
void theta_phi_of_rho_sigma(fp rho, fp sigma, fp &ps_theta, fp &ps_phi)
const
{
local_coords::theta_phi_of_mu_phi(rho, sigma, ps_theta, ps_phi);
}
void rho_sigma_of_theta_phi(fp ps_theta, fp ps_phi, fp &rho, fp &sigma)
const
{
local_coords::mu_phi_of_theta_phi(ps_theta, ps_phi, rho, sigma);
}
// convert (r,rho,sigma) <--> (x,y,z)
void xyz_of_r_rho_sigma(fp r, fp rho, fp sigma, fp &x, fp &y, fp &z)
const
{
local_coords::xyz_of_r_mu_phi(r, rho, sigma, x, y, z);
}
fp rho_of_xyz(fp x, fp y, fp z) const
{
return modulo_reduce_rho(local_coords::mu_of_yz(y, z));
}
fp sigma_of_xyz(fp x, fp y, fp z) const
{
return modulo_reduce_sigma(local_coords::phi_of_xy(x, y));
}
// convert (rho,sigma) --> direction cosines (xcos,ycos,zcos)
// with respect to the local coordinate system
void xyzcos_of_rho_sigma(fp rho, fp sigma,
fp &xcos, fp &ycos, fp &zcos)
const
{
local_coords::xyzcos_of_mu_phi(rho, sigma, xcos, ycos, zcos);
}
// partial (x,y,z) / partial (rho,sigma)
void partial_xyz_wrt_r_rho_sigma(fp r, fp rho, fp sigma,
fp &partial_x_wrt_r, fp &partial_x_wrt_rho, fp &partial_x_wrt_sigma,
fp &partial_y_wrt_r, fp &partial_y_wrt_rho, fp &partial_y_wrt_sigma,
fp &partial_z_wrt_r, fp &partial_z_wrt_rho, fp &partial_z_wrt_sigma)
const
{
local_coords::partial_xyz_wrt_r_mu_phi(r, rho, sigma,
partial_x_wrt_r, partial_x_wrt_rho, partial_x_wrt_sigma,
partial_y_wrt_r, partial_y_wrt_rho, partial_y_wrt_sigma,
partial_z_wrt_r, partial_z_wrt_rho, partial_z_wrt_sigma);
}
// partial (rho,sigma) / partial (x,y,z)
fp partial_rho_wrt_x(fp x, fp y, fp z) const { return 0.0; }
fp partial_rho_wrt_y(fp x, fp y, fp z) const
{
return local_coords::partial_mu_wrt_y(y, z);
}
fp partial_rho_wrt_z(fp x, fp y, fp z) const
{
return local_coords::partial_mu_wrt_z(y, z);
}
fp partial_sigma_wrt_x(fp x, fp y, fp z) const
{
return local_coords::partial_phi_wrt_x(x, y);
}
fp partial_sigma_wrt_y(fp x, fp y, fp z) const
{
return local_coords::partial_phi_wrt_y(x, y);
}
fp partial_sigma_wrt_z(fp x, fp y, fp z) const { return 0.0; }
// partial^2 (rho,sigma) / partial (xx,xy,xz,yy,yz)
fp partial2_rho_wrt_xx(fp x, fp y, fp z) const { return 0.0; }
fp partial2_rho_wrt_xy(fp x, fp y, fp z) const { return 0.0; }
fp partial2_rho_wrt_xz(fp x, fp y, fp z) const { return 0.0; }
fp partial2_rho_wrt_yy(fp x, fp y, fp z) const
{
return local_coords::partial2_mu_wrt_yy(y, z);
}
fp partial2_rho_wrt_yz(fp x, fp y, fp z) const
{
return local_coords::partial2_mu_wrt_yz(y, z);
}
fp partial2_rho_wrt_zz(fp x, fp y, fp z) const
{
return local_coords::partial2_mu_wrt_zz(y, z);
}
fp partial2_sigma_wrt_xx(fp x, fp y, fp z) const
{
return local_coords::partial2_phi_wrt_xx(x, y);
}
fp partial2_sigma_wrt_xy(fp x, fp y, fp z) const
{
return local_coords::partial2_phi_wrt_xy(x, y);
}
fp partial2_sigma_wrt_xz(fp x, fp y, fp z) const { return 0.0; }
fp partial2_sigma_wrt_yy(fp x, fp y, fp z) const
{
return local_coords::partial2_phi_wrt_yy(x, y);
}
fp partial2_sigma_wrt_yz(fp x, fp y, fp z) const { return 0.0; }
fp partial2_sigma_wrt_zz(fp x, fp y, fp z) const { return 0.0; }
// plotting coordinates (px,py)
// ... character string describing how (dpx,dpy) are
// defined in terms of (mu,nu,phi), eg "90 - drho = 90 - dphi"
// (used for labelling output files)
const char *name_of_dpx() const
{
return is_plus() ? "90 - dsigma = 90 - dphi"
: "90 + dsigma = 90 + dphi";
}
const char *name_of_dpy() const { return "drho = dmu"; }
// ... (rho,simga) --> (px,py)
fp dpx_of_rho_sigma(fp rho, fp sigma) const
{
const fp dsigma = jtutil::degrees_of_radians(sigma);
return is_plus() ? 90.0 - dsigma : 90.0 + dsigma;
}
fp dpy_of_rho_sigma(fp rho, fp sigma) const
{
return jtutil::degrees_of_radians(rho);
}
// compute the arc length of a surface in the specified plane
// (must be one of "xy" or "yz") over the patch's nominal bounds
// ... error_exit() if plane is invalid
fp plane_arc_length(const char plane[],
int ghosted_radius_gfn,
int g_xx_gfn, int g_xy_gfn, int g_xz_gfn,
int g_yy_gfn, int g_yz_gfn,
int g_zz_gfn,
enum integration_method method)
const;
// constructor, destructor
y_patch(patch_system &my_patch_system_in, int patch_number_in,
const char *name_in, bool is_plus_in,
const grid_arrays::grid_array_pars &grid_array_pars_in,
const grid::grid_pars &grid_pars_in);
~y_patch() {}
private:
// we forbid copying and passing by value
// by declaring the copy constructor and assignment operator
// private, but never defining them
y_patch(const y_patch &rhs);
y_patch &operator=(const y_patch &rhs);
};
//******************************************************************************
} // namespace AHFinderDirect
#endif /* TPATCH_H */