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

2523 lines
94 KiB
C

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <assert.h>
#include <limits.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"
#include "patch_info.h"
#include "patch_system.h"
#include "patch_system_info.h"
namespace AHFinderDirect
{
using jtutil::error_exit;
//******************************************************************************
//******************************************************************************
//******************************************************************************
//
// This function constructs a patch_system object.
//
// Constructor arguments:
// ghost_zone_width = Width in grid points of all ghost zones.
// patch_overlap_width = Number of grid points that adjacent
// nominally-just-touching patches should overlap.
// For example, with patch_overlap_width == 3, here
// are the grid points of two neighboring patches:
// x x x x x X
// |
// O o o o o o
// Here | marks the "just touching" boundary,
// x and o the grid points before this extension,
// and X and O the extra grid points added by this
// extension. For this example, the patch_extend_width
// parameter used by some other functions would
// be 1; in general
// patch_overlap_width = 2*patch_extend_width + 1
// N_zones_per_right_angle = This sets the grid spacing (same in both
// directions) to 90.0 / N_zones_per_right_angle.
// It's a fatal error (error_exit()) if this
// doesn't evenly divide the grid sizes in both
// directions.
// ip_interp_handle = Cactus handle to the interpatch interpolation operator;
// this must be a 1-D interpolator
// ip_interp_par_table_handle = Cactus handle to the parameter table for the
// interpatch interpolation operator
// surface_interp_handle = Cactus handle to the surface interpolation
// operator; this is optional, and is only used by
// radius_in_{local,global}_xyz_direction()
// If this is used, it must be a 2-D interpolator
// surface_interp_par_table_handle = Cactus handle to the parameter table
// for the surface interpolation operator;
// this is optional, and is only used by
// radius_in_local_xyz_direction()
// print_summary_msg_flag = true to print 2 lines of CCTK_VInfo messages
// giving the patch system type and origin
// false to skip this
// print_detailed_msg_flag = true to print extensive messages tracing the
// creation and initialization of various
// data structures
// false to skip this
//
patch_system::patch_system(fp origin_x_in, fp origin_y_in, fp origin_z_in,
enum patch_system_type type_in,
int ghost_zone_width, int patch_overlap_width,
int N_zones_per_right_angle,
int min_gfn_in, int max_gfn_in,
int ghosted_min_gfn_in, int ghosted_max_gfn_in,
int ip_interp_handle, int ip_interp_par_table_handle,
int surface_interp_handle_in,
int surface_interp_par_table_handle_in,
bool print_summary_msg_flag,
bool print_detailed_msg_flag)
: global_coords_(origin_x_in, origin_y_in, origin_z_in),
type_(type_in),
N_patches_(N_patches_of_type(type_in)),
all_patches_(new patch *[N_patches_]),
starting_gpn_(new int[N_patches_ + 1]),
ghosted_starting_gpn_(new int[N_patches_ + 1]),
gridfn_storage_(NULL), // set in setup_gridfn_storage()
ghosted_gridfn_storage_(NULL), // set in setup_gridfn_storage()
global_min_ym_(0), global_max_ym_(0),
// set in compute_synchronize_Jacobian()
surface_interp_handle_(surface_interp_handle_in),
surface_interp_par_table_handle_(surface_interp_par_table_handle_in)
{
if (!jtutil::is_odd(patch_overlap_width))
then error_exit(ERROR_EXIT,
"***** patch_system::patch_system(): implementation restriction:\n"
" patch_overlap_width=%d, but we only support odd values!\n",
patch_overlap_width); /*NOTREACHED*/
const int patch_extend_width = patch_overlap_width >> 1;
if (ghost_zone_width < fd_grid::molecule_radius())
{
cout << "***** patch_system::patch_system():" << endl
<< " must have ghost_zone_width >= fd_grid::molecule_radius()" << endl
<< " but got ghost_zone_width=" << ghost_zone_width << " fd_grid::molecule_radius()=" << fd_grid::molecule_radius() << "!" << endl
<< " finite difference order=4" << endl;
abort();
}
if (print_summary_msg_flag)
then
{
CCTK_VInfo(CCTK_THORNSTRING,
" constructing %s patch system",
name_of_type(type()));
CCTK_VInfo(CCTK_THORNSTRING,
" with %d angular zones per right angle",
N_zones_per_right_angle);
}
// construct/interlink the patches and ghost zones
switch (type_in)
{
case patch_system__full_sphere:
create_patches(patch_system_info::full_sphere::patch_info_array,
ghost_zone_width, patch_extend_width,
N_zones_per_right_angle,
print_detailed_msg_flag);
setup_gridfn_storage(min_gfn_in, max_gfn_in,
ghosted_min_gfn_in, ghosted_max_gfn_in,
print_detailed_msg_flag);
setup_ghost_zones__full_sphere(patch_overlap_width,
ip_interp_handle,
ip_interp_par_table_handle,
print_detailed_msg_flag);
break;
case patch_system__plus_z_hemisphere:
create_patches(patch_system_info::plus_z_hemisphere::patch_info_array,
ghost_zone_width, patch_extend_width,
N_zones_per_right_angle,
print_detailed_msg_flag);
setup_gridfn_storage(min_gfn_in, max_gfn_in,
ghosted_min_gfn_in, ghosted_max_gfn_in,
print_detailed_msg_flag);
setup_ghost_zones__plus_z_hemisphere(patch_overlap_width,
ip_interp_handle,
ip_interp_par_table_handle,
print_detailed_msg_flag);
break;
case patch_system__plus_xy_quadrant_mirrored:
create_patches(patch_system_info::plus_xy_quadrant::patch_info_array,
ghost_zone_width, patch_extend_width,
N_zones_per_right_angle,
print_detailed_msg_flag);
setup_gridfn_storage(min_gfn_in, max_gfn_in,
ghosted_min_gfn_in, ghosted_max_gfn_in,
print_detailed_msg_flag);
setup_ghost_zones__plus_xy_quadrant_mirrored(patch_overlap_width,
ip_interp_handle,
ip_interp_par_table_handle,
print_detailed_msg_flag);
break;
case patch_system__plus_xy_quadrant_rotating:
create_patches(patch_system_info::plus_xy_quadrant::patch_info_array,
ghost_zone_width, patch_extend_width,
N_zones_per_right_angle,
print_detailed_msg_flag);
setup_gridfn_storage(min_gfn_in, max_gfn_in,
ghosted_min_gfn_in, ghosted_max_gfn_in,
print_detailed_msg_flag);
setup_ghost_zones__plus_xy_quadrant_rotating(patch_overlap_width,
ip_interp_handle,
ip_interp_par_table_handle,
print_detailed_msg_flag);
break;
case patch_system__plus_xz_quadrant_mirrored:
create_patches(patch_system_info::plus_xz_quadrant::patch_info_array,
ghost_zone_width, patch_extend_width,
N_zones_per_right_angle,
print_detailed_msg_flag);
setup_gridfn_storage(min_gfn_in, max_gfn_in,
ghosted_min_gfn_in, ghosted_max_gfn_in,
print_detailed_msg_flag);
setup_ghost_zones__plus_xz_quadrant_mirrored(patch_overlap_width,
ip_interp_handle,
ip_interp_par_table_handle,
print_detailed_msg_flag);
break;
case patch_system__plus_xz_quadrant_rotating:
create_patches(patch_system_info::plus_xz_quadrant::patch_info_array,
ghost_zone_width, patch_extend_width,
N_zones_per_right_angle,
print_detailed_msg_flag);
setup_gridfn_storage(min_gfn_in, max_gfn_in,
ghosted_min_gfn_in, ghosted_max_gfn_in,
print_detailed_msg_flag);
setup_ghost_zones__plus_xz_quadrant_rotating(patch_overlap_width,
ip_interp_handle,
ip_interp_par_table_handle,
print_detailed_msg_flag);
break;
case patch_system__plus_xyz_octant_mirrored:
create_patches(patch_system_info::plus_xyz_octant::patch_info_array,
ghost_zone_width, patch_extend_width,
N_zones_per_right_angle,
print_detailed_msg_flag);
setup_gridfn_storage(min_gfn_in, max_gfn_in,
ghosted_min_gfn_in, ghosted_max_gfn_in,
print_detailed_msg_flag);
setup_ghost_zones__plus_xyz_octant_mirrored(patch_overlap_width,
ip_interp_handle,
ip_interp_par_table_handle,
print_detailed_msg_flag);
break;
case patch_system__plus_xyz_octant_rotating:
create_patches(patch_system_info::plus_xyz_octant::patch_info_array,
ghost_zone_width, patch_extend_width,
N_zones_per_right_angle,
print_detailed_msg_flag);
setup_gridfn_storage(min_gfn_in, max_gfn_in,
ghosted_min_gfn_in, ghosted_max_gfn_in,
print_detailed_msg_flag);
setup_ghost_zones__plus_xyz_octant_rotating(patch_overlap_width,
ip_interp_handle,
ip_interp_par_table_handle,
print_detailed_msg_flag);
break;
default:
error_exit(ERROR_EXIT,
"***** patch_system::patch_system(): bad type_in=(int)%d!\n",
int(type_in)); /*NOTREACHED*/
}
if (print_summary_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" ==> %d nominal, %d ghosted angular grid points",
N_grid_points(), ghosted_N_grid_points());
}
//******************************************************************************
//
// This function destroys a patch_system object.
//
patch_system::~patch_system()
{
for (int pn = N_patches() - 1; pn >= 0; --pn)
{
if (&ith_patch(pn))
delete &ith_patch(pn);
}
if (ghosted_gridfn_storage_)
delete[] ghosted_gridfn_storage_;
if (gridfn_storage_)
delete[] gridfn_storage_;
if (ghosted_starting_gpn_)
delete[] ghosted_starting_gpn_;
if (starting_gpn_)
delete[] starting_gpn_;
if (all_patches_)
delete[] all_patches_;
}
//******************************************************************************
//
// This function is called from the patch_system:: constructor to
// construct a set of patches as described by an array of patch_info
// structures and associated arguments, and make these patches members
// of this patch system. This function also correctly sets
// N_grid_points_
// N_ghosted_grid_points_
// all_patches_[]
// starting_gpn_[]
// ghosted_starting_gpn_[]
// This function does *NOT* create any of the ghost zones, and does
// *NOT* set up any gridfns.
//
void patch_system::create_patches(const struct patch_info patch_info_in[],
int ghost_zone_width, int patch_extend_width,
int N_zones_per_right_angle,
bool print_msg_flag)
{
N_grid_points_ = 0;
ghosted_N_grid_points_ = 0;
for (int pn = 0; pn < N_patches(); ++pn)
{
const struct patch_info &pi = patch_info_in[pn];
const struct grid::grid_array_pars &grid_array_pars = pi.grid_array_pars(ghost_zone_width,
patch_extend_width,
N_zones_per_right_angle);
const struct grid::grid_pars &grid_pars = pi.grid_pars(patch_extend_width,
N_zones_per_right_angle);
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" constructing %s patch (%d x %d grid points)",
pi.name,
jtutil::how_many_in_range(grid_array_pars.min_irho,
grid_array_pars.max_irho),
jtutil::how_many_in_range(grid_array_pars.min_isigma,
grid_array_pars.max_isigma));
struct patch *p;
switch (pi.ctype)
{
case 'z':
p = new z_patch(*this, pn,
pi.name, pi.is_plus,
grid_array_pars, grid_pars);
break;
case 'x':
p = new x_patch(*this, pn,
pi.name, pi.is_plus,
grid_array_pars, grid_pars);
break;
case 'y':
p = new y_patch(*this, pn,
pi.name, pi.is_plus,
grid_array_pars, grid_pars);
break;
default:
error_exit(ERROR_EXIT,
"***** patch_system::create_patches():\n"
" unknown patch_info_in[pn=%d].ctype=0x%02d='%c'!\n",
pn, pi.ctype, pi.ctype); /*NOTREACHED*/
}
// these record number of grid points in *previous* patches,
// i.e. they do *not* include the number of grid points in this patch
starting_gpn_[pn] = N_grid_points_;
ghosted_starting_gpn_[pn] = ghosted_N_grid_points_;
N_grid_points_ += p->N_grid_points();
ghosted_N_grid_points_ += p->ghosted_N_grid_points();
all_patches_[pn] = p;
}
starting_gpn_[N_patches_] = N_grid_points_;
ghosted_starting_gpn_[N_patches_] = ghosted_N_grid_points_;
}
//******************************************************************************
//
// This function is called from the patch_system:: constructor to set
// up the storage for all gridfns in all patches, giving each gridfn a
// contiguous-across-all-patches storage array. This function also makes
// a number of self-consistency checks to ensure that the gridfn storage
// subscripting is set up properly.
//
// This function assumes that all the patches have already been constructed
// before it is called.
//
// For example, given the patches {x,y,z}, the ghosted gridfns {H,J},
// and the nominal gridfns {a,b,c}, we might picture the storage like
// this:
//
// xa xa xa ya ya za za za za
// xb xb xb yb yb zb zb zb zb
// xc xc xc yc yc zc zc zc zc
//
// xH xH xH xH yH yH yH zH zH zH zH zH
// xJ xJ xJ xJ yJ yJ yJ zJ zJ zJ zJ zJ
//
// Here the upper/lower blocks are for nominal/ghosted gridfns.
// The storage is taken as being contiguous within each row (in fact
// within each block). Thus the storage for all the nominal gridfns
// (or all the ghosted gridfns) in a single patch is *non*-contiguous.
//
// The creation of patches is done in several phases: first the patches
// are constructed with no gridfn storage, then we are called to set up
// the gridfn storage (taking into account the sizes of the other patches),
// then finally ghost zones are constructed and interlinked.
//
// FIXME: We should pad the gridfn storage as necessary to avoid cache
// conflicts, but we don't do this at present.
//
void patch_system::setup_gridfn_storage(int min_gfn_in, int max_gfn_in,
int ghosted_min_gfn_in, int ghosted_max_gfn_in,
bool print_msg_flag)
{
const int N_gridfns_in = jtutil::how_many_in_range(min_gfn_in, max_gfn_in);
const int ghosted_N_gridfns_in = jtutil::how_many_in_range(ghosted_min_gfn_in, ghosted_max_gfn_in);
const int gfn_stride = N_grid_points();
const int ghosted_gfn_stride = ghosted_N_grid_points();
const int N_storage = gfn_stride * N_gridfns_in;
const int ghosted_N_storage = ghosted_gfn_stride * ghosted_N_gridfns_in;
if (print_msg_flag)
then
{
CCTK_VInfo(CCTK_THORNSTRING,
" setting up gridfn storage");
CCTK_VInfo(CCTK_THORNSTRING,
" gfn=[%d,%d] ghosted_gfn=[%d,%d]",
min_gfn_in, max_gfn_in,
ghosted_min_gfn_in, ghosted_max_gfn_in);
CCTK_VInfo(CCTK_THORNSTRING,
" N_grid_points()=%d ghosted_N_grid_points()=%d",
N_grid_points(), ghosted_N_grid_points());
}
// storage arrays for all gridfns
gridfn_storage_ = new fp[N_storage];
ghosted_gridfn_storage_ = new fp[ghosted_N_storage];
// divide up the storage array among the patches
// and set up the storage in the individual patches themselves
{
for (int pn = 0; pn < N_patches(); ++pn)
{
const int posn = starting_gpn_[pn];
const int ghosted_posn = ghosted_starting_gpn_[pn];
const struct grid_arrays::gridfn_pars gridfn_pars = {
min_gfn_in, max_gfn_in,
&gridfn_storage_[posn],
gfn_stride, 0, 0};
const struct grid_arrays::gridfn_pars ghosted_gridfn_pars = {
ghosted_min_gfn_in, ghosted_max_gfn_in,
&ghosted_gridfn_storage_[ghosted_posn],
ghosted_gfn_stride, 0, 0};
patch &p = ith_patch(pn);
p.setup_gridfn_storage(gridfn_pars, ghosted_gridfn_pars);
}
}
if (print_msg_flag)
then
{
CCTK_VInfo(CCTK_THORNSTRING,
" checking that storage is partitioned properly");
}
// check to make sure storage for distinct gridfns
// forms a partition of the overall storage array
const patch &pfirst = ith_patch(0);
const patch &plast = ith_patch(N_patches() - 1);
{
for (int gfn = min_gfn(); gfn + 1 < max_gfn(); ++gfn)
{
// range of storage occupied by gridfns:
// gfn --> [gfn_first, gfn_last]
// gfn+1 --> [gfn1_first, gfn1_last]
const fp *const gfn_last_ptr = &plast.gridfn(gfn, plast.max_irho(),
plast.max_isigma());
const fp *const gfn1_first_ptr = &pfirst.gridfn(gfn + 1, pfirst.min_irho(),
pfirst.min_isigma());
if (!(gfn1_first_ptr == gfn_last_ptr + 1))
then error_exit(PANIC_EXIT,
"***** patch_system::setup_gridfn_storage():\n"
" nominal-grid gridfns don't partition overall storage array!"
" (this should never happen!)\n"
" gfn=%d last point at gfn_last_ptr=%p\n"
" gfn+1=%d first point at gfn1_first_ptr=%p\n"
" should have gfn1_first_ptr == gfn_last_ptr+1\n",
gfn, (const void *)gfn_last_ptr,
gfn + 1, (const void *)gfn1_first_ptr); /*NOTREACHED*/
}
}
{
for (int ghosted_gfn = ghosted_min_gfn();
ghosted_gfn + 1 < ghosted_max_gfn();
++ghosted_gfn)
{
// range of storage occupied by ghosted gridfns:
// ghosted_gfn --> [gfn_first, gfn_last]
// ghosted_gfn+1 --> [gfn1_first, gfn1_last]
const fp *const ghosted_gfn_last_ptr = &plast.ghosted_gridfn(ghosted_gfn,
plast.ghosted_max_irho(),
plast.ghosted_max_isigma());
const fp *const ghosted_gfn1_first_ptr = &pfirst.ghosted_gridfn(ghosted_gfn + 1,
pfirst.ghosted_min_irho(),
pfirst.ghosted_min_isigma());
if (!(ghosted_gfn1_first_ptr == ghosted_gfn_last_ptr + 1))
then error_exit(PANIC_EXIT,
"***** patch_system::setup_gridfn_storage():\n"
" ghosted-grid gridfns don't partition overall storage array!"
" (this should never happen!)\n"
" ghosted_gfn=%d last point at ghosted_gfn_last_ptr=%p\n"
" ghosted_gfn+1=%d first point at ghosted_gfn1_first_ptr=%p\n"
" should have ghosted_gfn1_first_ptr == ghosted_gfn_last_ptr+1\n",
ghosted_gfn, (const void *)ghosted_gfn_last_ptr,
ghosted_gfn + 1,
(const void *)ghosted_gfn1_first_ptr);
/*NOTREACHED*/
}
}
// check to make sure storage for distinct patches
// forms a partition of the storage for each gridfn
{
for (int gfn = min_gfn(); gfn < max_gfn(); ++gfn)
{
for (int pn = 0; pn + 1 < N_patches(); ++pn)
{
const patch &p = ith_patch(pn);
const patch &p1 = ith_patch(pn + 1);
// range of storage occupied by gridfn:
// p --> [p_first, p_last]
// p1 --> [p1_first, p1_last]
const fp *const p_last_ptr = &p.gridfn(gfn, p.max_irho(), p.max_isigma());
const fp *const p1_first_ptr = &p1.gridfn(gfn, p1.min_irho(), p1.min_isigma());
if (!(p1_first_ptr == p_last_ptr + 1))
then error_exit(PANIC_EXIT,
"***** patch_system::setup_gridfn_storage():\n"
" nominal-grid patches gridfns don't partition storage for gfn=%d!\n"
" (this should never happen!)\n"
" gfn=%d %s patch last point at p_last_ptr=%p\n"
" gfn=%d %s patch first point at p1_first_ptr=%p\n"
" should have p1_first_ptr == p_last_ptr+1\n",
gfn,
gfn, p.name(), (const void *)p_last_ptr,
gfn + 1, p1.name(), (const void *)p1_first_ptr);
/*NOTREACHED*/
}
}
}
{
for (int ghosted_gfn = ghosted_min_gfn();
ghosted_gfn < ghosted_max_gfn();
++ghosted_gfn)
{
for (int pn = 0; pn + 1 < N_patches(); ++pn)
{
const patch &p = ith_patch(pn);
const patch &p1 = ith_patch(pn + 1);
// range of storage occupied by ghosted gridfn:
// p --> [p_first, p_last]
// p1 --> [p1_first, p1_last]
const fp *const p_last_ptr = &p.ghosted_gridfn(ghosted_gfn,
p.ghosted_max_irho(),
p.ghosted_max_isigma());
const fp *const p1_first_ptr = &p1.ghosted_gridfn(ghosted_gfn,
p1.ghosted_min_irho(),
p1.ghosted_min_isigma());
if (!(p1_first_ptr == p_last_ptr + 1))
then error_exit(PANIC_EXIT,
"***** patch_system::setup_gridfn_storage():\n"
" nominal-grid patches gridfns don't partition storage for gfn=%d!\n"
" (this should never happen!)\n"
" %s patch (pn=%d) last point at p_last_ptr=%p\n"
" %s patch (pn=%d) first point at p1_first_ptr=%p\n"
" should have p1_first_ptr == p_last_ptr+1\n",
ghosted_gfn,
p.name(), pn, (const void *)p_last_ptr,
p1.name(), pn + 1, (const void *)p1_first_ptr);
/*NOTREACHED*/
}
}
}
}
//******************************************************************************
//
// This function sets up (constructs and interlinks) the ghost zones
// for a full-sphere patch system.
//
void patch_system::setup_ghost_zones__full_sphere(int patch_overlap_width,
int ip_interp_handle, int ip_interp_par_table_handle,
bool print_msg_flag)
{
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" seting up full sphere ghost zones");
patch &pz = ith_patch(patch_system_info::full_sphere::patch_number__pz);
patch &px = ith_patch(patch_system_info::full_sphere::patch_number__px);
patch &py = ith_patch(patch_system_info::full_sphere::patch_number__py);
patch &mx = ith_patch(patch_system_info::full_sphere::patch_number__mx);
patch &my = ith_patch(patch_system_info::full_sphere::patch_number__my);
patch &mz = ith_patch(patch_system_info::full_sphere::patch_number__mz);
// create the ghost zones
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" creating ghost zones");
create_interpatch_ghost_zones(pz, px, patch_overlap_width);
create_interpatch_ghost_zones(pz, py, patch_overlap_width);
create_interpatch_ghost_zones(pz, mx, patch_overlap_width);
create_interpatch_ghost_zones(pz, my, patch_overlap_width);
create_interpatch_ghost_zones(px, py, patch_overlap_width);
create_interpatch_ghost_zones(py, mx, patch_overlap_width);
create_interpatch_ghost_zones(mx, my, patch_overlap_width);
create_interpatch_ghost_zones(my, px, patch_overlap_width);
create_interpatch_ghost_zones(mz, px, patch_overlap_width);
create_interpatch_ghost_zones(mz, py, patch_overlap_width);
create_interpatch_ghost_zones(mz, mx, patch_overlap_width);
create_interpatch_ghost_zones(mz, my, patch_overlap_width);
// finish setting up the interpatch ghost zones
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" finishing interpatch setup");
finish_interpatch_setup(pz, px,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(pz, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(pz, mx,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(pz, my,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(px, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(py, mx,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(mx, my,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(my, px,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(mz, px,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(mz, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(mz, mx,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(mz, my,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
assert_all_ghost_zones_fully_setup();
}
//******************************************************************************
//
// This function sets up (constructs and interlinks) the ghost zones
// for a +z hemisphere patch system.
//
void patch_system::setup_ghost_zones__plus_z_hemisphere(int patch_overlap_width,
int ip_interp_handle, int ip_interp_par_table_handle,
bool print_msg_flag)
{
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" setting up +z hemisphere ghost zones");
patch &pz = ith_patch(patch_system_info::plus_z_hemisphere::patch_number__pz);
patch &px = ith_patch(patch_system_info::plus_z_hemisphere::patch_number__px);
patch &py = ith_patch(patch_system_info::plus_z_hemisphere::patch_number__py);
patch &mx = ith_patch(patch_system_info::plus_z_hemisphere::patch_number__mx);
patch &my = ith_patch(patch_system_info::plus_z_hemisphere::patch_number__my);
// create the ghost zones
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" creating ghost zones");
create_interpatch_ghost_zones(pz, px, patch_overlap_width);
create_interpatch_ghost_zones(pz, py, patch_overlap_width);
create_interpatch_ghost_zones(pz, mx, patch_overlap_width);
create_interpatch_ghost_zones(pz, my, patch_overlap_width);
create_interpatch_ghost_zones(px, py, patch_overlap_width);
create_interpatch_ghost_zones(py, mx, patch_overlap_width);
create_interpatch_ghost_zones(mx, my, patch_overlap_width);
create_interpatch_ghost_zones(my, px, patch_overlap_width);
px.create_mirror_symmetry_ghost_zone(px.max_rho_patch_edge());
py.create_mirror_symmetry_ghost_zone(py.max_rho_patch_edge());
mx.create_mirror_symmetry_ghost_zone(mx.min_rho_patch_edge());
my.create_mirror_symmetry_ghost_zone(my.min_rho_patch_edge());
// finish setting up the interpatch ghost zones
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" finishing interpatch setup");
finish_interpatch_setup(pz, px,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(pz, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(pz, mx,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(pz, my,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(px, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(py, mx,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(mx, my,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(my, px,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
assert_all_ghost_zones_fully_setup();
}
//******************************************************************************
//
// This function sets up (constructs and interlinks) the ghost zones
// for a +xy quadrant (mirrored) patch system.
//
void patch_system::setup_ghost_zones__plus_xy_quadrant_mirrored(int patch_overlap_width,
int ip_interp_handle, int ip_interp_par_table_handle,
bool print_msg_flag)
{
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" setting up +xy quadrant (mirrored) ghost zones");
patch &pz = ith_patch(patch_system_info::plus_xy_quadrant::patch_number__pz);
patch &px = ith_patch(patch_system_info::plus_xy_quadrant::patch_number__px);
patch &py = ith_patch(patch_system_info::plus_xy_quadrant::patch_number__py);
patch &mz = ith_patch(patch_system_info::plus_xy_quadrant::patch_number__mz);
// create the ghost zones
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" creating ghost zones");
create_interpatch_ghost_zones(pz, px, patch_overlap_width);
create_interpatch_ghost_zones(pz, py, patch_overlap_width);
create_interpatch_ghost_zones(px, py, patch_overlap_width);
create_interpatch_ghost_zones(mz, px, patch_overlap_width);
create_interpatch_ghost_zones(mz, py, patch_overlap_width);
pz.create_mirror_symmetry_ghost_zone(pz.min_rho_patch_edge());
pz.create_mirror_symmetry_ghost_zone(pz.min_sigma_patch_edge());
px.create_mirror_symmetry_ghost_zone(px.min_sigma_patch_edge());
py.create_mirror_symmetry_ghost_zone(py.max_sigma_patch_edge());
mz.create_mirror_symmetry_ghost_zone(mz.max_rho_patch_edge());
mz.create_mirror_symmetry_ghost_zone(mz.max_sigma_patch_edge());
// finish setting up the interpatch ghost zones
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" finishing interpatch setup");
finish_interpatch_setup(pz, px,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(pz, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(px, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(mz, px,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(mz, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
assert_all_ghost_zones_fully_setup();
}
//******************************************************************************
//
// This function sets up (constructs and interlinks) the ghost zones
// for a +xy quadrant (rotating) patch system.
//
void patch_system::setup_ghost_zones__plus_xy_quadrant_rotating(int patch_overlap_width,
int ip_interp_handle, int ip_interp_par_table_handle,
bool print_msg_flag)
{
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" setting up +xy quadrant (rotating) ghost zones");
patch &pz = ith_patch(patch_system_info::plus_xy_quadrant::patch_number__pz);
patch &px = ith_patch(patch_system_info::plus_xy_quadrant::patch_number__px);
patch &py = ith_patch(patch_system_info::plus_xy_quadrant::patch_number__py);
patch &mz = ith_patch(patch_system_info::plus_xy_quadrant::patch_number__mz);
// create the ghost zones
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" creating ghost zones");
create_interpatch_ghost_zones(pz, px, patch_overlap_width);
create_interpatch_ghost_zones(pz, py, patch_overlap_width);
create_interpatch_ghost_zones(px, py, patch_overlap_width);
create_interpatch_ghost_zones(mz, px, patch_overlap_width);
create_interpatch_ghost_zones(mz, py, patch_overlap_width);
create_periodic_symmetry_ghost_zones(pz.min_rho_patch_edge(),
pz.min_sigma_patch_edge(),
true);
create_periodic_symmetry_ghost_zones(px.min_sigma_patch_edge(),
py.max_sigma_patch_edge(),
true);
create_periodic_symmetry_ghost_zones(mz.max_rho_patch_edge(),
mz.max_sigma_patch_edge(),
true);
// finish setting up the interpatch ghost zones
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" finishing interpatch setup");
finish_interpatch_setup(pz, px,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(pz, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(px, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(mz, px,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(mz, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
assert_all_ghost_zones_fully_setup();
}
//******************************************************************************
//
// This function sets up (constructs and interlinks) the ghost zones
// for a +xz quadrant (mirrored) patch system.
//
void patch_system::setup_ghost_zones__plus_xz_quadrant_mirrored(int patch_overlap_width,
int ip_interp_handle, int ip_interp_par_table_handle,
bool print_msg_flag)
{
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" setting up +xz quadrant (mirrored) ghost zones");
patch &pz = ith_patch(patch_system_info::plus_xz_quadrant::patch_number__pz);
patch &px = ith_patch(patch_system_info::plus_xz_quadrant::patch_number__px);
patch &py = ith_patch(patch_system_info::plus_xz_quadrant::patch_number__py);
patch &my = ith_patch(patch_system_info::plus_xz_quadrant::patch_number__my);
// create the ghost zones
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" creating ghost zones");
create_interpatch_ghost_zones(pz, px, patch_overlap_width);
create_interpatch_ghost_zones(pz, py, patch_overlap_width);
create_interpatch_ghost_zones(pz, my, patch_overlap_width);
create_interpatch_ghost_zones(px, py, patch_overlap_width);
create_interpatch_ghost_zones(px, my, patch_overlap_width);
pz.create_mirror_symmetry_ghost_zone(pz.min_sigma_patch_edge());
px.create_mirror_symmetry_ghost_zone(px.max_rho_patch_edge());
py.create_mirror_symmetry_ghost_zone(py.max_rho_patch_edge());
py.create_mirror_symmetry_ghost_zone(py.max_sigma_patch_edge());
my.create_mirror_symmetry_ghost_zone(my.min_rho_patch_edge());
my.create_mirror_symmetry_ghost_zone(my.min_sigma_patch_edge());
// finish setting up the interpatch ghost zones
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" finishing interpatch setup");
finish_interpatch_setup(pz, px,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(pz, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(pz, my,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(px, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(px, my,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
assert_all_ghost_zones_fully_setup();
}
//******************************************************************************
//
// This function sets up (constructs and interlinks) the ghost zones
// for a +xz quadrant (rotating) patch system.
//
void patch_system::setup_ghost_zones__plus_xz_quadrant_rotating(int patch_overlap_width,
int ip_interp_handle, int ip_interp_par_table_handle,
bool print_msg_flag)
{
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" setting up +xz quadrant (rotating) ghost zones");
patch &pz = ith_patch(patch_system_info::plus_xz_quadrant::patch_number__pz);
patch &px = ith_patch(patch_system_info::plus_xz_quadrant::patch_number__px);
patch &py = ith_patch(patch_system_info::plus_xz_quadrant::patch_number__py);
patch &my = ith_patch(patch_system_info::plus_xz_quadrant::patch_number__my);
// create the ghost zones
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" creating ghost zones");
create_interpatch_ghost_zones(pz, px, patch_overlap_width);
create_interpatch_ghost_zones(pz, py, patch_overlap_width);
create_interpatch_ghost_zones(pz, my, patch_overlap_width);
create_interpatch_ghost_zones(px, py, patch_overlap_width);
create_interpatch_ghost_zones(px, my, patch_overlap_width);
px.create_mirror_symmetry_ghost_zone(px.max_rho_patch_edge());
py.create_mirror_symmetry_ghost_zone(py.max_rho_patch_edge());
my.create_mirror_symmetry_ghost_zone(my.min_rho_patch_edge());
create_periodic_symmetry_ghost_zones(pz.min_sigma_patch_edge(),
pz.min_sigma_patch_edge(),
false);
create_periodic_symmetry_ghost_zones(py.max_sigma_patch_edge(),
my.min_sigma_patch_edge(),
false);
// finish setting up the interpatch ghost zones
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" finishing interpatch setup");
finish_interpatch_setup(pz, px,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(pz, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(pz, my,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(px, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(px, my,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
assert_all_ghost_zones_fully_setup();
}
//******************************************************************************
//
// This function sets up (constructs and interlinks) the ghost zones
// for a +xyz octant (mirrored) patch system.
//
void patch_system::setup_ghost_zones__plus_xyz_octant_mirrored(int patch_overlap_width,
int ip_interp_handle, int ip_interp_par_table_handle,
bool print_msg_flag)
{
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" setting up +xyz octant (mirrored) ghost zones");
patch &pz = ith_patch(patch_system_info::plus_xyz_octant::patch_number__pz);
patch &px = ith_patch(patch_system_info::plus_xyz_octant::patch_number__px);
patch &py = ith_patch(patch_system_info::plus_xyz_octant::patch_number__py);
// create the ghost zones
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" creating ghost zones");
create_interpatch_ghost_zones(pz, px, patch_overlap_width);
create_interpatch_ghost_zones(pz, py, patch_overlap_width);
create_interpatch_ghost_zones(px, py, patch_overlap_width);
pz.create_mirror_symmetry_ghost_zone(pz.min_rho_patch_edge());
pz.create_mirror_symmetry_ghost_zone(pz.min_sigma_patch_edge());
px.create_mirror_symmetry_ghost_zone(px.max_rho_patch_edge());
px.create_mirror_symmetry_ghost_zone(px.min_sigma_patch_edge());
py.create_mirror_symmetry_ghost_zone(py.max_rho_patch_edge());
py.create_mirror_symmetry_ghost_zone(py.max_sigma_patch_edge());
// finish setting up the interpatch ghost zones
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" finishing interpatch setup");
finish_interpatch_setup(pz, px,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(pz, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(px, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
assert_all_ghost_zones_fully_setup();
}
//******************************************************************************
//
// This function sets up (constructs and interlinks) the ghost zones
// for a +xyz octant (rotating) patch system.
//
void patch_system::setup_ghost_zones__plus_xyz_octant_rotating(int patch_overlap_width,
int ip_interp_handle, int ip_interp_par_table_handle,
bool print_msg_flag)
{
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" setting up +xyz octant (rotating) ghost zones");
patch &pz = ith_patch(patch_system_info::plus_xyz_octant::patch_number__pz);
patch &px = ith_patch(patch_system_info::plus_xyz_octant::patch_number__px);
patch &py = ith_patch(patch_system_info::plus_xyz_octant::patch_number__py);
// create the ghost zones
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" creating ghost zones");
create_interpatch_ghost_zones(pz, px, patch_overlap_width);
create_interpatch_ghost_zones(pz, py, patch_overlap_width);
create_interpatch_ghost_zones(px, py, patch_overlap_width);
px.create_mirror_symmetry_ghost_zone(px.max_rho_patch_edge());
py.create_mirror_symmetry_ghost_zone(py.max_rho_patch_edge());
create_periodic_symmetry_ghost_zones(pz.min_rho_patch_edge(),
pz.min_sigma_patch_edge(),
true);
create_periodic_symmetry_ghost_zones(px.min_sigma_patch_edge(),
py.max_sigma_patch_edge(),
true);
// finish setting up the interpatch ghost zones
if (print_msg_flag)
then CCTK_VInfo(CCTK_THORNSTRING,
" finishing interpatch setup");
finish_interpatch_setup(pz, px,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(pz, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
finish_interpatch_setup(px, py,
patch_overlap_width,
ip_interp_handle, ip_interp_par_table_handle);
assert_all_ghost_zones_fully_setup();
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
//
// This function creates a pair of periodic-symmetry ghost zones.
//
// static
void patch_system::create_periodic_symmetry_ghost_zones(const patch_edge &ex, const patch_edge &ey,
bool ipar_map_is_plus)
{
ex.my_patch()
.create_periodic_symmetry_ghost_zone(ex, ey, ipar_map_is_plus);
if (ex == ey)
then
{
// ex and ey are the same edge (i.e. the symmetry maps the edge
// back to itself), so we only want to set up the edge once
// ==> no-op here
}
else
ey.my_patch()
.create_periodic_symmetry_ghost_zone(ey, ex, ipar_map_is_plus);
}
//******************************************************************************
//
// This function automagically figures out which edges of two adjacent
// patches are adjacent, then creates both patches' ghost zones on those
// edges and interlinks them with their respective patches.
//
// static
void patch_system::create_interpatch_ghost_zones(patch &px, patch &py,
int patch_overlap_width)
{
const patch_edge &ex = px.edge_adjacent_to_patch(py, patch_overlap_width);
const patch_edge &ey = py.edge_adjacent_to_patch(px, patch_overlap_width);
px.create_interpatch_ghost_zone(ex, ey, patch_overlap_width);
py.create_interpatch_ghost_zone(ey, ex, patch_overlap_width);
}
//******************************************************************************
//
// This function automagically figures out which edges of two adjacent
// patches are adjacent, then finishes setting up both ghost zones.
//
// static
void patch_system::finish_interpatch_setup(patch &px, patch &py,
int patch_overlap_width,
int ip_interp_handle, int ip_interp_par_table_handle)
{
const patch_edge &ex = px.edge_adjacent_to_patch(py, patch_overlap_width);
const patch_edge &ey = py.edge_adjacent_to_patch(px, patch_overlap_width);
px.ghost_zone_on_edge(ex)
.cast_to_interpatch_ghost_zone()
.finish_setup(ip_interp_handle, ip_interp_par_table_handle);
py.ghost_zone_on_edge(ey)
.cast_to_interpatch_ghost_zone()
.finish_setup(ip_interp_handle, ip_interp_par_table_handle);
}
//******************************************************************************
//
// This function assert()s that all ghost zones of all patches have
// been fully set up.
//
void patch_system::assert_all_ghost_zones_fully_setup() const
{
for (int pn = 0; pn < N_patches(); ++pn)
{
ith_patch(pn).assert_all_ghost_zones_fully_setup();
}
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
//
// This function decodes a patch system's type into N_patches.
//
// static
int patch_system::N_patches_of_type(enum patch_system_type type_in)
{
switch (type_in)
{
case patch_system__full_sphere:
return patch_system_info::full_sphere::N_patches;
case patch_system__plus_z_hemisphere:
return patch_system_info::plus_z_hemisphere::N_patches;
case patch_system__plus_xy_quadrant_mirrored:
case patch_system__plus_xy_quadrant_rotating:
return patch_system_info::plus_xy_quadrant::N_patches;
case patch_system__plus_xz_quadrant_mirrored:
case patch_system__plus_xz_quadrant_rotating:
return patch_system_info::plus_xz_quadrant::N_patches;
case patch_system__plus_xyz_octant_mirrored:
case patch_system__plus_xyz_octant_rotating:
return patch_system_info::plus_xyz_octant::N_patches;
default:
error_exit(PANIC_EXIT,
"***** patch_system::N_patches_of_type(): bad type=(int)%d!\n"
" (this should never happen!)\n",
int(type_in)); /*NOTREACHED*/
}
}
//******************************************************************************
//
// This function decodes a patch system's type into a human-readable
// type name (a C string).
//
// static
const char *patch_system::name_of_type(enum patch_system_type type_in)
{
switch (type_in)
{
case patch_system__full_sphere:
return "full sphere";
case patch_system__plus_z_hemisphere:
return "+z hemisphere";
case patch_system__plus_xy_quadrant_mirrored:
return "+xy quadrant (mirrored)";
case patch_system__plus_xy_quadrant_rotating:
return "+xy quadrant (rotating)";
case patch_system__plus_xz_quadrant_mirrored:
return "+xz quadrant (mirrored)";
case patch_system__plus_xz_quadrant_rotating:
return "+xz quadrant (rotating)";
case patch_system__plus_xyz_octant_mirrored:
return "+xyz octant (mirrored)";
case patch_system__plus_xyz_octant_rotating:
return "+xyz octant (rotating)";
default:
error_exit(PANIC_EXIT,
"***** patch_system::name_of_type(): bad type=(int)%d!\n"
" (this should never happen!)\n",
int(type_in)); /*NOTREACHED*/
}
}
//******************************************************************************
//
// This function encodes a human-readable type name (a C string) into
// a patch system's type into.
//
// static
enum patch_system::patch_system_type
patch_system::type_of_name(const char *name_in)
{
if (strcmp(name_in, "full sphere") == 0)
return patch_system__full_sphere;
else if (strcmp(name_in, "+z hemisphere") == 0)
return patch_system__plus_z_hemisphere;
else if (strcmp(name_in, "+xy quadrant (mirrored)") == 0)
return patch_system__plus_xy_quadrant_mirrored;
else if (strcmp(name_in, "+xy quadrant (rotating)") == 0)
return patch_system__plus_xy_quadrant_rotating;
else if (strcmp(name_in, "+xz quadrant (mirrored)") == 0)
return patch_system__plus_xz_quadrant_mirrored;
else if (strcmp(name_in, "+xz quadrant (rotating)") == 0)
return patch_system__plus_xz_quadrant_rotating;
else if (strcmp(name_in, "+xyz octant (mirrored)") == 0)
return patch_system__plus_xyz_octant_mirrored;
else if (strcmp(name_in, "+xyz octant (rotating)") == 0)
return patch_system__plus_xyz_octant_rotating;
else
error_exit(PANIC_EXIT,
"***** patch_system::type_of_name(): unknown name=\"%s\"!",
name_in); /*NOTREACHED*/
}
//******************************************************************************
//
// This function finds a (the) patch with a specified sign and xyz ctype.
// If no such patch exists, it does an error_exit() (and doesn't return
// to the caller).
//
// FIXME:
// - This function could be implemented to be very fast (using the
// patch numbers in patch_system_info::), but right now it just does
// a sequential search through all the patches, so it's pretty slow :(
//
const patch &patch_system::plus_or_minus_xyz_patch(bool is_plus, char ctype)
const
{
for (int pn = 0; pn < N_patches(); ++pn)
{
const patch &p = ith_patch(pn);
if ((p.is_plus() == is_plus) && (p.ctype() == ctype))
then return p;
}
error_exit(ERROR_EXIT,
"***** patch_system::plus_or_minus_xyz_patch():\n"
" can't find any %c%c patch!",
(is_plus ? '+' : '-'), ctype); /*NOTREACHED*/
}
//******************************************************************************
//
// This function finds a patch from its human-readable name, and returns
// the patch number, or does an error_exit() if no patch is found with
// the specified name.
//
int patch_system::patch_number_of_name(const char *name) const
{
for (int pn = 0; pn < N_patches(); ++pn)
{
if (strcmp(ith_patch(pn).name(), name) == 0)
return pn;
}
error_exit(ERROR_EXIT,
"***** patch_system::patch_number_of_name():\n"
" no patch with name \"%s\"!\n",
name); /*NOTREACHED*/
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
//
// This function decodes a 0-origin grid point number into a
// (patch,irho,isigma) triple.
//
// Arguments:
// gpn = The grid point number to decode.
// (irho,isigma) = (out) The decoded patch coordinates.
//
// Results:
// This function returns a reference to the decoded patch. (An alternative
// design would be to return this via a patch*& argument, but the design
// used here seems slightly cleaner to use in practice.)
//
const patch &
patch_system::patch_irho_isigma_of_gpn(int gpn, int &irho, int &isigma)
const
{
assert(gpn >= 0);
assert(gpn < N_grid_points());
for (int pn = 0; pn < N_patches(); ++pn)
{
// n.b. [pn+1] is ok since starting_gpn_[] has size N_patches()+1
if ((gpn >= starting_gpn_[pn]) && (gpn < starting_gpn_[pn + 1]))
then
{
const patch &p = ith_patch(pn);
const int gpn_in_patch = gpn - starting_gpn_[pn];
assert(gpn_in_patch >= 0);
assert(gpn_in_patch < p.N_grid_points());
p.irho_isigma_of_gpn(gpn_in_patch, irho, isigma);
return p;
}
}
error_exit(PANIC_EXIT,
"***** patch_system::patch_irho_isigma_of_gpn(gpn=%d):\n"
" couldn't find any patch! (this should never happen!)\n"
" N_grid_points()=%d\n",
gpn,
N_grid_points()); /*NOTREACHED*/
}
//******************************************************************************
//
// This function decodes a 0-origin grid point number into a *ghosted*
// (patch,irho,isigma) triple.
//
// Arguments:
// gpn = The grid point number to decode.
// (irho,isigma) = (out) The decoded patch coordinates.
//
// Results:
// This function returns a reference to the decoded patch. (An alternative
// design would be to return this via a patch*& argument, but the design
// used here seems slightly cleaner to use in practice.)
//
const patch &
patch_system::ghosted_patch_irho_isigma_of_gpn(int gpn, int &irho, int &isigma)
const
{
assert(gpn >= 0);
assert(gpn < ghosted_N_grid_points());
for (int pn = 0; pn < N_patches(); ++pn)
{
// n.b. [pn+1] is ok since ghosted_starting_gpn_[]
// has size N_patches()+1
if ((gpn >= ghosted_starting_gpn_[pn]) && (gpn < ghosted_starting_gpn_[pn + 1]))
then
{
const patch &p = ith_patch(pn);
const int gpn_in_patch = gpn - ghosted_starting_gpn_[pn];
assert(gpn_in_patch >= 0);
assert(gpn_in_patch < p.ghosted_N_grid_points());
p.ghosted_irho_isigma_of_gpn(gpn_in_patch, irho, isigma);
return p;
}
}
error_exit(PANIC_EXIT,
"***** patch_system::ghosted_patch_irho_isigma_of_gpn(gpn=%d):\n"
" couldn't find any patch! (this should never happen!)\n"
" ghosted_N_grid_points()=%d\n",
gpn,
ghosted_N_grid_points()); /*NOTREACHED*/
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
//
// This function sets a (nominal-grid) gridfn to a constant value.
//
void patch_system::set_gridfn_to_constant(fp a, int dst_gfn)
{
for (int pn = 0; pn < N_patches(); ++pn)
{
patch &p = ith_patch(pn);
for (int irho = p.min_irho(); irho <= p.max_irho(); ++irho)
{
for (int isigma = p.min_isigma();
isigma <= p.max_isigma();
++isigma)
{
p.gridfn(dst_gfn, irho, isigma) = a;
}
}
}
}
//******************************************************************************
//
// This function copies one (nominal-grid) gridfn to another.
//
void patch_system::gridfn_copy(int src_gfn, int dst_gfn)
{
for (int pn = 0; pn < N_patches(); ++pn)
{
patch &p = ith_patch(pn);
for (int irho = p.min_irho(); irho <= p.max_irho(); ++irho)
{
for (int isigma = p.min_isigma();
isigma <= p.max_isigma();
++isigma)
{
p.gridfn(dst_gfn, irho, isigma) = p.gridfn(src_gfn, irho, isigma);
}
}
}
}
//******************************************************************************
//
// This function adds a scalar to a ghosted gridfn.
//
void patch_system::add_to_ghosted_gridfn(fp delta, int ghosted_dst_gfn)
{
for (int pn = 0; pn < N_patches(); ++pn)
{
patch &p = ith_patch(pn);
for (int irho = p.ghosted_min_irho();
irho <= p.ghosted_max_irho();
++irho)
{
for (int isigma = p.ghosted_min_isigma();
isigma <= p.ghosted_max_isigma();
++isigma)
{
p.ghosted_gridfn(ghosted_dst_gfn, irho, isigma) += delta;
}
}
}
}
//******************************************************************************
//
// Recentering
//
void patch_system::recentering(fp x, fp y, fp z)
{
global_coords_.recentering(x, y, z);
}
//******************************************************************************
//
// This function computes norms of a nominal-grid gridfn.
//
void patch_system::gridfn_norms(int src_gfn, jtutil::norm<fp> &norms)
const
{
if (!is_valid_gfn(src_gfn))
then error_exit(ERROR_EXIT,
"***** patch_system::gridfn_norms(): invalid src_gfn=%d!\n",
src_gfn); /*NOTREACHED*/
norms.reset();
for (int pn = 0; pn < N_patches(); ++pn)
{
const patch &p = ith_patch(pn);
for (int irho = p.min_irho(); irho <= p.max_irho(); ++irho)
{
for (int isigma = p.min_isigma();
isigma <= p.max_isigma();
++isigma)
{
norms.data(p.gridfn(src_gfn, irho, isigma));
}
}
}
}
//******************************************************************************
//
// This function computes norms of a ghosted-grid gridfn over the
// nominal grid.
//
void patch_system::ghosted_gridfn_norms(int ghosted_src_gfn,
jtutil::norm<fp> &norms)
const
{
if (!is_valid_ghosted_gfn(ghosted_src_gfn))
then error_exit(ERROR_EXIT,
"***** patch_system::gridfn_norms(): invalid ghosted_src_gfn=%d!\n",
ghosted_src_gfn); /*NOTREACHED*/
norms.reset();
for (int pn = 0; pn < N_patches(); ++pn)
{
const patch &p = ith_patch(pn);
for (int irho = p.min_irho(); irho <= p.max_irho(); ++irho)
{
for (int isigma = p.min_isigma();
isigma <= p.max_isigma();
++isigma)
{
norms.data(p.ghosted_gridfn(ghosted_src_gfn, irho, isigma));
}
}
}
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
//
// This function computes an approximation to the circumference of a
// surface in the xy, xz, or yz plane. Note that we compute the full
// circumference all around the sphere, even if the patch system only
// covers a proper subset of this.
//
// We assume that adjacent patches are butt-joined, i.e. that their
// nominal boundaries just touch.
//
// Arguments:
// plane[] = (in) "xy", "xz", or "yz" to specify the integration 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 patch_system::circumference(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 patch::integration_method method)
const
{
//
// compute arc length around the patch system
//
fp arc_length = 0.0;
for (int pn = 0; pn < N_patches(); ++pn)
{
const patch &p = ith_patch(pn);
if ((p.ctype() == plane[0]) || (p.ctype() == plane[1]))
then arc_length += p.plane_arc_length(plane,
ghosted_radius_gfn,
g_xx_gfn, g_xy_gfn, g_xz_gfn,
g_yy_gfn, g_yz_gfn,
g_zz_gfn,
method);
}
//
// correct the arc length
// for the fact that the patch system may not cover the full 2-sphere
//
switch (type())
{
case patch_system__full_sphere:
break;
case patch_system__plus_z_hemisphere:
arc_length *= ((plane[0] == 'x') && (plane[1] == 'y')) ? 1.0 : 2.0;
break;
case patch_system__plus_xy_quadrant_mirrored:
case patch_system__plus_xy_quadrant_rotating:
arc_length *= ((plane[0] == 'x') && (plane[1] == 'y')) ? 4.0 : 2.0;
break;
case patch_system__plus_xz_quadrant_mirrored:
case patch_system__plus_xz_quadrant_rotating:
arc_length *= ((plane[0] == 'x') && (plane[1] == 'z')) ? 4.0 : 2.0;
break;
case patch_system__plus_xyz_octant_mirrored:
case patch_system__plus_xyz_octant_rotating:
arc_length *= 4.0;
break;
default:
error_exit(PANIC_EXIT,
"***** patch_system::circumference(): unknown patch system type()=(int)%d!\n"
" (this should never happen!)\n",
int(type())); /*NOTREACHED*/
}
return arc_length;
}
//******************************************************************************
//
// This function computes an approximation to the (surface) integral of
// a gridfn over the 2-sphere
// $\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).
//
// We assume that adjacent patches are butt-joined, i.e. that their
// nominal boundaries just touch.
//
// Arguments:
// unknown_src_gfn = (in) The gridfn to be integrated. This may be
// either nominal-grid or ghosted-grid.
// src_gfn_is_even_across_{xy,xz,yz}_plane
// = (in) Boolean flags specifying whether the gridfn to be integrated
// is even (true) or odd (false) across the corresponding planes.
// Only the flags corresponding to boundaries of the patch system
// are used. For example, for a plus_z_hemisphere patch system,
// only the src_gfn_is_even_across_xy_plane flag is used.
// 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_system::integrate_gridfn(int unknown_src_gfn,
bool src_gfn_is_even_across_xy_plane,
bool src_gfn_is_even_across_xz_plane,
bool src_gfn_is_even_across_yz_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 patch::integration_method method)
const
{
//
// compute integral over patch system
//
fp integral = 0.0;
for (int pn = 0; pn < N_patches(); ++pn)
{
const patch &p = ith_patch(pn);
integral += p.integrate_gridfn(unknown_src_gfn,
ghosted_radius_gfn,
g_xx_gfn, g_xy_gfn, g_xz_gfn,
g_yy_gfn, g_yz_gfn,
g_zz_gfn,
method);
}
//
// correct the integral
// for the fact that the patch system may not cover the full 2-sphere
//
switch (type())
{
case patch_system__full_sphere:
break;
case patch_system__plus_z_hemisphere:
integral *= src_gfn_is_even_across_xy_plane ? 2.0 : 0.0;
break;
case patch_system__plus_xy_quadrant_mirrored:
case patch_system__plus_xy_quadrant_rotating:
integral *= src_gfn_is_even_across_xz_plane ? 2.0 : 0.0;
integral *= src_gfn_is_even_across_yz_plane ? 2.0 : 0.0;
break;
case patch_system__plus_xz_quadrant_mirrored:
case patch_system__plus_xz_quadrant_rotating:
integral *= src_gfn_is_even_across_xy_plane ? 2.0 : 0.0;
integral *= src_gfn_is_even_across_yz_plane ? 2.0 : 0.0;
break;
case patch_system__plus_xyz_octant_mirrored:
case patch_system__plus_xyz_octant_rotating:
integral *= src_gfn_is_even_across_xy_plane ? 2.0 : 0.0;
integral *= src_gfn_is_even_across_xz_plane ? 2.0 : 0.0;
integral *= src_gfn_is_even_across_yz_plane ? 2.0 : 0.0;
break;
default:
error_exit(PANIC_EXIT,
"***** patch_system::integrate_gridfn(): bad patch system type()=(int)%d!\n"
" (this should never happen!)\n",
int(type())); /*NOTREACHED*/
}
return integral;
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
//
// This function finds what patch contains (the ray from the origin to)
// a given local (x,y,z) position.
//
// If there are multiple patches containing the position, we return the
// one which would still contain it if patches didn't overlap; if multiple
// patches satisfy this criterion then it's arbitrary which one we return.
//
// If no patch contains the position (this can only if the point as at
// the local coordinate origin, or for a non--full-sphere patch system),
// then we return a NULL pointer.
//
// Arguments:
// (x,y,z) = The local coordinates to be converted.
//
// Results:
// This function returns a reference to the containing patch.
//
const patch *patch_system::patch_containing_local_xyz(fp x, fp y, fp z)
const
{
if ((x == 0.0) && (y == 0.0) && (z == 0.0))
then return NULL;
// to which axis is (x,y,z) closest?
// ... or equivalently, which of |x|, |y|, and |z| is largest?
const fp abs_x = jtutil::abs(x);
const fp abs_y = jtutil::abs(y);
const fp abs_z = jtutil::abs(z);
if ((abs_z >= abs_x) && (abs_z >= abs_y))
then return &plus_or_minus_xyz_patch(z > 0.0, 'z'); // +/- z patch
else if ((abs_x >= abs_y) && (abs_x >= abs_z))
then return &plus_or_minus_xyz_patch(x > 0.0, 'x'); // +/- x patch
else if ((abs_y >= abs_x) && (abs_y >= abs_z))
then return &plus_or_minus_xyz_patch(y > 0.0, 'y'); // +/- y patch
else
error_exit(ERROR_EXIT,
"***** patch_system::patch_containing_local_xyz():\n"
" unknown (wierd!) ordering of |x|, |y|, and |z|!\n"
" (this should never happen!)\n"
" [local] (x,y,z)=(%g,%g,%g)\n",
double(x), double(y), double(z)); /*NOTREACHED*/
}
//******************************************************************************
//
// This function computes the radius of a patch-system 2-surface in the
// direction of a specified local (x,y,z) point, taking into account any
// patch-system symmetries. If the point coincides with the local origin,
// we return the dummy value 1.0.
//
// Bugs:
// Due to the surface-interpolator overhead, repeatedly calling this
// function is rather inefficient.
//
fp patch_system::radius_in_local_xyz_direction(int ghosted_radius_gfn,
fp x, fp y, fp z)
const
{
if ((x == 0.0) && (y == 0.0) && (z == 0.0))
then return 1.0;
//
// apply symmetries to map (x,y,z) into that part of the 2-sphere
// which is covered by the patch system
//
switch (type())
{
case patch_system__full_sphere:
break;
case patch_system__plus_z_hemisphere:
z = fabs(z);
break;
case patch_system__plus_xy_quadrant_mirrored:
case patch_system__plus_xy_quadrant_rotating:
x = fabs(x);
y = fabs(y);
break;
case patch_system__plus_xz_quadrant_mirrored:
case patch_system__plus_xz_quadrant_rotating:
x = fabs(x);
z = fabs(z);
break;
case patch_system__plus_xyz_octant_mirrored:
case patch_system__plus_xyz_octant_rotating:
x = fabs(x);
y = fabs(y);
z = fabs(z);
break;
default:
error_exit(PANIC_EXIT,
"***** patch_system::radius_in_local_xyz_direction():\n"
" unknown patch system type()=(int)%d!\n"
" (this should never happen!)\n",
int(type())); /*NOTREACHED*/
}
const patch *p_ptr = patch_containing_local_xyz(x, y, z);
if (p_ptr == NULL)
then error_exit(ERROR_EXIT,
"***** patch_system::radius_in_local_xyz_direction():\n"
" can't find containing patch!\n"
" (this should never happen!)\n"
" [local] (x,y,z)=(%g,%g,%g)\n",
double(x), double(y), double(z)); /*NOTREACHED*/
const patch &p = *p_ptr;
const fp rho = p.rho_of_xyz(x, y, z);
const fp sigma = p.sigma_of_xyz(x, y, z);
//
// Set up the surface interpolator to interpolate the surface radius
// gridfn to the (rho,sigma) coordinates:
//
// Notes on the interpolator setup:
// * The interpolator assumes Fortran subscripting, so we take the
// coordinates in the order (sigma,rho) to match our C subscripting
// in the patch system.
// * To avoid having to set up min/max array subscripts in the parameter
// table, we treat the patch as using 0-origin (integer) array subscripts
// (irho - ghosted_min_irho(), isigma - ghosted_min_isigma()). However,
// we use the usual floating-point coordinates.
//
const int N_dims = 2;
const CCTK_REAL coord_origin[N_dims] = {p.ghosted_min_sigma(), p.ghosted_min_rho()};
const CCTK_REAL coord_delta[N_dims] = {p.delta_sigma(), p.delta_rho()};
const int N_interp_points = 1;
const int interp_coords_type_code = CCTK_VARIABLE_REAL;
const void *const interp_coords[N_dims] = {static_cast<const void *>(&sigma), static_cast<const void *>(&rho)};
const int N_input_arrays = 1;
const CCTK_INT input_array_dims[N_dims] = {p.ghosted_N_isigma(), p.ghosted_N_irho()};
const CCTK_INT input_array_type_codes[N_input_arrays] = {CCTK_VARIABLE_REAL};
const void *const input_arrays[N_input_arrays] = {
static_cast<const void *>(
p.ghosted_gridfn_data_array(ghosted_radius_gfn))};
const int N_output_arrays = 1;
const CCTK_INT output_array_type_codes[N_output_arrays] = {CCTK_VARIABLE_REAL};
fp xyz_radius;
void *const output_arrays[N_output_arrays] = {static_cast<void *>(&xyz_radius)};
return xyz_radius;
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
//
// This function prints an unknown-grid gridfn in ASCII format to a
// named output file. The output format is suitable for a gnuplot
// 'splot' command. (Individual patches may be selected with the
// select.patch program (perl script).) The output format is either
// # print_xyz_flag == false
// dpx dpy gridfn
// or
// # print_xyz_flag == true
// dpx dpy gridfn global_x global_y global_z
// where global_[xyz} are derived from the angular position and a
// specified (unknown-grid) radius gridfn.
//
void patch_system::print_unknown_gridfn(bool ghosted_flag, int unknown_gfn,
bool print_xyz_flag, bool radius_is_ghosted_flag,
int unknown_radius_gfn,
const char output_file_name[], bool want_ghost_zones)
const
{
if (want_ghost_zones && !ghosted_flag)
then error_exit(PANIC_EXIT,
"***** patch_system::print_unknown_gridfn(unknown_gfn=%d):\n"
" can't have want_ghost_zones && !ghosted_flag !\n",
unknown_gfn); /*NOTREACHED*/
if (want_ghost_zones && print_xyz_flag && !radius_is_ghosted_flag)
then error_exit(PANIC_EXIT,
"***** patch_system::print_unknown_gridfn(unknown_gfn=%d):\n"
" can't have want_ghost_zones && print_xyz_flag\n"
" && !radius_is_ghosted_flag!\n"
" unknown_radius_gfn=%d\n",
unknown_gfn,
unknown_radius_gfn); /*NOTREACHED*/
FILE *output_fp = fopen(output_file_name, "w");
if (output_fp == NULL)
then error_exit(ERROR_EXIT,
"***** patch_system::print_unknown_gridfn(unknown_gfn=%d):\n"
" can't open output file \"%s\"\n!",
unknown_gfn,
output_file_name); /*NOTREACHED*/
fprintf(output_fp, "# N_patches = %d\n", N_patches());
fprintf(output_fp, "# origin = %.15g %.15g %.15g\n",
double(origin_x()), double(origin_y()), double(origin_z()));
fprintf(output_fp, "\n");
for (int pn = 0; pn < N_patches(); ++pn)
{
const patch &p = ith_patch(pn);
fprintf(output_fp, "### %s patch\n", p.name());
fprintf(output_fp, "# N_rho = %d\n",
p.effective_N_irho(want_ghost_zones));
fprintf(output_fp, "# N_sigma = %d\n",
p.effective_N_isigma(want_ghost_zones));
fprintf(output_fp, "# %s_gfn=%d\n",
(ghosted_flag ? "ghosted" : "nominal"), unknown_gfn);
fprintf(output_fp, "# dpx = %s\n", p.name_of_dpx());
fprintf(output_fp, "# dpy = %s\n", p.name_of_dpy());
fprintf(output_fp, "#\n");
fprintf(output_fp,
print_xyz_flag
? "# dpx\tdpy\tgridfn\tglobal_x\tglobal_y\tglobal_z\n"
: "# dpx\tdpy\tgridfn\n");
for (int irho = p.effective_min_irho(want_ghost_zones);
irho <= p.effective_max_irho(want_ghost_zones);
++irho)
{
for (int isigma = p.effective_min_isigma(want_ghost_zones);
isigma <= p.effective_max_isigma(want_ghost_zones);
++isigma)
{
const fp rho = p.rho_of_irho(irho);
const fp sigma = p.sigma_of_isigma(isigma);
const fp dpx = p.dpx_of_rho_sigma(rho, sigma);
const fp dpy = p.dpy_of_rho_sigma(rho, sigma);
fprintf(output_fp,
"%g\t%g\t%#.15g",
dpx, dpy, p.unknown_gridfn(ghosted_flag, unknown_gfn, irho, isigma));
if (print_xyz_flag)
then
{
const fp r = p.unknown_gridfn(radius_is_ghosted_flag,
unknown_radius_gfn,
irho, isigma);
fp local_x, local_y, local_z;
p.xyz_of_r_rho_sigma(r, rho, sigma,
local_x, local_y, local_z);
const fp global_x = origin_x() + local_x;
const fp global_y = origin_y() + local_y;
const fp global_z = origin_z() + local_z;
fprintf(output_fp,
"\t%#.10g\t%#.10g\t%#.10g",
global_x, global_y, global_z);
}
fprintf(output_fp, "\n");
}
fprintf(output_fp, "\n");
}
fprintf(output_fp, "\n");
}
fclose(output_fp);
}
//******************************************************************************
//
// This function reads an unknown-grid gridfn in ASCII format from
// a named input file. Comments ('#' in column 1) and blank lines
// are ignored, otherwise the input format matches that written by
// print_unknown_gridfn(): the first 3 numbers on each line are taken
// to be dpx, dpy, and the gridfn value; anything else on the line is
// ignored.
//
void patch_system::read_unknown_gridfn(bool ghosted_flag, int unknown_gfn,
const char input_file_name[],
bool want_ghost_zones)
{
if (want_ghost_zones && !ghosted_flag)
then error_exit(PANIC_EXIT,
"***** patch_system::read_unknown_gridfn(unknown_gfn=%d):\n"
" can't have want_ghost_zones && !ghosted_flag !\n",
unknown_gfn); /*NOTREACHED*/
FILE *input_fp = fopen(input_file_name, "r");
if (input_fp == NULL)
then error_exit(ERROR_EXIT,
"***** patch_system::read_unknown_gridfn(unknown_gfn=%d):\n"
" can't open input file \"%s\"\n!",
unknown_gfn,
input_file_name); /*NOTREACHED*/
int line_number = 1;
for (int pn = 0; pn < N_patches(); ++pn)
{
patch &p = ith_patch(pn);
for (int irho = p.effective_min_irho(want_ghost_zones);
irho <= p.effective_max_irho(want_ghost_zones);
++irho)
{
for (int isigma = p.effective_min_isigma(want_ghost_zones);
isigma <= p.effective_max_isigma(want_ghost_zones);
++isigma)
{
const fp rho = p.rho_of_irho(irho);
const fp sigma = p.sigma_of_isigma(isigma);
const fp dpx = p.dpx_of_rho_sigma(rho, sigma);
const fp dpy = p.dpy_of_rho_sigma(rho, sigma);
const int buffer_size = 250;
char buffer[buffer_size];
// read/discard comments and blank lines
do
{
if (fgets(buffer, buffer_size, input_fp) == NULL)
then error_exit(ERROR_EXIT,
"***** patch::read_unknown_gridfn(%s patch, unknown_gfn=%d):\n"
" I/O error or unexpected end-of-file on input!\n"
" at irho=%d of [%d,%d], isigma=%d of [%d,%d]\n"
" dpx=%g dpy=%g\n",
p.name(), unknown_gfn,
irho, p.effective_min_irho(want_ghost_zones),
p.effective_max_irho(want_ghost_zones),
isigma,
p.effective_min_isigma(want_ghost_zones),
p.effective_max_isigma(want_ghost_zones),
dpx, dpy); /*NOTREACHED*/
++line_number;
} while ((buffer[0] == '#') || (buffer[0] == '\n'));
double read_dpx, read_dpy, read_gridfn_value;
if (sscanf(buffer, "%lf %lf %lf",
&read_dpx, &read_dpy, &read_gridfn_value) != 3)
then error_exit(ERROR_EXIT,
"***** patch::read_unknown_gridfn(%s patch, unknown_gfn=%d):\n"
" bad input data at input line %d!\n",
p.name(), unknown_gfn,
line_number); /*NOTREACHED*/
if (!(jtutil::fuzzy<fp>::EQ(read_dpx, dpx) && jtutil::fuzzy<fp>::EQ(read_dpy, dpy)))
then error_exit(ERROR_EXIT,
"***** patch::read_unknown_gridfn(%s patch, unknown_gfn=%d):\n"
" wrong (dpx,dpy) at input line %d!\n"
" expected (%g,%g)\n"
" read (%g,%g)\n",
p.name(), unknown_gfn,
line_number,
dpx, dpy,
read_dpx, read_dpy); /*NOTREACHED*/
p.unknown_gridfn(ghosted_flag,
unknown_gfn, irho, isigma) = read_gridfn_value;
}
}
}
fclose(input_fp);
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
//
// This function "synchronizes" all ghost zones of all patches, i.e. it
// update the ghost-zone values of the specified gridfns via the appropriate
// sequence of symmetry operations and interpatch interpolations. This
// process is described in detail in the header comments in "ghost_zone.hh".
//
void patch_system::synchronize(int ghosted_min_gfn_to_sync,
int ghosted_max_gfn_to_sync)
{
//
// Phase 1:
// Fill in gridfn data at all the non-corner points of symmetry ghost
// zones, using the symmetries to get this data from its "home patch"
// nominal grids.
//
{
for (int pn = 0; pn < N_patches(); ++pn)
{
patch &p = ith_patch(pn);
// 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)
{
ghost_zone &gz = p.minmax_ang_ghost_zone(want_min, want_rho);
if (gz.is_symmetry())
then gz.synchronize(ghosted_min_gfn_to_sync,
ghosted_max_gfn_to_sync,
false, // want corners?
true); // want non-corner?
}
}
}
}
//
// Phase 2:
// Fill in gridfn data in all the interpatch ghost zones, using interpatch
// interpolation from neighboring patches as described above.
//
{
for (int pn = 0; pn < N_patches(); ++pn)
{
patch &p = ith_patch(pn);
// 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)
{
ghost_zone &gz = p.minmax_ang_ghost_zone(want_min, want_rho);
if (gz.is_interpatch())
then gz.synchronize(ghosted_min_gfn_to_sync,
ghosted_max_gfn_to_sync);
}
}
}
}
//
// Phase 3:
// Fill in gridfn data at all the corner points of symmetry ghost zones,
// using the symmetries to get this data from its "home patch" nominal
// grids or ghost zones.
//
{
for (int pn = 0; pn < N_patches(); ++pn)
{
patch &p = ith_patch(pn);
// 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)
{
ghost_zone &gz = p.minmax_ang_ghost_zone(want_min, want_rho);
if (gz.is_symmetry())
then gz.synchronize(ghosted_min_gfn_to_sync,
ghosted_max_gfn_to_sync,
true, // want corners?
false); // want non-corner?
}
}
}
}
}
//******************************************************************************
//
// This function does any precomputation necessary to compute the Jacobian
// of synchronize() , taking into account synchronize()'s full 3-phase
// algorithm. In practice, this means it computes the individual Jacobian
// of each ghost zone, and sets global_{min,max}_ym_ .
//
void patch_system::compute_synchronize_Jacobian(int ghosted_min_gfn_to_sync,
int ghosted_max_gfn_to_sync)
const
{
global_min_ym_ = +INT_MAX;
global_max_ym_ = -INT_MAX;
for (int pn = 0; pn < N_patches(); ++pn)
{
const patch &p = ith_patch(pn);
// 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)
{
ghost_zone &gz = p.minmax_ang_ghost_zone(want_min, want_rho);
// is dummy//gz.compute_Jacobian(ghosted_min_gfn_to_sync, ghosted_max_gfn_to_sync);
global_min_ym_ = min(global_min_ym_,
gz.Jacobian_min_y_ipar_m());
global_max_ym_ = max(global_max_ym_,
gz.Jacobian_max_y_ipar_m());
}
}
}
}
//******************************************************************************
//
// Given that compute_synchronize_Jacobian() has been called, this
// function computes the global min/max m over all ghost zone points.
// This is useful for sizing the buffer for synchronize_Jacobian().
//
void patch_system::synchronize_Jacobian_global_minmax_ym(int &min_ym, int &max_ym)
const
{
min_ym = global_min_ym_;
max_ym = global_max_ym_;
}
//******************************************************************************
//
// Given that compute_synchronize_Jacobian() has been called, this
// function computes a single row of the Jacobian, taking into account
// synchronize()'s 3-phase algorithm:
// - It returns the edge to which the y point belongs (the caller can get
// the patch from this edge).
// - It stores y_iperp and y_posn and min/max ym in the named arguments.
// - It stores the Jacobian elements
// partial synchronize() gridfn(ghosted_gfn, px, x_iperp, x_ipar)
// -------------------------------------------------------------
// partial gridfn(ghosted_gfn, py, y_iperp, y_posn+ym)
// in the caller-supplied buffer
// Jacobian_buffer(ym)
// for each ym in the min/max ym range.
//
// In practice, the main task of this function is taking into account
// synchronize()'s 3-phase algorithm. There are several cases:
// - ghost zone is symmetry && x point is in non-corner
// ==> x value was computed by a phase 1 symmetry operation,
// using (only) nominal-grid data
// ==> overall Jacobian(x,y) = ghost zone Jacobian(x,y)
// - ghost zone is symmetry && x point is in corner
// --> x value was computed by a phase 3 symmetry operation,
// from some point (call it z),
// ==> overall Jacobian(x,y) = overall Jacobian(z,y)
// ==> call this function recursively to get z's Jacobian
// (z must be in the noncorner part of some ghost zone,
// so this won't lead to infinite recursion)
// - ghost zone is interpatch
// ==> x value was computed by a phase 2 interpatch interpolation
// - using (only) nominal-grid data
// ==> overall Jacobian(x,y) = ghost zone Jacobian(x,y)
// - using a mixture of nominal-grid data
// and data computed by a phase 1 symmetry operation
// ==> overall Jacobian(x,y) = "fold" ghost zone Jacobian(x,y)
// to take the phase 1 symmetry
// operation into account
//
const patch_edge &
patch_system::synchronize_Jacobian(const ghost_zone &xgz,
int x_iperp, int x_ipar,
int &y_iperp,
int &y_posn, int &min_ym, int &max_ym,
jtutil::array1d<fp> &Jacobian_buffer)
const
{
const patch_edge &xe = xgz.my_edge();
if (xgz.is_symmetry() && xe.ipar_is_in_noncorner(x_ipar))
then
{
// ghost zone is symmetry && x point is in non-corner
// ==> x value was computed by a phase 1 symmetry operation,
// using (only) nominal-grid data
// ==> overall Jacobian(x,y) = ghost zone Jacobian(x,y)
return ghost_zone_Jacobian(xgz,
x_iperp, x_ipar,
y_iperp,
y_posn, min_ym, max_ym,
Jacobian_buffer);
}
else if (xgz.is_symmetry() && xe.ipar_is_in_corner(x_ipar))
then
{
// ghost zone is symmetry && x point is in corner
// --> x value was computed by a phase 3 symmetry operation,
// from some point (call it z),
// ==> overall Jacobian(x,y) = overall Jacobian(z,y)
// ==> call this function recursively to get z's Jacobian
// (z must be in the noncorner part of some ghost zone,
// so this won't lead to infinite recursion)
const patch &zp = xgz.other_patch();
const patch_edge &ze = xgz.other_edge();
const symmetry_ghost_zone &xsgz = xgz.cast_to_symmetry_ghost_zone();
const int z_iperp = xsgz.iperp_map_of_iperp(x_iperp);
const int z_ipar = xsgz.ipar_map_of_ipar(x_ipar);
//
// Computing z's edge/ghost zone is tricky. For example:
// |
// p1 e3|e4 p2
// |
// | z
// -----------e1-----------+------------e2---------
// | x
// |
// Here the point x in the corner of p1's e1 ghost zone,
// is computed by the phase 3 symmetry operation (a reflection
// about e1) from z. Thus zp == p1 and ze == e1.
//
// But we need to "turn the corner" to compute z's "true" edge
// e3 (so we can recursively call this function to compute z's
// Jacobian). Thus we explicitly check which ghost zone of p1
// (here the e3 ghost zone) contains the point z.
//
const int z_irho = ze.irho_of_iperp_ipar(z_iperp, z_ipar);
const int z_isigma = ze.isigma_of_iperp_ipar(z_iperp, z_ipar);
const ghost_zone &true_zgz = zp.ghost_zone_containing_noncorner_point(z_irho, z_isigma);
const patch_edge &true_ze = true_zgz.my_edge();
const int true_z_iperp = true_ze.iperp_of_irho_isigma(z_irho, z_isigma);
const int true_z_ipar = true_ze.ipar_of_irho_isigma(z_irho, z_isigma);
// make sure we have the right ghost zone!
assert(true_zgz.is_in_ghost_zone(true_z_iperp, true_z_ipar));
return synchronize_Jacobian(true_zgz,
true_z_iperp, true_z_ipar,
y_iperp,
y_posn, min_ym, max_ym,
Jacobian_buffer);
}
else if (xgz.is_interpatch())
then
{
// ghost zone is interpatch
// ==> x value was computed by a phase 2 interpatch interpolation
// - using (only) nominal-grid data
// ==> overall Jacobian(x,y) = ghost zone Jacobian(x,y)
// - using a mixture of nominal-grid data
// and data computed by a phase 1 symmetry operation
// ==> overall Jacobian(x,y) = "fold" ghost zone Jacobian(x,y)
// to take the phase 1 symmetry
// operation into account
//
// For example,
// |
// xp xe|ye a yp
// | b
// | xc
// ----------xse-----------+---d-------yse----------
// | e
// |
// here point x is computed by interpatch-interpolating in the
// par direction from the 5 y points abcde. e is outside the
// nominal grid, so its Jacobian must be "folded" over to c.
// Notice that this "folding" must be done about the edge yse,
// *not* about ye itself.
// Jacobian of the phase 2 interpatch interpolation
const patch_edge &ye = ghost_zone_Jacobian(xgz,
x_iperp, x_ipar,
y_iperp,
y_posn, min_ym, max_ym,
Jacobian_buffer);
const int min_y_ipar = y_posn + min_ym;
const int max_y_ipar = y_posn + max_ym;
// fold any points in the Jacobian outside the nominal grid
if (ye.ipar_is_in_min_ipar_corner(min_y_ipar))
then
{
fold_Jacobian(ye, ye.min_par_adjacent_edge(),
y_iperp,
y_posn, min_ym, max_ym,
min_ym, ye.min_ipar_corner__max_ipar() - y_posn,
Jacobian_buffer);
min_ym = ye.min_ipar_without_corners() - y_posn;
}
if (ye.ipar_is_in_max_ipar_corner(max_y_ipar))
then
{
fold_Jacobian(ye, ye.max_par_adjacent_edge(),
y_iperp,
y_posn, min_ym, max_ym,
ye.max_ipar_corner__min_ipar() - y_posn, max_ym,
Jacobian_buffer);
max_ym = ye.max_ipar_without_corners() - y_posn;
}
return ye;
}
else
error_exit(PANIC_EXIT,
"***** patch_system::synchronize_Jacobian():\n"
" don't know what to do with ghost zone (this should never happen)!\n"
" xgz.my_patch()=\"%s\" xe=xgz.my_edge()=\"%s\"\n"
" xgz.other_patch()=\"%s\" xgz.other_edge()=\"%s\"\n"
" xgz.is_symmetry()=(int)%d xgz.is_interpatch()=(int)%d\n"
" x_iperp=%d x_ipar=%d\n"
" xe.ipar_is_in_{min,max}_ipar_corner(x_ipar)=(int){%d,%d}\n"
" xe.ipar_is_in_{corner,noncorner}(x_ipar)=(int){%d,%d}\n",
xgz.my_patch().name(), xe.name(),
xgz.other_patch().name(), xgz.other_edge().name(),
int(xgz.is_symmetry()), int(xgz.is_interpatch()),
x_iperp, x_ipar,
xe.ipar_is_in_min_ipar_corner(x_ipar),
xe.ipar_is_in_max_ipar_corner(x_ipar),
xe.ipar_is_in_corner(x_ipar),
xe.ipar_is_in_noncorner(x_ipar)); /*NOTREACHED*/
}
//******************************************************************************
//
// This function "folds" part of a(n interpatch) Jacobian row to take
// a symmetry operation into account. For example:
// |
// |e_Jac
// | p
// | a
// | b
// | c=y
// ---------+---d-------e_fold-------
// | e=x sgz_fold
// |
// Here the Jacobian abcde is to be "folded", because e is outside the
// nominal grid (its Jacobian must be "folded" over to c).
//
// Notice that the folding (about the edge e_fold) is in the par direction
// with respect to e_Jac, but the perp direction with respect to e_fold.
// Since e_fold and e_Jac are adjacent edges,
// e_Jac (iperp,ipar) == e_fold (ipar,iperp)
//
// Arguments:
// e_Jac = edge which the Jacobian lies along
// e_fold = edge about which to fold; the corresponding ghost zone must be
// symmetry ghost zone, and at present we only support the case
// where this is a "local" (mirror-image) symmetry ghost zone
// iperp = iperp-wrt-e_Jac coordinate of Jacobian
// posn = ipar-wrt-e_Jac coordinate of Jacobian molecule reference point
// [min,max]_m = range of ipar-wrt-e_Jac molecule m in Jacobian
// [min,max]_fold_m = range of ipar-wrt-e_Jac molecule m which is to folded;
// this must be a subrange of [min,max]_m
//
void patch_system::fold_Jacobian(const patch_edge &e_Jac,
const patch_edge &e_fold,
int iperp,
int posn, int min_m, int max_m,
int min_fold_m, int max_fold_m,
jtutil::array1d<fp> &Jacobian_buffer)
const
{
// check that [min,max]_fold_m is a subrange of [min,max]_m
assert(min_fold_m >= min_m);
assert(min_fold_m <= max_m);
assert(max_fold_m >= min_m);
assert(max_fold_m <= max_m);
const patch &p = e_fold.my_patch();
assert(e_Jac.my_patch() == p);
const symmetry_ghost_zone &sgz_fold = p.ghost_zone_on_edge(e_fold)
.cast_to_symmetry_ghost_zone();
//
// At present we only handle the case show in the comments above,
// where sgz_fold is a local (mirror-image) symmetry, i.e. where
// y is guaranteed to be within the molecule abcde.
//
if (sgz_fold.other_edge() != e_fold)
then error_exit(ERROR_EXIT,
"***** patch_system::fold_Jacobian()\n"
" implementation restriction: at present we only handle folding\n"
" via \"local\" (mirror-image) symmetries!\n"
" p=\"%s\" e_Jac=\"%s\" e_fold=\"%s\"\n"
" but sgz_fold.other_edge()=\"%s\" != e_fold\n",
p.name(), e_Jac.name(), e_fold.name(),
sgz_fold.other_edge().name()); /*NOTREACHED*/
for (int xm = min_fold_m; xm <= max_fold_m; ++xm)
{
const int x_Jac_ipar = posn + xm; // x ipar wrt e_Jac
const int x_fold_iperp = x_Jac_ipar; // ... == iperp wrt e_fold
const int y_fold_iperp = sgz_fold.iperp_map_of_iperp(x_fold_iperp);
// y iperp wrt e_fold
const int y_Jac_ipar = y_fold_iperp; // ... == ipar wrt e_Jac
const int ym = y_Jac_ipar - posn;
// check that y is indeed within the molecule
assert(ym >= min_m);
assert(ym <= max_m);
// actually "fold" the molecule
Jacobian_buffer(ym) += Jacobian_buffer(xm);
}
}
//******************************************************************************
//
// Given that compute_synchronize_Jacobian() has been called, this
// function computes a single row of the Jacobian of a given ghost zone,
// *not* taking into account synchronize()'s 3-phase algorithm:
// - It returns the edge to which the y point belongs (the caller can get
// the patch from this edge).
// - It stores y_iperp and y_posn and min/max ym in the named arguments.
// - It stores the Jacobian elements
// partial synchronize() gridfn(ghosted_gfn, px, x_iperp, x_ipar)
// -------------------------------------------------------------
// partial gridfn(ghosted_gfn, py, y_iperp, y_posn+ym)
// in the caller-supplied buffer
// Jacobian_buffer(ym)
// for each ym in the min/max ym range
//
const patch_edge &
patch_system::ghost_zone_Jacobian(const ghost_zone &xgz,
int x_iperp, int x_ipar,
int &y_iperp,
int &y_posn, int &min_ym, int &max_ym,
jtutil::array1d<fp> &Jacobian_buffer)
const
{
y_iperp = xgz.Jacobian_y_iperp(x_iperp);
y_posn = xgz.Jacobian_y_ipar_posn(x_iperp, x_ipar);
min_ym = xgz.Jacobian_min_y_ipar_m();
max_ym = xgz.Jacobian_max_y_ipar_m();
for (int ym = min_ym; ym <= max_ym; ++ym)
{
Jacobian_buffer(ym) = xgz.Jacobian(x_iperp, x_ipar, ym);
}
return xgz.other_edge();
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
} // namespace AHFinderDirect