387 lines
13 KiB
C
387 lines
13 KiB
C
|
|
|
|
#include "macrodef.h"
|
|
#ifdef With_AHF
|
|
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
|
|
#include "util_Table.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_system.h"
|
|
|
|
#include "Jacobian.h"
|
|
|
|
#include "gfns.h"
|
|
#include "gr.h"
|
|
|
|
namespace AHFinderDirect
|
|
{
|
|
using jtutil::error_exit;
|
|
|
|
namespace
|
|
{
|
|
|
|
void expansion_Jacobian_partial_SD(patch_system &ps, Jacobian &Jac,
|
|
bool print_msg_flag);
|
|
|
|
void add_ghost_zone_Jacobian(const patch_system &ps,
|
|
Jacobian &Jac,
|
|
fp mol,
|
|
const patch &xp, const ghost_zone &xmgz,
|
|
int x_II,
|
|
int xm_irho, int xm_isigma);
|
|
|
|
enum expansion_status
|
|
expansion_Jacobian_dr_FD(patch_system *ps_ptr, Jacobian *Jac_ptr, fp add_to_expansion,
|
|
bool initial_flag,
|
|
bool print_msg_flag);
|
|
}
|
|
|
|
//******************************************************************************
|
|
|
|
//
|
|
// If ps_ptr != NULL and Jac_ptr != NULL, this function computes the
|
|
// Jacobian matrix J[Theta(h)] of the expansion Theta(h). We assume
|
|
// that Theta(h) has already been computed.
|
|
//
|
|
// If ps_ptr == NULL and Jac_ptr == NULL, this function does a dummy
|
|
// computation, in which only any expansion() (and hence geometry
|
|
// interpolator) calls are done, these with the number of interpolation
|
|
// points set to 0 and all the output array pointers set to NULL.
|
|
//
|
|
// It's illegal for one but not both of ps_ptr and Jac_ptr to be NULL.
|
|
//
|
|
// Arguments:
|
|
// ps_ptr --> The patch system, or == NULL to do (only) a dummy computation.
|
|
// Jac_ptr --> The Jacobian, or == NULL to do (only) a dummy computation.
|
|
// add_to_expansion = A real number to add to the expansion.
|
|
//
|
|
// Results:
|
|
// This function returns a status code indicating whether the computation
|
|
// succeeded or failed, and if the latter, what caused the failure.
|
|
//
|
|
enum expansion_status
|
|
expansion_Jacobian(patch_system *ps_ptr, Jacobian *Jac_ptr,
|
|
fp add_to_expansion,
|
|
bool initial_flag,
|
|
bool print_msg_flag /* = false */)
|
|
{
|
|
const bool active_flag = (ps_ptr != NULL) && (Jac_ptr != NULL);
|
|
enum expansion_status status;
|
|
|
|
if (active_flag)
|
|
then expansion_Jacobian_partial_SD(*ps_ptr, *Jac_ptr,
|
|
print_msg_flag);
|
|
// this function looks at ps_ptr and Jac_ptr (non-NULL vs NULL)
|
|
// to choose a normal vs dummy computation
|
|
{
|
|
status = expansion_Jacobian_dr_FD(ps_ptr, Jac_ptr, add_to_expansion,
|
|
initial_flag,
|
|
print_msg_flag);
|
|
if (status != expansion_success)
|
|
then return status; // *** ERROR RETURN ***
|
|
}
|
|
|
|
return expansion_success; // *** NORMAL RETURN ***
|
|
}
|
|
//
|
|
// This function computes the partial derivative terms in the Jacobian
|
|
// matrix of the expansion Theta(h), by symbolic differentiation from
|
|
// the Jacobian coefficient (angular) gridfns. The Jacobian is traversed
|
|
// by rows, using equation (25) of my 1996 apparent horizon finding paper.
|
|
//
|
|
// Inputs (angular gridfns, on ghosted grid):
|
|
// h # shape of trial surface
|
|
// Theta # Theta(h) assumed to already be computed
|
|
// partial_Theta_wrt_partial_d_h # Jacobian coefficients
|
|
// partial_Theta_wrt_partial_dd_h # (also assumed to already be computed)
|
|
//
|
|
// Outputs:
|
|
// The Jacobian matrix is stored in the Jacobian object Jac.
|
|
//
|
|
namespace
|
|
{
|
|
void expansion_Jacobian_partial_SD(patch_system &ps, Jacobian &Jac,
|
|
bool print_msg_flag)
|
|
{
|
|
Jac.zero_matrix();
|
|
ps.compute_synchronize_Jacobian();
|
|
|
|
for (int xpn = 0; xpn < ps.N_patches(); ++xpn)
|
|
{
|
|
patch &xp = ps.ith_patch(xpn);
|
|
|
|
for (int x_irho = xp.min_irho(); x_irho <= xp.max_irho(); ++x_irho)
|
|
{
|
|
for (int x_isigma = xp.min_isigma(); x_isigma <= xp.max_isigma(); ++x_isigma)
|
|
{
|
|
//
|
|
// compute the main Jacobian terms for this grid point, i.e.
|
|
// partial Theta(this point x, Jacobian row II)
|
|
// ---------------------------------------------
|
|
// partial h(other points y, Jacobian column JJ)
|
|
//
|
|
|
|
// Jacobian row index
|
|
const int II = ps.gpn_of_patch_irho_isigma(xp, x_irho, x_isigma);
|
|
|
|
// Jacobian coefficients for this point
|
|
const fp Jacobian_coeff_rho = xp.gridfn(gfns::gfn__partial_Theta_wrt_partial_d_h_1,
|
|
x_irho, x_isigma);
|
|
const fp Jacobian_coeff_sigma = xp.gridfn(gfns::gfn__partial_Theta_wrt_partial_d_h_2,
|
|
x_irho, x_isigma);
|
|
const fp Jacobian_coeff_rho_rho = xp.gridfn(gfns::gfn__partial_Theta_wrt_partial_dd_h_11,
|
|
x_irho, x_isigma);
|
|
const fp Jacobian_coeff_rho_sigma = xp.gridfn(gfns::gfn__partial_Theta_wrt_partial_dd_h_12,
|
|
x_irho, x_isigma);
|
|
const fp Jacobian_coeff_sigma_sigma = xp.gridfn(gfns::gfn__partial_Theta_wrt_partial_dd_h_22,
|
|
x_irho, x_isigma);
|
|
|
|
// partial_rho, partial_rho_rho
|
|
{
|
|
for (int m_irho = xp.molecule_min_m();
|
|
m_irho <= xp.molecule_max_m();
|
|
++m_irho)
|
|
{
|
|
const int xm_irho = x_irho + m_irho;
|
|
const fp Jac_rho = Jacobian_coeff_rho * xp.partial_rho_coeff(m_irho);
|
|
const fp Jac_rho_rho = Jacobian_coeff_rho_rho * xp.partial_rho_rho_coeff(m_irho);
|
|
const fp Jac_sum = Jac_rho + Jac_rho_rho;
|
|
if (xp.is_in_nominal_grid(xm_irho, x_isigma))
|
|
then
|
|
{
|
|
const int xm_JJ = Jac.II_of_patch_irho_isigma(xp, xm_irho, x_isigma);
|
|
Jac.sum_into_element(II, xm_JJ, Jac_sum);
|
|
}
|
|
else
|
|
add_ghost_zone_Jacobian(ps, Jac,
|
|
Jac_sum,
|
|
xp, xp.minmax_rho_ghost_zone(m_irho < 0),
|
|
II, xm_irho, x_isigma);
|
|
}
|
|
}
|
|
|
|
// partial_sigma, partial_sigma_sigma
|
|
{
|
|
for (int m_isigma = xp.molecule_min_m();
|
|
m_isigma <= xp.molecule_max_m();
|
|
++m_isigma)
|
|
{
|
|
const int xm_isigma = x_isigma + m_isigma;
|
|
const fp Jac_sigma = Jacobian_coeff_sigma * xp.partial_sigma_coeff(m_isigma);
|
|
const fp Jac_sigma_sigma = Jacobian_coeff_sigma_sigma * xp.partial_sigma_sigma_coeff(m_isigma);
|
|
const fp Jac_sum = Jac_sigma + Jac_sigma_sigma;
|
|
if (xp.is_in_nominal_grid(x_irho, xm_isigma))
|
|
then
|
|
{
|
|
const int xm_JJ = Jac.II_of_patch_irho_isigma(xp, x_irho, xm_isigma);
|
|
Jac.sum_into_element(II, xm_JJ, Jac_sum);
|
|
}
|
|
else
|
|
add_ghost_zone_Jacobian(ps, Jac,
|
|
Jac_sum,
|
|
xp, xp.minmax_sigma_ghost_zone(m_isigma < 0),
|
|
II, x_irho, xm_isigma);
|
|
}
|
|
}
|
|
|
|
// partial_rho_sigma
|
|
{
|
|
for (int m_irho = xp.molecule_min_m();
|
|
m_irho <= xp.molecule_max_m();
|
|
++m_irho)
|
|
{
|
|
for (int m_isigma = xp.molecule_min_m();
|
|
m_isigma <= xp.molecule_max_m();
|
|
++m_isigma)
|
|
{
|
|
const int xm_irho = x_irho + m_irho;
|
|
const int xm_isigma = x_isigma + m_isigma;
|
|
const fp Jac_rho_sigma = Jacobian_coeff_rho_sigma * xp.partial_rho_sigma_coeff(m_irho, m_isigma);
|
|
if (xp.is_in_nominal_grid(xm_irho, xm_isigma))
|
|
then
|
|
{
|
|
const int xm_JJ = Jac.II_of_patch_irho_isigma(xp, xm_irho, xm_isigma);
|
|
Jac.sum_into_element(II, xm_JJ, Jac_rho_sigma);
|
|
}
|
|
else
|
|
{
|
|
const ghost_zone &xmgz = xp.corner_ghost_zone_containing_point(m_irho < 0, m_isigma < 0,
|
|
xm_irho, xm_isigma);
|
|
add_ghost_zone_Jacobian(ps, Jac,
|
|
Jac_rho_sigma,
|
|
xp, xmgz,
|
|
II, xm_irho, xm_isigma);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//******************************************************************************
|
|
|
|
//
|
|
// This function adds the ghost-zone Jacobian dependency contributions
|
|
// for a single ghost-zone point, to a Jacobian matrix.
|
|
//
|
|
// Arguments:
|
|
// ps = The patch system.
|
|
// Jac = (out) The Jacobian matrix.
|
|
// mol = The molecule coefficient.
|
|
// xp = The patch containing the center point of the molecule.
|
|
// xmgz = If the x+m point is in a ghost zone, this must be that ghost zone.
|
|
// If the x+m point is not in a ghost zone, this argument is ignored.
|
|
// x_II = The Jacobian row of the x point.
|
|
// xm_(irho,isigma) = The coordinates (in xp) of the x+m point of the molecule.
|
|
|
|
namespace
|
|
{
|
|
void add_ghost_zone_Jacobian(const patch_system &ps,
|
|
Jacobian &Jac,
|
|
fp mol,
|
|
const patch &xp, const ghost_zone &xmgz,
|
|
int x_II,
|
|
int xm_irho, int xm_isigma)
|
|
{
|
|
const patch_edge &xme = xmgz.my_edge();
|
|
const int xm_iperp = xme.iperp_of_irho_isigma(xm_irho, xm_isigma);
|
|
const int xm_ipar = xme.ipar_of_irho_isigma(xm_irho, xm_isigma);
|
|
|
|
// FIXME: this won't change from one call to another
|
|
// ==> it would be more efficient to reuse the same buffer
|
|
// across multiple calls on this function
|
|
int global_min_ym, global_max_ym;
|
|
ps.synchronize_Jacobian_global_minmax_ym(global_min_ym, global_max_ym);
|
|
jtutil::array1d<fp> Jacobian_buffer(global_min_ym, global_max_ym);
|
|
|
|
// on what other points y does this molecule point xm depend
|
|
// via the patch_system::synchronize() operation?
|
|
int y_iperp;
|
|
int y_posn, min_ym, max_ym;
|
|
const patch_edge &ye = ps.synchronize_Jacobian(xmgz,
|
|
xm_iperp, xm_ipar,
|
|
y_iperp,
|
|
y_posn, min_ym, max_ym,
|
|
Jacobian_buffer);
|
|
patch &yp = ye.my_patch();
|
|
|
|
// add the Jacobian contributions from the ym points
|
|
for (int ym = min_ym; ym <= max_ym; ++ym)
|
|
{
|
|
const int y_ipar = y_posn + ym;
|
|
const int y_irho = ye.irho_of_iperp_ipar(y_iperp, y_ipar);
|
|
const int y_isigma = ye.isigma_of_iperp_ipar(y_iperp, y_ipar);
|
|
const int y_JJ = Jac.II_of_patch_irho_isigma(yp, y_irho, y_isigma);
|
|
Jac.sum_into_element(x_II, y_JJ, mol * Jacobian_buffer(ym));
|
|
}
|
|
}
|
|
}
|
|
|
|
//******************************************************************************
|
|
|
|
//
|
|
// If ps_ptr != NULL and Jac_ptr != NULL, this function sums the d/dr
|
|
// terms into the Jacobian matrix of the expansion Theta(h), computing
|
|
// those terms by finite differencing.
|
|
//
|
|
// If ps_ptr == NULL and Jac_ptr == NULL, this function does a dummy
|
|
// computation, in which only any expansion() (and hence geometry
|
|
// interpolator) calls are done, these with the number of interpolation
|
|
// points set to 0 and all the output array pointers set to NULL.
|
|
//
|
|
// It's illegal for one but not both of ps_ptr and Jac_ptr to be NULL.
|
|
//
|
|
// The basic algorithm is that
|
|
// Jac += diag[ (Theta(h+epsilon) - Theta(h)) / epsilon ]
|
|
//
|
|
// Inputs (angular gridfns, on ghosted grid):
|
|
// h # shape of trial surface
|
|
// Theta # Theta(h) assumed to already be computed
|
|
//
|
|
// Outputs:
|
|
// Jac += d/dr terms
|
|
//
|
|
// Results:
|
|
// This function returns a status code indicating whether the computation
|
|
// succeeded or failed, and if the latter, what caused the failure.
|
|
//
|
|
namespace
|
|
{
|
|
enum expansion_status
|
|
expansion_Jacobian_dr_FD(patch_system *ps_ptr, Jacobian *Jac_ptr, fp add_to_expansion,
|
|
bool initial_flag,
|
|
bool print_msg_flag)
|
|
{
|
|
const bool active_flag = (ps_ptr != NULL) && (Jac_ptr != NULL);
|
|
|
|
const double epsilon = 1e-6;
|
|
// compute Theta(h+epsilon)
|
|
if (active_flag)
|
|
then
|
|
{
|
|
ps_ptr->gridfn_copy(gfns::gfn__Theta, gfns::gfn__save_Theta);
|
|
ps_ptr->add_to_ghosted_gridfn(epsilon, gfns::gfn__h);
|
|
}
|
|
const enum expansion_status status = expansion(ps_ptr, add_to_expansion,
|
|
initial_flag);
|
|
if (status != expansion_success)
|
|
then return status; // *** ERROR RETURN ***
|
|
|
|
if (active_flag)
|
|
then
|
|
{
|
|
for (int pn = 0; pn < ps_ptr->N_patches(); ++pn)
|
|
{
|
|
patch &p = ps_ptr->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)
|
|
{
|
|
const int II = ps_ptr->gpn_of_patch_irho_isigma(p, irho, isigma);
|
|
const fp old_Theta = p.gridfn(gfns::gfn__save_Theta,
|
|
irho, isigma);
|
|
const fp new_Theta = p.gridfn(gfns::gfn__Theta,
|
|
irho, isigma);
|
|
const fp d_dr_term = (new_Theta - old_Theta) / epsilon;
|
|
Jac_ptr->sum_into_element(II, II, d_dr_term);
|
|
}
|
|
}
|
|
}
|
|
|
|
// restore h and Theta
|
|
ps_ptr->add_to_ghosted_gridfn(-epsilon, gfns::gfn__h);
|
|
ps_ptr->gridfn_copy(gfns::gfn__save_Theta, gfns::gfn__Theta);
|
|
}
|
|
|
|
return expansion_success; // *** NORMAL RETURN ***
|
|
}
|
|
}
|
|
|
|
//******************************************************************************
|
|
|
|
} // namespace AHFinderDirect
|
|
#endif
|