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

605 lines
24 KiB
C

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <limits.h>
#include <math.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;
//******************************************************************************
//******************************************************************************
//******************************************************************************
//
// These functions verify (assert()) that a ghost zone is indeed of
// the specified type, then static_cast to the appropriate derived class.
//
const symmetry_ghost_zone &ghost_zone::cast_to_symmetry_ghost_zone()
const
{
assert(is_symmetry());
return static_cast<const symmetry_ghost_zone &>(*this);
}
symmetry_ghost_zone &ghost_zone::cast_to_symmetry_ghost_zone()
{
assert(is_symmetry());
return static_cast<symmetry_ghost_zone &>(*this);
}
//**************************************
const interpatch_ghost_zone &ghost_zone::cast_to_interpatch_ghost_zone()
const
{
assert(is_interpatch());
return static_cast<const interpatch_ghost_zone &>(*this);
}
interpatch_ghost_zone &ghost_zone::cast_to_interpatch_ghost_zone()
{
assert(is_interpatch());
return static_cast<interpatch_ghost_zone &>(*this);
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
//
// This function constructs a mirror-symmetry ghost zone object
//
symmetry_ghost_zone::symmetry_ghost_zone(const patch_edge &my_edge_in)
: ghost_zone(my_edge_in,
my_edge_in, // other edge == my edge
ghost_zone_is_symmetry)
{
// iperp_map: i --> (i of ghost zone) - i
iperp_map_ = new jtutil::cpm_map<fp>(min_iperp(), max_iperp(),
my_edge_in.fp_grid_outer_iperp());
// ipar_map_: identity map
ipar_map_ = new jtutil::cpm_map<fp>(extreme_min_ipar(), extreme_max_ipar());
}
//******************************************************************************
//
// This function constructs a periodic-symmetry ghost zone object.
//
symmetry_ghost_zone::symmetry_ghost_zone(const patch_edge &my_edge_in, const patch_edge &other_edge_in,
int my_edge_sample_ipar, int other_edge_sample_ipar,
bool ipar_map_is_plus)
: ghost_zone(my_edge_in,
other_edge_in,
ghost_zone_is_symmetry)
{
//
// perpendicular map
//
const fp fp_my_period_plane_iperp = my_edge().fp_grid_outer_iperp();
const fp fp_other_period_plane_iperp = other_edge().fp_grid_outer_iperp();
// iperp mapping must be outside --> inside
// i.e. if both edges have iperp as the same min/max "direction",
// then the mapping is iperp increasing --> iperp decreasing
// (i.e. the map's sign is -1)
const bool is_iperp_map_plus = !(my_edge().is_min() == other_edge().is_min());
iperp_map_ = new jtutil::cpm_map<fp>(min_iperp(), max_iperp(),
fp_my_period_plane_iperp,
fp_other_period_plane_iperp,
is_iperp_map_plus);
//
// parallel map
//
ipar_map_ = new jtutil::cpm_map<fp>(extreme_min_ipar(), extreme_max_ipar(),
my_edge_sample_ipar, other_edge_sample_ipar,
ipar_map_is_plus);
}
//******************************************************************************
//
// This function destroys a symmetry_ghost_zone object.
//
symmetry_ghost_zone::~symmetry_ghost_zone()
{
delete ipar_map_;
delete iperp_map_;
}
//******************************************************************************
//
// This function "synchronizes" a ghost zone, i.e. it updates the
// ghost-zone values of the specified gridfns via the appropriate
// symmetry operations.The flags specify which part(s) of the ghost zone
// we want.
//
void symmetry_ghost_zone::synchronize(int ghosted_min_gfn, int ghosted_max_gfn,
bool want_corners /* = true */,
bool want_noncorner /* = true */)
{
// printf("*Sync sym ghost zone in %s patch\n", my_patch().name());
for (int gfn = ghosted_min_gfn; gfn <= ghosted_max_gfn; ++gfn)
{
for (int iperp = min_iperp(); iperp <= max_iperp(); ++iperp)
{
for (int ipar = min_ipar(iperp); ipar <= max_ipar(iperp); ++ipar)
{
// do we want to do this point?
if (!my_edge().ipar_is_in_selected_part(want_corners, want_noncorner,
ipar))
then continue; // *** LOOP CONTROL ***
const int sym_iperp = iperp_map_of_iperp(iperp);
const int sym_ipar = ipar_map_of_ipar(ipar);
const int sym_irho = other_edge()
.irho_of_iperp_ipar(sym_iperp, sym_ipar);
const int sym_isigma = other_edge()
.isigma_of_iperp_ipar(sym_iperp, sym_ipar);
const fp sym_gridfn = other_patch()
.ghosted_gridfn(gfn, sym_irho, sym_isigma);
const int irho = my_edge().irho_of_iperp_ipar(iperp, ipar);
const int isigma = my_edge().isigma_of_iperp_ipar(iperp, ipar);
my_patch().ghosted_gridfn(gfn, irho, isigma) = sym_gridfn;
}
}
}
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
//
// This function constructs an interpatch_ghost_zone object.
//
interpatch_ghost_zone::interpatch_ghost_zone(const patch_edge &my_edge_in,
const patch_edge &other_edge_in,
int patch_overlap_width)
: ghost_zone(my_edge_in,
other_edge_in,
ghost_zone_is_interpatch),
// remaining pointers are all set up properly by finish_setup()
other_patch_interp_(NULL),
other_iperp_(NULL),
min_ipar_used_(NULL), max_ipar_used_(NULL),
other_par_(NULL),
interp_result_buffer_(NULL),
Jacobian_y_ipar_posn_(NULL), Jacobian_buffer_(NULL) // no comma
{
//
// verify that we have the expected relationships between
// this and the other patch's (mu,nu,phi) coordinates:
//
// perp coordinate is common to us and the other patch, so
// ghost zone must be min in one patch, max in the other
if (my_edge().is_min() == other_edge().is_min())
then error_exit(ERROR_EXIT,
"***** interpatch_ghost_zone::interpatch_ghost_zone:\n"
" my_patch().name()=\"%s\" my_edge().name()=%s\n"
" other_patch().name()=\"%s\" other_edge().name()=%s\n"
" ghost zone must be min in one patch, max in the other!\n",
my_patch().name(), my_edge().name(),
other_patch().name(), other_edge().name()); /*NOTREACHED*/
// coord in common between the two patches must be perp coord in both patches
// and this patch's tau coordinate must be other edge's parallel coordinate
const local_coords::coords_set common_coords_set = local_coords::coords_set_not(my_patch().coords_set_rho_sigma() ^
other_patch().coords_set_rho_sigma());
if (!((common_coords_set == my_edge().coords_set_perp()) && (common_coords_set == other_edge().coords_set_perp()) && (my_patch().coords_set_tau() == other_edge().coords_set_par())))
then error_exit(PANIC_EXIT,
"***** interpatch_ghost_zone::interpatch_ghost_zone:\n"
" (rho,sigma,tau) coordinates don't match up properly\n"
" between this patch/edge and the other patch/edge!\n"
" my_patch().name()=\"%s\" my_edge().name()=%s\n"
" other_patch().name()=\"%s\" other_edge().name()=%s\n"
" my_patch().coords_set_{rho,sigma,tau}={%s,%s,%s}\n"
" my_edge().coords_set_{perp,par}={%s,%s}\n"
" other_patch().coords_set_{rho,sigma,tau}={%s,%s,%s}\n"
" other_edge().coords_set_{perp,par}={%s,%s}\n",
my_patch().name(), my_edge().name(),
other_patch().name(), other_edge().name(),
local_coords::name_of_coords_set(my_patch().coords_set_rho()),
local_coords::name_of_coords_set(my_patch().coords_set_sigma()),
local_coords::name_of_coords_set(my_patch().coords_set_tau()),
local_coords::name_of_coords_set(my_edge().coords_set_perp()),
local_coords::name_of_coords_set(my_edge().coords_set_par()),
local_coords::name_of_coords_set(other_patch().coords_set_rho()),
local_coords::name_of_coords_set(other_patch().coords_set_sigma()),
local_coords::name_of_coords_set(other_patch().coords_set_tau()),
local_coords::name_of_coords_set(other_edge().coords_set_perp()),
local_coords::name_of_coords_set(other_edge().coords_set_par()));
/*NOTREACHED*/
// perp coordinate must match (mod 2*pi) across the two patches
// after taking into account any 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
const fp other_overlap = (patch_overlap_width - 1) * other_edge().perp_map().delta_fp();
const fp other_outer_perp_minus_overlap // move back inwards into other patch
// by overlap distance, to get a value
// that should match our own
// grid_outer_perp() value
= other_edge().grid_outer_perp() + (other_edge().is_min() ? +other_overlap : -other_overlap);
if (!local_coords::fuzzy_EQ_ang(my_edge().grid_outer_perp(),
other_outer_perp_minus_overlap))
then error_exit(ERROR_EXIT,
"***** interpatch_ghost_zone::interpatch_ghost_zone:\n"
" my_patch().name()=\"%s\" my_edge().name()=%s\n"
" other_patch().name()=\"%s\" other_edge().name()=%s\n"
" perp coordinate doesn't match (mod 2*pi) across the two patches!\n"
" my_edge().grid_outer_perp()=%g <--(compare this)\n"
" patch_overlap_width=%d other_overlap=%g\n"
" other_edge.grid_outer_perp()=%g\n"
" other_outer_perp_minus_overlap=%g <--(against this)\n",
my_patch().name(), my_edge().name(),
other_patch().name(), other_edge().name(),
double(my_edge().grid_outer_perp()),
patch_overlap_width, double(other_overlap),
double(other_edge().grid_outer_perp()),
double(other_outer_perp_minus_overlap)); /*NOTREACHED*/
//
// set up the iperp interpatch coordinate mapping
// (gives other patch's iperp coordinate for interpolation)
//
// compute the iperp --> other_iperp mapping for a sample point;
// ... if the ghost zone is empty, then the sample point will necessarily
// be out-of-range in the ghost zone, so we use the *unchecked*
// conversions to avoid errors in this case
// ... we do the computation using the fact that perp is the same
// coordinate in both patches (modulo 2*pi radians = 360 degrees)
const int sample_iperp = outer_iperp();
const fp sample_perp = my_edge().perp_map().fp_of_int_unchecked(sample_iperp);
// unchecked conversion here!
const fp other_sample_perp = other_patch()
.modulo_reduce_ang(other_edge().perp_is_rho(),
sample_perp);
const fp fp_other_sample_iperp = other_edge()
.fp_iperp_of_perp(other_sample_perp);
// verify that this is fuzzily a grid point
if (!jtutil::fuzzy<fp>::is_integer(fp_other_sample_iperp))
then error_exit(ERROR_EXIT,
"***** interpatch_ghost_zone::interpatch_ghost_zone:\n"
" my_patch().name()=\"%s\" my_edge().name()=%s\n"
" other_patch().name()=\"%s\" other_edge().name()=%s\n"
" sample_iperp=%d sample_perp=%g\n"
" other_sample_perp=%g fp_other_sample_iperp=%g\n"
" ==> fp_other_sample_iperp isn't fuzzily an integer!\n"
" ==> patches aren't commensurate in the perpendicular coordinate!\n",
my_patch().name(), my_edge().name(),
other_patch().name(), other_edge().name(),
sample_iperp, double(sample_perp),
double(other_sample_perp),
double(fp_other_sample_iperp)); /*NOTREACHED*/
const int other_sample_iperp = jtutil::round<fp>::to_integer(fp_other_sample_iperp);
// compute the +/- sign (direction) of the iperp --> other_iperp mapping
//
// Since perp is the same in both patches (mod 2*pi radians = 360 degrees),
// the overall +/- sign is just the product of the signs of the two individual
// iperp <--> perp mappings.
//
// ... signs encoded as (floating-point) +/- 1.0
const double iperp_map_sign_pm1 = jtutil::signum(my_edge().perp_map().delta_fp()) * jtutil::signum(other_edge().perp_map().delta_fp());
// ... signs encoded as is_plus bool flag
const bool is_iperp_map_plus = (iperp_map_sign_pm1 > 0.0);
// now we finally know enough to set up the other_iperp(iperp)
// coordinate mapping
other_iperp_ = new jtutil::cpm_map<fp>(min_iperp(), max_iperp(),
sample_iperp, other_sample_iperp,
is_iperp_map_plus);
}
//******************************************************************************
//
// this function destroys an interpatch_ghost_zone object.
//
interpatch_ghost_zone::~interpatch_ghost_zone()
{
delete Jacobian_buffer_;
delete Jacobian_y_ipar_posn_;
delete interp_result_buffer_;
delete other_par_;
delete max_ipar_used_;
delete min_ipar_used_;
delete other_iperp_;
delete other_patch_interp_;
}
//******************************************************************************
//
// These functions compute the [min,max] ipar of the ghost zone for
// a given iperp, taking into account how we treat the corners
// (cf. the example in the header comments in "ghost_zone.hh"):
//
// If an adjacent ghost zone is symmetry,
// we do not include that corner;
// If an adjacent ghost zone is interpatch,
// we include up to the diagonal line, and if we are a rho ghost zone,
// then also the diagonal line itself. E.g. For the example in the
// header comments "ghost_zone.hh", the +x ghost zone includes (6,6),
// (7,6), and (7,7), while the +y ghost zone includes (6,7)
//
// ... in the following 2 functions,
// the iabs() term includes the diagonal,
// so we must remove the diagonal for !is_rho,
// i.e. add 1 to min_ipar and subtract 1 from max_ipar
//
int interpatch_ghost_zone::min_ipar(int iperp) const
{
return min_par_adjacent_ghost_zone().is_symmetry()
? my_edge().min_ipar_without_corners()
: my_edge().min_ipar_without_corners() - iabs(iperp - my_edge().nominal_grid_outer_iperp()) + (is_rho() ? 0 : 1);
}
int interpatch_ghost_zone::max_ipar(int iperp) const
{
return max_par_adjacent_ghost_zone().is_symmetry()
? my_edge().max_ipar_without_corners()
: my_edge().max_ipar_without_corners() + iabs(iperp - my_edge().nominal_grid_outer_iperp()) - (is_rho() ? 0 : 1);
}
//******************************************************************************
//
// This function finishes the construction/setup of an interpatch_ghost_zone
// object. It
// - sets up the par coordinate mapping information
// - sets up the interpatch interpolator data pointer and result arrays
// - constructs the patch_interp object to interpolate from the *other* patch
//
// We use our ipar as the patch_interp's parindex.
//
void interpatch_ghost_zone::finish_setup(int interp_handle,
int interp_par_table_handle)
{
min_other_iperp_ = min(other_iperp(min_iperp()),
other_iperp(max_iperp()));
max_other_iperp_ = max(other_iperp(min_iperp()),
other_iperp(max_iperp()));
//
// set up arrays giving actual [min,max] ipar that we'll use
// at each other_iperp (later on we will pass these arrays to the
// other patch's patch_interp object, with ipar being parindex there
//
min_ipar_used_ = new jtutil::array1d<int>(min_other_iperp_, max_other_iperp_);
max_ipar_used_ = new jtutil::array1d<int>(min_other_iperp_, max_other_iperp_);
{
for (int iperp = min_iperp(); iperp <= max_iperp(); ++iperp)
{
(*min_ipar_used_)(other_iperp(iperp)) = min_ipar(iperp);
(*max_ipar_used_)(other_iperp(iperp)) = max_ipar(iperp);
}
}
//
// set up array giving other patch's par coordinate for interpolation
//
other_par_ = new jtutil::array2d<fp>(min_other_iperp_, max_other_iperp_,
extreme_min_ipar(), extreme_max_ipar());
{
for (int iperp = min_iperp(); iperp <= max_iperp(); ++iperp)
{
for (int ipar = min_ipar(iperp); ipar <= max_ipar(iperp); ++ipar)
{
// compute the other_par corresponding to (iperp,ipar)
// ... here we use the fact (which we verified in our constructor)
// that other edge's parallel coordinate == our tau coordinate
// (at least modulo 2*pi radians = 360 degrees)
const fp perp = my_edge().perp_of_iperp(iperp);
const fp par = my_edge().par_of_ipar(ipar);
const fp rho = my_edge().rho_of_perp_par(perp, par);
const fp sigma = my_edge().sigma_of_perp_par(perp, par);
const fp tau = my_patch().tau_of_rho_sigma(rho, sigma);
const fp other_par = other_patch()
.modulo_reduce_ang(other_edge().par_is_rho(), tau);
(*other_par_)(other_iperp(iperp), ipar) = other_par;
}
}
}
//
// set up interpolation result buffer
//
interp_result_buffer_ = new jtutil::array3d<fp>(my_patch().ghosted_min_gfn(),
my_patch().ghosted_max_gfn(),
min_other_iperp_, max_other_iperp_,
extreme_min_ipar(), extreme_max_ipar());
//
// construct the patch_interp object to interpolate from the *other* patch
// ... the patch_interp should use gridfn data from it's (the other patch's)
// min/max par ghost zones if those (adjacent) adjacent ghost zones
// are symmetry, but not if they're interpatch,
// cf the header comments in "ghost_zone.hh"
//
const ghost_zone &other_ghost_zone = other_patch()
.ghost_zone_on_edge(other_edge());
const bool ok_to_use_min_par_ghost_zone = other_ghost_zone.min_par_adjacent_ghost_zone()
.is_symmetry()
? true
: false;
const bool ok_to_use_max_par_ghost_zone = other_ghost_zone.max_par_adjacent_ghost_zone()
.is_symmetry()
? true
: false;
other_patch_interp_ = new patch_interp(other_edge(),
min_other_iperp_, max_other_iperp_,
*min_ipar_used_, *max_ipar_used_,
*other_par_,
ok_to_use_min_par_ghost_zone,
ok_to_use_max_par_ghost_zone,
interp_handle, interp_par_table_handle);
}
//******************************************************************************
//
// This function asserts() that
// - we have a patch_interp object
// - our and the patch_interp object's notions of the "other patch" agree
// - the other patch has an interpatch ghost zone on this edge
// - the other patch's interpatch ghost zone on this edge,
// points back to our patch
//
void interpatch_ghost_zone::assert_fully_setup() const
{
assert(other_patch_interp_ != NULL);
assert(other_patch() == other_patch_interp_->my_patch());
assert(other_patch()
.ghost_zone_on_edge(other_edge())
.is_interpatch());
assert(my_patch() == other_patch()
.ghost_zone_on_edge(other_edge())
.other_patch());
}
//******************************************************************************
//
// This function "synchronizes" a ghost zone, i.e. it updates the
// ghost-zone values of the specified gridfns via the appropriate
// interpatch interpolations.
//
// The flags specify which part(s) of the ghost zone we want, but
// the present implementation only supports the case where all the
// flags are true , i.e. we want the entire ghost zone.
//
void interpatch_ghost_zone::synchronize(int ghosted_min_gfn, int ghosted_max_gfn,
bool want_corners /* = true */,
bool want_noncorner /* = true */)
{
#ifdef DEBUG_AHFD
printf("*Sync interpatch ghost zone in %s\n", my_patch().name());
#endif
// make sure the caller wants the entire ghost zone
if (!(want_corners && want_noncorner))
then error_exit(ERROR_EXIT,
"***** interpatch_ghost_zone::synchronize():\n"
" we only support operating on the *entire* ghost zone,\n"
" but we were passed flags specifying a proper subset!\n"
" want_corners=(int)%d want_noncorner=(int)%d\n",
want_corners, want_noncorner); /*NOTREACHED*/
//
// move from 'Compute_Jacobian' below
//
assert(other_patch_interp_ != NULL);
other_patch_interp_->molecule_minmax_ipar_m(Jacobian_min_y_ipar_m_,
Jacobian_max_y_ipar_m_);
#ifdef DEBUG_AHFD
printf("%d %d %d %d %d %d \n", Jacobian_min_y_ipar_m_, Jacobian_max_y_ipar_m_,
min_other_iperp_, max_other_iperp_, extreme_min_ipar(), extreme_max_ipar());
getchar();
#endif
// /*
if (Jacobian_y_ipar_posn_ == NULL)
Jacobian_y_ipar_posn_ = new jtutil::array2d<CCTK_INT>(min_other_iperp_, max_other_iperp_,
extreme_min_ipar(), extreme_max_ipar());
if (Jacobian_buffer_ == NULL)
Jacobian_buffer_ = new jtutil::array3d<fp>(min_other_iperp_, max_other_iperp_,
extreme_min_ipar(), extreme_max_ipar(),
Jacobian_min_y_ipar_m_, Jacobian_max_y_ipar_m_);
// do the interpolation into our result buffer
other_patch_interp_->interpolate(ghosted_min_gfn, ghosted_max_gfn,
*interp_result_buffer_, //);
*Jacobian_y_ipar_posn_,
*Jacobian_buffer_);
// other_patch_interp_->molecule_posn(*Jacobian_y_ipar_posn_);
// store the results back into our gridfns
for (int gfn = ghosted_min_gfn; gfn <= ghosted_max_gfn; ++gfn)
{
for (int iperp = min_iperp(); iperp <= max_iperp(); ++iperp)
{
const int oiperp = other_iperp(iperp);
for (int ipar = min_ipar(iperp); ipar <= max_ipar(iperp); ++ipar)
{
int irho = my_edge().irho_of_iperp_ipar(iperp, ipar);
int isigma = my_edge().isigma_of_iperp_ipar(iperp, ipar);
my_patch().ghosted_gridfn(gfn, irho, isigma) = (*interp_result_buffer_)(gfn, oiperp, ipar);
}
}
}
}
//******************************************************************************
//
// This function allocates the internal buffers for the Jacobian, and
// computes that Jacobian
// partial synchronize gridfn(ghosted_gfn, iperp, ipar)
// ------------------------------------------------------------
// partial other patch gridfn(ghosted_gfn, oiperp, posn+ipar_m)
// where
// oiperp = Jacobian_oiperp(iperp)
// posn = Jacobian_oipar_posn(iperp, ipar)
// into the internal buffers.
//
void interpatch_ghost_zone::compute_Jacobian(int ghosted_min_gfn, int ghosted_max_gfn,
bool want_corners /* = true */,
bool want_noncorner /* = true */)
const
{
// make sure the caller wants the entire ghost zone
if (!(want_corners && want_noncorner))
then error_exit(ERROR_EXIT,
"***** interpatch_ghost_zone::compute_Jacobian():\n"
" we only support operating on the *entire* ghost zone,\n"
" but we were passed flags specifying a proper subset!\n"
" want_corners=(int)%d want_noncorner=(int)%d\n",
want_corners, want_noncorner); /*NOTREACHED*/
}
//******************************************************************************
//******************************************************************************
//******************************************************************************
} // namespace AHFinderDirect