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

956 lines
36 KiB
C

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <assert.h>
#include "cctk.h"
#include "config.h"
#include "stdc.h"
#include "util.h"
#include "array.h"
#include "cpm_map.h"
#include "linear_map.h"
#include "coords.h"
#include "tgrid.h"
#include "fd_grid.h"
#include "patch.h"
#include "patch_edge.h"
#include "patch_interp.h"
#include "ghost_zone.h"
namespace AHFinderDirect
{
using jtutil::error_exit;
//******************************************************************************
//******************************************************************************
//******************************************************************************
//
// This function constructs a patch object.
//
patch::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)
: fd_grid(grid_array_pars_in, grid_pars_in),
my_patch_system_(my_patch_system_in),
patch_number_(patch_number_in),
name_(name_in),
is_plus_(is_plus_in), ctype_(ctype_in),
coords_set_rho_(coords_set_rho_in),
coords_set_sigma_(coords_set_sigma_in),
coords_set_tau_(coords_set_tau_in),
min_rho_patch_edge_(*new patch_edge(*this, side_is_min, side_is_rho)),
max_rho_patch_edge_(*new patch_edge(*this, side_is_max, side_is_rho)),
min_sigma_patch_edge_(*new patch_edge(*this, side_is_min, side_is_sigma)),
max_sigma_patch_edge_(*new patch_edge(*this, side_is_max, side_is_sigma)),
min_rho_ghost_zone_(NULL),
max_rho_ghost_zone_(NULL),
min_sigma_ghost_zone_(NULL),
max_sigma_ghost_zone_(NULL) // no comma
{
}
//******************************************************************************
//
// This function destroys a patch object.
//
patch::~patch()
{
// no need to check for null pointers, since delete NULL is a silent no-op
delete max_sigma_ghost_zone_;
delete min_sigma_ghost_zone_;
delete max_rho_ghost_zone_;
delete min_rho_ghost_zone_;
delete &max_sigma_patch_edge_;
delete &min_sigma_patch_edge_;
delete &max_rho_patch_edge_;
delete &min_rho_patch_edge_;
}
//******************************************************************************
//
// This function constructs a z_patch object.
//
z_patch::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)
: patch(my_patch_system_in, patch_number_in,
name_in, is_plus_in, 'z',
local_coords::coords_set_mu, local_coords::coords_set_nu,
local_coords::coords_set_phi,
grid_array_pars_in, grid_pars_in)
{
}
//******************************************************************************
//
// This function constructs an x_patch object.
//
x_patch::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)
: patch(my_patch_system_in, patch_number_in,
name_in, is_plus_in, 'x',
local_coords::coords_set_nu, local_coords::coords_set_phi,
local_coords::coords_set_mu,
grid_array_pars_in, grid_pars_in)
{
}
//******************************************************************************
//
// This function constructs a y_patch object.
//
y_patch::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)
: patch(my_patch_system_in, patch_number_in,
name_in, is_plus_in, 'y',
local_coords::coords_set_mu, local_coords::coords_set_phi,
local_coords::coords_set_nu,
grid_array_pars_in, grid_pars_in)
{
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
//
// This function computes the (rho,sigma) induced 2-D metric from the
// 3-D (x,y,z) metric of the space containing the patch, as per p.33 of
// my apparent horizon finding notes.
//
// Arguments:
// (r,rho,sigma) = The coordinates where the Jacobian is wanted.
// partial_surface_r_wrt_(rho,sigma)
// = The partial derivatives of the surface radius with respect to
// the (rho,sigma) coordinates.
// g_{xx,xy,xz,yy,yz,zz} = The xyz 3-metric components $g_{ij}$.
// g_{rho_rho,rho_sigma,sigma_sigma} = The (rho,sigma) induced 2-D metric.
//
// Results:
// This function returns the Jacobian of the (rho,sigma) induced 2-D metric.
//
fp patch::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
{
fp partial_x_wrt_r, partial_x_wrt_rho, partial_x_wrt_sigma;
fp partial_y_wrt_r, partial_y_wrt_rho, partial_y_wrt_sigma;
fp partial_z_wrt_r, partial_z_wrt_rho, partial_z_wrt_sigma;
partial_xyz_wrt_r_rho_sigma(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);
const fp dx_wrt_rho = partial_x_wrt_rho + partial_x_wrt_r * partial_surface_r_wrt_rho;
const fp dx_wrt_sigma = partial_x_wrt_sigma + partial_x_wrt_r * partial_surface_r_wrt_sigma;
const fp dy_wrt_rho = partial_y_wrt_rho + partial_y_wrt_r * partial_surface_r_wrt_rho;
const fp dy_wrt_sigma = partial_y_wrt_sigma + partial_y_wrt_r * partial_surface_r_wrt_sigma;
const fp dz_wrt_rho = partial_z_wrt_rho + partial_z_wrt_r * partial_surface_r_wrt_rho;
const fp dz_wrt_sigma = partial_z_wrt_sigma + partial_z_wrt_r * partial_surface_r_wrt_sigma;
g_rho_rho = +dx_wrt_rho * dx_wrt_rho * g_xx + 2.0 * dx_wrt_rho * dy_wrt_rho * g_xy + 2.0 * dx_wrt_rho * dz_wrt_rho * g_xz + dy_wrt_rho * dy_wrt_rho * g_yy + 2.0 * dy_wrt_rho * dz_wrt_rho * g_yz + dz_wrt_rho * dz_wrt_rho * g_zz;
g_rho_sigma = +dx_wrt_rho * dx_wrt_sigma * g_xx + (dx_wrt_rho * dy_wrt_sigma + dy_wrt_rho * dx_wrt_sigma) * g_xy + (dx_wrt_rho * dz_wrt_sigma + dz_wrt_rho * dx_wrt_sigma) * g_xz + dy_wrt_rho * dy_wrt_sigma * g_yy + (dy_wrt_rho * dz_wrt_sigma + dz_wrt_rho * dy_wrt_sigma) * g_yz + dz_wrt_rho * dz_wrt_sigma * g_zz;
g_sigma_sigma = +dx_wrt_sigma * dx_wrt_sigma * g_xx + 2.0 * dx_wrt_sigma * dy_wrt_sigma * g_xy + 2.0 * dx_wrt_sigma * dz_wrt_sigma * g_xz + dy_wrt_sigma * dy_wrt_sigma * g_yy + 2.0 * dy_wrt_sigma * dz_wrt_sigma * g_yz + dz_wrt_sigma * dz_wrt_sigma * g_zz;
return g_rho_rho * g_sigma_sigma - jtutil::pow2(g_rho_sigma);
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
//
// This function decodes the character-string name of an integration method
// into an enum integration_method . See the comments in "patch.hh" on the
// declaration of enum integration_method for details on the methods and
// their character-string names.
//
// static
enum patch::integration_method
patch::decode_integration_method(const char method_string[])
{
if ((strcmp(method_string, "trapezoid") == 0) || (strcmp(method_string, "trapezoid rule") == 0))
return integration_method__trapezoid;
else if ((strcmp(method_string, "Simpson") == 0) || (strcmp(method_string, "Simpson's rule") == 0))
return integration_method__Simpson;
else if ((strcmp(method_string, "Simpson (variant)") == 0) || (strcmp(method_string, "Simpson's rule (variant)") == 0))
return integration_method__Simpson_variant;
else if (strcmp(method_string, "automatic choice") == 0)
return integration_method__automatic_choice;
else
error_exit(ERROR_EXIT,
"***** patch::decode_integration_method():\n"
" unknown method_string=\"%s\"!\n",
method_string); /*NOTREACHED*/
}
//******************************************************************************
//
// This function computes an approximation to the arc length of a surface
// over the patch's nominal bounds along the rho direction (i.e. in a
// dsigma=constant plane where dsigma is a multiple of 90 degrees)
//
// Arguments:
// ghosted_radius_gfn = (in) The surface radius.
// g_{xx,xy,xz,yy,yz,zz}_gfn = (in) The xyz 3-metric components.
// method = (in) Selects the integration scheme.
//
fp patch::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
{
fp dsigma;
if (is_valid_dsigma(0.0))
then dsigma = 0.0;
else if (is_valid_dsigma(90.0))
then dsigma = 90.0;
else if (is_valid_dsigma(180.0))
then dsigma = 180.0;
else if (is_valid_dsigma(-90.0))
then dsigma = -90.0;
else
error_exit(PANIC_EXIT,
"***** patch::rho_arc_length(): can't find valid dsigma\n"
" which is a multiple of 90 degrees!\n"
" %s patch: [min,max]_dsigma()=[%g,%g]\n",
name(), min_dsigma(), max_dsigma());
const fp sigma = sigma_of_dsigma(dsigma);
const int isigma = isigma_of_sigma(sigma);
fp sum = 0.0;
for (int irho = min_irho(); irho <= max_irho(); ++irho)
{
const fp rho = rho_of_irho(irho);
const fp r = ghosted_gridfn(ghosted_radius_gfn, irho, isigma);
const fp partial_surface_r_wrt_rho = partial_rho(ghosted_radius_gfn, irho, isigma);
const fp partial_surface_r_wrt_sigma = partial_sigma(ghosted_radius_gfn, irho, isigma);
const fp g_xx = gridfn(g_xx_gfn, irho, isigma);
const fp g_xy = gridfn(g_xy_gfn, irho, isigma);
const fp g_xz = gridfn(g_xz_gfn, irho, isigma);
const fp g_yy = gridfn(g_yy_gfn, irho, isigma);
const fp g_yz = gridfn(g_yz_gfn, irho, isigma);
const fp g_zz = gridfn(g_zz_gfn, irho, isigma);
fp g_rho_rho, g_rho_sigma, g_sigma_sigma;
rho_sigma_metric(r, rho, sigma,
partial_surface_r_wrt_rho,
partial_surface_r_wrt_sigma,
g_xx, g_xy, g_xz,
g_yy, g_yz,
g_zz,
g_rho_rho, g_rho_sigma,
g_sigma_sigma);
const fp coeff = integration_coeff(method,
max_irho() - min_irho(),
irho - min_irho());
sum += coeff * sqrt(g_rho_rho);
}
return delta_rho() * sum;
}
//******************************************************************************
//
// This function computes an approximation to the arc length of a surface
// over the patch's nominal bounds along the sigma direction (i.e. in a
// drho=constant plane where drho is a multiple of 90 degrees)
//
// Arguments:
// ghosted_radius_gfn = (in) The surface radius.
// g_{xx,xy,xz,yy,yz,zz}_gfn = (in) The xyz 3-metric components.
// method = (in) Selects the integration scheme.
//
fp patch::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
{
fp drho;
if (is_valid_drho(0.0))
then drho = 0.0;
else if (is_valid_drho(90.0))
then drho = 90.0;
else if (is_valid_drho(180.0))
then drho = 180.0;
else if (is_valid_drho(-90.0))
then drho = -90.0;
else
error_exit(PANIC_EXIT,
"***** patch::sigma_arc_length(): can't find valid drho\n"
" which is a multiple of 90 degrees!\n"
" %s patch: [min,max]_drho()=[%g,%g]\n",
name(), min_drho(), max_drho());
const fp rho = rho_of_drho(drho);
const int irho = irho_of_rho(rho);
fp sum = 0.0;
for (int isigma = min_isigma(); isigma <= max_isigma(); ++isigma)
{
const fp sigma = sigma_of_isigma(isigma);
const fp r = ghosted_gridfn(ghosted_radius_gfn, irho, isigma);
const fp partial_surface_r_wrt_rho = partial_rho(ghosted_radius_gfn, irho, isigma);
const fp partial_surface_r_wrt_sigma = partial_sigma(ghosted_radius_gfn, irho, isigma);
const fp g_xx = gridfn(g_xx_gfn, irho, isigma);
const fp g_xy = gridfn(g_xy_gfn, irho, isigma);
const fp g_xz = gridfn(g_xz_gfn, irho, isigma);
const fp g_yy = gridfn(g_yy_gfn, irho, isigma);
const fp g_yz = gridfn(g_yz_gfn, irho, isigma);
const fp g_zz = gridfn(g_zz_gfn, irho, isigma);
fp g_rho_rho, g_rho_sigma, g_sigma_sigma;
rho_sigma_metric(r, rho, sigma,
partial_surface_r_wrt_rho,
partial_surface_r_wrt_sigma,
g_xx, g_xy, g_xz,
g_yy, g_yz,
g_zz,
g_rho_rho, g_rho_sigma,
g_sigma_sigma);
const fp coeff = integration_coeff(method,
max_isigma() - min_isigma(),
isigma - min_isigma());
sum += coeff * sqrt(g_sigma_sigma);
}
return delta_sigma() * sum;
}
//******************************************************************************
//
// This function computes the arc length of a surface in the specified
// plane ("xz" or "yz") over the patch's nominal bounds.
//
// Arguments:
// plane[] = (in) "xz" or "yz" to specify the plane.
// ghosted_radius_gfn = (in) The surface radius.
// g_{xx,xy,xz,yy,yz,zz}_gfn = (in) The xyz 3-metric components.
// method = (in) Selects the integration scheme.
//
fp z_patch::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
{
if ((plane[0] == 'x') && (plane[1] == 'z'))
then // xz-plane = rotation about y = nu arc = sigma sigma
return sigma_arc_length(ghosted_radius_gfn,
g_xx_gfn, g_xy_gfn, g_xz_gfn,
g_yy_gfn, g_yz_gfn,
g_zz_gfn,
method);
else if ((plane[0] == 'y') && (plane[1] == 'z'))
then // yz-plane = rotation about x = mu arc = rho arc
return rho_arc_length(ghosted_radius_gfn,
g_xx_gfn, g_xy_gfn, g_xz_gfn,
g_yy_gfn, g_yz_gfn,
g_zz_gfn,
method);
else
error_exit(ERROR_EXIT,
"***** z_patch::plane_arc_length(): %s patch, plane=\"%s\", but\n"
" this patch doesn't contain that plane!\n",
name(), plane); /*NOTREACHED*/
}
//******************************************************************************
//
// This function computes the arc length of a surface in the specified
// plane ("xy" or "xz") over the patch's nominal bounds.
//
// Arguments:
// plane[] = (in) "xy" or "xz" to specify the plane.
// ghosted_radius_gfn = (in) The surface radius.
// g_{xx,xy,xz,yy,yz,zz}_gfn = (in) The xyz 3-metric components.
// method = (in) Selects the integration scheme.
//
fp x_patch::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
{
if ((plane[0] == 'x') && (plane[1] == 'y'))
then // xy-plane = rotation about z = phi arc = sigma arc
return sigma_arc_length(ghosted_radius_gfn,
g_xx_gfn, g_xy_gfn, g_xz_gfn,
g_yy_gfn, g_yz_gfn,
g_zz_gfn,
method);
else if ((plane[0] == 'x') && (plane[1] == 'z'))
then // xz-plane = rotation about y = nu arc = rho arc
return rho_arc_length(ghosted_radius_gfn,
g_xx_gfn, g_xy_gfn, g_xz_gfn,
g_yy_gfn, g_yz_gfn,
g_zz_gfn,
method);
else
error_exit(ERROR_EXIT,
"***** x_patch::plane_arc_length(): %s patch, plane=\"%s\", but\n"
" this patch doesn't contain that plane!\n",
name(), plane); /*NOTREACHED*/
}
//******************************************************************************
//
// This function computes the arc length of a surface in the specified
// plane ("xy" or "yz") over the patch's nominal bounds.
//
// Arguments:
// plane[] = (in) "xy" or "yz" to specify the plane.
// ghosted_radius_gfn = (in) The surface radius.
// g_{xx,xy,xz,yy,yz,zz}_gfn = (in) The xyz 3-metric components.
// method = (in) Selects the integration scheme.
//
fp y_patch::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
{
if ((plane[0] == 'x') && (plane[1] == 'y'))
then // xy-plane = rotation about z = phi arc = sigma arc
return sigma_arc_length(ghosted_radius_gfn,
g_xx_gfn, g_xy_gfn, g_xz_gfn,
g_yy_gfn, g_yz_gfn,
g_zz_gfn,
method);
else if ((plane[0] == 'y') && (plane[1] == 'z'))
then // yz-plane = rotation about x = mu arc = rho arc
return rho_arc_length(ghosted_radius_gfn,
g_xx_gfn, g_xy_gfn, g_xz_gfn,
g_yy_gfn, g_yz_gfn,
g_zz_gfn,
method);
else
error_exit(ERROR_EXIT,
"***** y_patch::plane_arc_length(): %s patch, plane=\"%s\", but\n"
" this patch doesn't contain that plane!\n",
name(), plane); /*NOTREACHED*/
}
//******************************************************************************
//
// This function computes an approximation to 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).
//
// Arguments:
// unknown_src_gfn = (in) The gridfn to be integrated. This 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.
// ghosted_radius_gfn = (in) The surface radius.
// g_{xx,xy,xz,yy,yz,zz}_gfn = (in) The xyz 3-metric components.
// method = (in) Selects the integration scheme.
//
fp patch::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
{
const bool src_is_ghosted = is_valid_ghosted_gfn(unknown_src_gfn);
fp sum = 0.0;
for (int irho = min_irho(); irho <= max_irho(); ++irho)
{
for (int isigma = min_isigma(); isigma <= max_isigma(); ++isigma)
{
const fp fn = unknown_gridfn(src_is_ghosted,
unknown_src_gfn, irho, isigma);
const fp rho = rho_of_irho(irho);
const fp sigma = sigma_of_isigma(isigma);
const fp r = ghosted_gridfn(ghosted_radius_gfn, irho, isigma);
const fp partial_surface_r_wrt_rho = partial_rho(ghosted_radius_gfn, irho, isigma);
const fp partial_surface_r_wrt_sigma = partial_sigma(ghosted_radius_gfn, irho, isigma);
const fp g_xx = gridfn(g_xx_gfn, irho, isigma);
const fp g_xy = gridfn(g_xy_gfn, irho, isigma);
const fp g_xz = gridfn(g_xz_gfn, irho, isigma);
const fp g_yy = gridfn(g_yy_gfn, irho, isigma);
const fp g_yz = gridfn(g_yz_gfn, irho, isigma);
const fp g_zz = gridfn(g_zz_gfn, irho, isigma);
fp g_rho_rho, g_rho_sigma, g_sigma_sigma;
const fp Jac = rho_sigma_metric(r, rho, sigma,
partial_surface_r_wrt_rho,
partial_surface_r_wrt_sigma,
g_xx, g_xy, g_xz,
g_yy, g_yz,
g_zz,
g_rho_rho, g_rho_sigma,
g_sigma_sigma);
const fp coeff_rho = integration_coeff(method,
max_irho() - min_irho(),
irho - min_irho());
const fp coeff_sigma = integration_coeff(method,
max_isigma() - min_isigma(),
isigma - min_isigma());
sum += coeff_rho * coeff_sigma * fn * sqrt(jtutil::abs(Jac));
}
}
return delta_rho() * delta_sigma() * sum;
}
//******************************************************************************
//
// This function computes the integration coefficients for
// integrate_gridfn() . That is, if we write
// $\int_{x_0}^{x_N} f(x) \, dx
// \approx \Delta x \, \sum_{i=0}^N c_i f(x_i)$
// then this function computes $c_i$.
//
// For method == integration_method__automatic_choice the choices are
// N=1 trapezoid
// N=2 Simpson
// N=3 trapezoid
// N=4 Simpson
// N=5 trapezoid
// N=6 Simpson
// N=7 and up Simpson variant
//
// Arguments:
// method = Specifies the integration method.
// N = The number of integration *intervals*. (The number of integration
// *points* is N+1.)
// i = Specifies the point at which the coefficient is desired.
//
// static
fp patch::integration_coeff(enum integration_method method, int N, int i)
{
assert(i >= 0);
assert(i <= N);
if (method == integration_method__automatic_choice)
then
{
if (N >= 7)
then method = integration_method__Simpson_variant;
else if ((N % 2) == 0)
then method = integration_method__Simpson;
else
method = integration_method__trapezoid;
}
switch (method)
{
case integration_method__trapezoid:
if ((i == 0) || (i == N))
then return 0.5;
else
return 1.0;
case integration_method__Simpson:
if ((N % 2) != 0)
then error_exit(ERROR_EXIT,
"***** patch::integration_coeff():\n"
" Simpson's rule requires N to be even, but N=%d!\n",
N); /*NOTREACHED*/
if ((i == 0) || (i == N))
then return 1.0 / 3.0;
else if ((i % 2) == 0)
then return 2.0 / 3.0;
else
return 4.0 / 3.0;
case integration_method__Simpson_variant:
if (N < 7)
then error_exit(ERROR_EXIT,
"***** patch::integration_coeff():\n"
" Simpson's rule (variant) requires N >= 7, but N=%d!\n",
N); /*NOTREACHED*/
if ((i == 0) || (i == N))
then return 17.0 / 48.0;
else if ((i == 1) || (i == N - 1))
then return 59.0 / 48.0;
else if ((i == 2) || (i == N - 2))
then return 43.0 / 48.0;
else if ((i == 3) || (i == N - 3))
then return 49.0 / 48.0;
else
return 1.0;
default:
error_exit(ERROR_EXIT,
"***** patch::integration_coeff(): unknown method=(int)%d!\n"
" (this should never happen!)\n",
int(method)); /*NOTREACHED*/
}
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
//
// This function returns a reference to the ghost zone on a specified
// edge, after first assert()ing that the edge belongs to this patch.
//
// N.b. This function can't be inline in "patch.hh" because it needs
// member functions of class patch_edge, which comes after class patch
// in our #include order.
//
ghost_zone &patch::ghost_zone_on_edge(const patch_edge &e)
const
{
assert(e.my_patch() == *this);
return minmax_ang_ghost_zone(e.is_min(), e.is_rho());
}
//******************************************************************************
//
// This function determines which of the two adjacent ghost zones meeting
// at a specified corner, contains a specified point. If the point isn't
// in either ghost zone, an error_exit() is done. If the point is in both
// ghost zones, it's arbitrary which one will be chosen.
//
// Arguments:
// {rho,sigma}_is_min = Specify the corner (and implicitly the ghost zones).
// irho,isigma = Specify the point.
//
// Results:
// This function returns (a reference to) the desired ghost zone.
ghost_zone &patch::corner_ghost_zone_containing_point(bool rho_is_min, bool sigma_is_min,
int irho, int isigma)
const
{
ghost_zone &rho_gz = minmax_rho_ghost_zone(rho_is_min);
ghost_zone &sigma_gz = minmax_sigma_ghost_zone(sigma_is_min);
const patch_edge &rho_edge = rho_gz.my_edge();
const patch_edge &sigma_edge = sigma_gz.my_edge();
const int rho_iperp = rho_edge.iperp_of_irho_isigma(irho, isigma);
const int rho_ipar = rho_edge.ipar_of_irho_isigma(irho, isigma);
const int sigma_iperp = sigma_edge.iperp_of_irho_isigma(irho, isigma);
const int sigma_ipar = sigma_edge.ipar_of_irho_isigma(irho, isigma);
const bool is_in_rho_ghost_zone = rho_gz.is_in_ghost_zone(rho_iperp, rho_ipar);
const bool is_in_sigma_ghost_zone = sigma_gz.is_in_ghost_zone(sigma_iperp, sigma_ipar);
// check that point is in at least one ghost zone
if (!is_in_rho_ghost_zone && !is_in_sigma_ghost_zone)
then error_exit(ERROR_EXIT,
"***** patch::corner_ghost_zone_containing_point():\n"
" neither ghost zone contains point (this should never happen)!\n"
" patch=%s rho_is_min=(int)%d sigma_is_min=(int)%d\n"
" irho=%d isigma=%d\n",
name(), int(rho_is_min), int(sigma_is_min),
irho, isigma); /*NOTREACHED*/
return is_in_rho_ghost_zone ? rho_gz : sigma_gz;
}
//******************************************************************************
//
// This function determines which ghost zone contains a specified
// noncorner point.
//
// If the point isn't in any ghost zone of this patch, or if the point
// is in the corner of a ghost zone, an error_exit() is done.
//
// Arguments:
// irho,isigma = Specify the point.
//
// Results:
// This function returns (a reference to) the desired ghost zone.
ghost_zone &patch::ghost_zone_containing_noncorner_point(int irho, int isigma)
const
{
// n.b. these loops must use _int_ variables for the loop
// to terminate!
for (int want_min = false; want_min <= true; ++want_min)
{
for (int want_rho = false; want_rho <= true; ++want_rho)
{
const patch_edge &e = minmax_ang_patch_edge(want_min, want_rho);
const int iperp = e.iperp_of_irho_isigma(irho, isigma);
const int ipar = e.ipar_of_irho_isigma(irho, isigma);
ghost_zone &gz = minmax_ang_ghost_zone(want_min, want_rho);
if (gz.is_in_ghost_zone(iperp, ipar) && gz.my_edge().ipar_is_in_noncorner(ipar))
then return gz;
}
}
error_exit(ERROR_EXIT,
"***** patch::ghost_zone_containing_noncorner_point():\n"
" no ghost zone contains point (this should never happen)!\n"
" patch=%s irho=%d isigma=%d\n",
name(), irho, isigma); /*NOTREACHED*/
}
//******************************************************************************
//
// This function assert()s that a specified ghost zone of this patch
// hasn't already been set up, then constructs it as a mirror-symmetry
// ghost zone and properly links this to/from the patch.
//
void patch::create_mirror_symmetry_ghost_zone(const patch_edge &my_edge)
{
// make sure we belong to the right patch
assert(my_edge.my_patch() == *this);
symmetry_ghost_zone *temp = new symmetry_ghost_zone(my_edge);
set_ghost_zone(my_edge, temp);
}
//******************************************************************************
//
// This function assert()s that a specified ghost zone of this patch
// hasn't already been set up, then creates it as a periodic-symmetry
// ghost zone and properly links this to/from the patch.
//
void patch::create_periodic_symmetry_ghost_zone(const patch_edge &my_edge, const patch_edge &other_edge,
bool is_ipar_map_plus)
{
// make sure we belong to the right patch
assert(my_edge.my_patch() == *this);
int my_sample_ipar = my_edge.min_ipar_without_corners();
int other_sample_ipar = is_ipar_map_plus
? other_edge.min_ipar_without_corners()
: other_edge.max_ipar_without_corners();
symmetry_ghost_zone *temp = new symmetry_ghost_zone(my_edge, other_edge,
my_sample_ipar, other_sample_ipar,
is_ipar_map_plus);
set_ghost_zone(my_edge, temp);
}
//******************************************************************************
//
// This function assert()s that a specified ghost zone of this patch
// hasn't already been set up, then creates it as an interpatch ghost
// zone (with lots of NULL pointers for info we can't compute yet)
// and properly links this to/from the patch.
//
void patch::create_interpatch_ghost_zone(const patch_edge &my_edge, const patch_edge &other_edge,
int patch_overlap_width)
{
// make sure we belong to the right patch
assert(my_edge.my_patch() == *this);
interpatch_ghost_zone *temp = new interpatch_ghost_zone(my_edge, other_edge,
patch_overlap_width);
set_ghost_zone(my_edge, temp);
}
//******************************************************************************
//
// This is a helper function for setup_*_ghost_zone(). This function
// assert()s that one of the ghost zone pointers (which one is selected
// by edge ) is NULL, then stores a value in it.
//
void patch::set_ghost_zone(const patch_edge &edge, ghost_zone *gzp)
{
ghost_zone *&ghost_zone_ptr_to_set = edge.is_min()
? (edge.is_rho() ? min_rho_ghost_zone_ : min_sigma_ghost_zone_)
: (edge.is_rho() ? max_rho_ghost_zone_ : max_sigma_ghost_zone_);
assert(ghost_zone_ptr_to_set == NULL);
ghost_zone_ptr_to_set = gzp;
}
//******************************************************************************
//
// This function finds which patch edge is adjacent to a neighboring
// patch q, or does an error_exit() if q isn't actually a neighboring patch.
// The computation is done using only (rho,sigma) coordinate sets and
// min/max dang bounds ==> it's ok to use this function in setting up
// interpatch ghost zones.
//
// Arguments:
// q = The (supposedly) neighboring patch.
// patch_overlap_width = The number of grid points these patches overlap.
// If this is nonzero, then these patches must have the
// same grid spacing in the perpendicular direction.
//
const patch_edge &patch::edge_adjacent_to_patch(const patch &q,
int patch_overlap_width /* = 0 */)
const
{
const patch &p = *this;
// which (rho,sigma) coordinate do the patches have in common?
// ... this is the perp coordinate for the border
const local_coords::coords_set common_coord_set = p.coords_set_rho_sigma() & q.coords_set_rho_sigma();
// is this coordinate rho or sigma in each patch?
const bool common_is_p_rho = (common_coord_set == p.coords_set_rho());
const bool common_is_p_sigma = (common_coord_set == p.coords_set_sigma());
if ((common_is_p_rho ^ common_is_p_sigma) != 0x1)
then error_exit(ERROR_EXIT,
"***** patch::edge_adjacent_to_patch():\n"
" common coordinate isn't exactly one of p.{rho,sigma}!\n"
" p.name()=\"%s\" q.name()=\"%s\"\n"
" common_coord_set=%s\n"
" common_is_p_rho=%d common_is_p_sigma=%d\n",
p.name(), q.name(),
local_coords::name_of_coords_set(common_coord_set),
int(common_is_p_rho), int(common_is_p_sigma));
/*NOTREACHED*/
const bool common_is_q_rho = (common_coord_set == q.coords_set_rho());
const bool common_is_q_sigma = (common_coord_set == q.coords_set_sigma());
if ((common_is_q_rho ^ common_is_q_sigma) != 0x1)
then error_exit(ERROR_EXIT,
"***** patch::edge_adjacent_to_patch():\n"
" common coordinate isn't exactly one of q.{rho,sigma}!\n"
" p.name()=\"%s\" q.name()=\"%s\"\n"
" common_coord_set=%s\n"
" common_is_q_rho=%d common_is_q_sigma=%d\n",
p.name(), q.name(),
local_coords::name_of_coords_set(common_coord_set),
int(common_is_q_rho), int(common_is_q_sigma));
/*NOTREACHED*/
// how much do the patches overlap?
// ... eg patch_overlap_width = 3 would be
// p p p p p
// q q q q q
// so the overlap would be (patch_overlap_width-1) * delta = 2 * delta
if ((patch_overlap_width - 1 != 0) && jtutil::fuzzy<fp>::NE(p.delta_dang(common_is_p_rho),
q.delta_dang(common_is_q_rho)))
then error_exit(ERROR_EXIT,
"***** patch::edge_adjacent_to_patch():\n"
" patch_overlap_width != 0 must have same perp grid spacing in both patches!\n"
" p.name()=\"%s\" q.name()=\"%s\"\n"
" common_coord_set=%s\n"
" common_is_p_rho=%d common_is_q_rho=%d\n"
" p.delta_dang(common_is_p_rho)=%g\n"
" q.delta_dang(common_is_q_rho)=%g\n",
p.name(), q.name(),
local_coords::name_of_coords_set(common_coord_set),
int(common_is_p_rho), int(common_is_q_rho),
double(p.delta_dang(common_is_p_rho)),
double(q.delta_dang(common_is_q_rho))); /*NOTREACHED*/
const fp doverlap = fp(patch_overlap_width - 1) * p.delta_dang(common_is_p_rho);
// where is the common boundary relative to the min/max sides of each patch?
const bool common_is_p_min_q_max = local_coords::fuzzy_EQ_dang(p.min_dang(common_is_p_rho),
q.max_dang(common_is_q_rho) - doverlap);
const bool common_is_p_max_q_min = local_coords::fuzzy_EQ_dang(p.max_dang(common_is_p_rho),
q.min_dang(common_is_q_rho) + doverlap);
if ((common_is_p_min_q_max ^ common_is_p_max_q_min) != 0x1)
then error_exit(ERROR_EXIT,
"***** patch::edge_adjacent_to_patch():\n"
" common coordinate isn't exactly one of {pmax/qmin, pmin/qmax}!\n"
" p.name()=\"%s\" q.name()=\"%s\"\n"
" common_coord_set=%s\n"
" common_is_p_rho=%d common_is_q_rho=%d\n"
" p.delta_dang(common_is_p_rho)=%g\n"
" q.delta_dang(common_is_q_rho)=%g\n"
" patch_overlap_width=%d doverlap=%g\n"
" common_is_p_min_q_max=%d common_is_p_max_q_min=%d\n",
p.name(), q.name(),
local_coords::name_of_coords_set(common_coord_set),
int(common_is_p_rho), int(common_is_q_rho),
double(p.delta_dang(common_is_p_rho)),
double(q.delta_dang(common_is_q_rho)),
patch_overlap_width, double(doverlap),
int(common_is_p_min_q_max), int(common_is_p_max_q_min));
/*NOTREACHED*/
return p.minmax_ang_patch_edge(common_is_p_min_q_max, common_is_p_rho);
}
//******************************************************************************
//
// This function verifies (via assert()) that all ghost zones of this
// patch have been fully set up.
//
void patch::assert_all_ghost_zones_fully_setup() const
{
assert(min_rho_ghost_zone_ != NULL);
assert(max_rho_ghost_zone_ != NULL);
assert(min_sigma_ghost_zone_ != NULL);
assert(max_sigma_ghost_zone_ != NULL);
// these calls are no-ops for non-interpatch ghost zones
min_rho_ghost_zone().assert_fully_setup();
max_rho_ghost_zone().assert_fully_setup();
min_sigma_ghost_zone().assert_fully_setup();
max_sigma_ghost_zone().assert_fully_setup();
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
} // namespace AHFinderDirect