// horizon_Jacobian.cc -- evaluate Jacobian matrix of LHS function H(h) // $Id$ // // <<>> // // horizon_Jacobian - top-level driver to compute the Jacobian /// /// horizon_Jacobian_NP - compute the Jacobian by numerical perturbation /// /// horizon_Jacobian_SD - compute the Jacobian (symbolic differentiation) /// add_ghost_zone_Jacobian - add ghost zone dependencies to Jacobian /// #include #include #include #include #include "util_Table.h" #include "cctk.h" #include "cctk_Arguments.h" #include "stdc.h" #include "config.hh" #include "../jtutil/util.hh" #include "../jtutil/array.hh" #include "../jtutil/cpm_map.hh" #include "../jtutil/linear_map.hh" using jtutil::error_exit; #include "../util/coords.hh" #include "../util/grid.hh" #include "../util/fd_grid.hh" #include "../util/patch.hh" #include "../util/patch_edge.hh" #include "../util/patch_interp.hh" #include "../util/ghost_zone.hh" #include "../util/patch_system.hh" #include "../elliptic/Jacobian.hh" #include "gfns.hh" #include "AHFinderDirect.hh" //****************************************************************************** // // ***** prototypes for functions local to this file ***** // namespace { void horizon_Jacobian_NP(patch_system& ps, const struct cactus_grid_info& cgi, const struct geometry_info& ii, const struct Jacobian_info& Jacobian_info, Jacobian& Jac); void horizon_Jacobian_SD(patch_system& ps, const struct cactus_grid_info& cgi, const struct geometry_info& gi, const struct Jacobian_info& Jacobian_info, Jacobian& Jac); void add_ghost_zone_Jacobian(const patch_system& ps, const patch& xp, const ghost_zone& xmgz, int x_II, int xm_irho, int xm_isigma, fp mol, Jacobian& Jac); } //****************************************************************************** // // This function is a top-level driver to compute the Jacobian matrix // J[H(h)] of the LHS function H(h). It just decodes the Jacobian_method // parameter and calls the appropriate subfunction. void horizon_Jacobian(patch_system& ps, const struct cactus_grid_info& cgi, const struct geometry_info& gi, const struct Jacobian_info& Jacobian_info, Jacobian& Jac) { switch (Jacobian_info.Jacobian_method) { case numerical_perturbation: horizon_Jacobian_NP(ps, cgi, gi, Jacobian_info, Jac); break; case symbolic_differentiation_with_FD_dr: horizon_Jacobian_SD(ps, cgi, gi, Jacobian_info, Jac); break; case symbolic_differentiation: // FIXME FIXME: need true symbolic diff for d/dr terms horizon_Jacobian_SD(ps, cgi, gi, Jacobian_info, Jac); break; default: error_exit(PANIC_EXIT, "***** horizon_Jacobian(): unknown Jacobian_info.Jacobian_method=(int)%d!\n" " (this should never happen!)\n" , int(Jacobian_info.Jacobian_method)); /*NOTREACHED*/ } } //****************************************************************************** //****************************************************************************** //****************************************************************************** // // This function computes the Jacobian matrix of the LHS function H(h) // by numerical perturbation of the H(h) function. The algorithm is as // follows: // // we assume that H = H(h) has already been evaluated // save_H = H // for each point (y,JJ) // { // const fp save_h_y = h at y; // h at y += perturbation_amplitude; // evaluate H(h) (silently) // for each point (x,II) // { // Jac(II,JJ) = (H(II) - save_H(II)) / perturbation_amplitude; // } // h at y = save_h_y; // } // H = save_H // namespace { void horizon_Jacobian_NP(patch_system& ps, const struct cactus_grid_info& cgi, const struct geometry_info& ii, const struct Jacobian_info& Jacobian_info, Jacobian& Jac) { CCTK_VInfo(CCTK_THORNSTRING, " horizon Jacobian (numerical perturbation)"); const fp epsilon = Jacobian_info.perturbation_amplitude; ps.gridfn_copy(gfns::gfn__H, gfns::gfn__save_H); for (int ypn = 0 ; ypn < ps.N_patches() ; ++ypn) { patch& yp = ps.ith_patch(ypn); CCTK_VInfo(CCTK_THORNSTRING, " perturbing in %s patch", yp.name()); for (int y_irho = yp.min_irho() ; y_irho <= yp.max_irho() ; ++y_irho) { for (int y_isigma = yp.min_isigma() ; y_isigma <= yp.max_isigma() ; ++y_isigma) { const int JJ = ps.gpn_of_patch_irho_isigma(yp, y_irho,y_isigma); const fp save_h_y = yp.ghosted_gridfn(gfns::gfn__h, y_irho,y_isigma); yp.ghosted_gridfn(gfns::gfn__h, y_irho,y_isigma) += epsilon; horizon_function(ps, cgi, ii); 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) { const int II = ps.gpn_of_patch_irho_isigma(xp, x_irho,x_isigma); const fp old_H = xp.gridfn(gfns::gfn__save_H, x_irho,x_isigma); const fp new_H = xp.gridfn(gfns::gfn__H, x_irho,x_isigma); Jac(II,JJ) = (new_H - old_H) / epsilon; } } } yp.ghosted_gridfn(gfns::gfn__h, y_irho,y_isigma) = save_h_y; } } } ps.gridfn_copy(gfns::gfn__save_H, gfns::gfn__H); } } //****************************************************************************** //****************************************************************************** //****************************************************************************** // // This function computes the Jacobian matrix of the LHS function H(h) // by symbolic differentiation from the Jacobian coefficient (angular) // gridfns. The computation is done a Jacobian row at a time, using // equation (25) of my 1996 apparent horizon finding paper, except that // the d/dr term is done by numerical perturbation. // // The overall algorithm is // save_H = H // h += perturbation_amplitude // evaluate H(h) (silently) // Jac = diagonal[ (H(h) - save_H)/perturbation_amplitude ] // h -= perturbation_amplitude // H = save_H // Jac += Jacobian coeffs * molecule coeffs // // Inputs (angular gridfns, on ghosted grid): // h # shape of trial surface // H # H(h) assumed to already be computed // partial_H_wrt_partial_d_h # Jacobian coefficients // partial_H_wrt_partial_dd_h # (also assumed to already be computed) // // Outputs: // The Jacobian matrix is stored in the Jacobian object Jac. // namespace { void horizon_Jacobian_SD(patch_system& ps, const struct cactus_grid_info& cgi, const struct geometry_info& gi, const struct Jacobian_info& Jacobian_info, Jacobian& Jac) { CCTK_VInfo(CCTK_THORNSTRING, " horizon Jacobian (symbolic differentiation)"); ps.compute_synchronize_Jacobian(); Jac.zero(); // // compute d/dr term in Jacobian by numerical perturbation // CCTK_VInfo(CCTK_THORNSTRING, " computing d/dr terms by numerical perturbation"); const fp epsilon = Jacobian_info.perturbation_amplitude; ps.gridfn_copy(gfns::gfn__H, gfns::gfn__save_H); ps.add_to_ghosted_gridfn(epsilon, gfns::gfn__h); horizon_function(ps, cgi, gi); for (int pn = 0 ; pn < ps.N_patches() ; ++pn) { patch& p = ps.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.gpn_of_patch_irho_isigma(p, irho,isigma); const fp old_H = p.gridfn(gfns::gfn__save_H, irho,isigma); const fp new_H = p.gridfn(gfns::gfn__H, irho,isigma); Jac(II,II) += (new_H - old_H) / epsilon; } } } ps.add_to_ghosted_gridfn(-epsilon, gfns::gfn__h); ps.gridfn_copy(gfns::gfn__save_H, gfns::gfn__H); // // now the main symbolic-differentiation Jacobian computation // CCTK_VInfo(CCTK_THORNSTRING, " computing main terms by symbolic differentiation"); 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 Jacobian row for this grid point, i.e. // partial H(this point x, Jacobian row II) // --------------------------------------------- // partial h(other points y, Jacobian column JJ) // // FIXME FIXME: we still have to take into account the // position-dependence of the coefficients, // cf the difference between J[3H(h)] and J[2H(h)]; // here we're sort of computing the former, but // Newton's method really wants the latter // // 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_H_wrt_partial_d_h_1, x_irho, x_isigma); const fp Jacobian_coeff_sigma = xp.gridfn(gfns::gfn__partial_H_wrt_partial_d_h_2, x_irho, x_isigma); const fp Jacobian_coeff_rho_rho = xp.gridfn(gfns::gfn__partial_H_wrt_partial_dd_h_11, x_irho, x_isigma); const fp Jacobian_coeff_rho_sigma = xp.gridfn(gfns::gfn__partial_H_wrt_partial_dd_h_12, x_irho, x_isigma); const fp Jacobian_coeff_sigma_sigma = xp.gridfn(gfns::gfn__partial_H_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 Jac(II, xp,xm_irho,x_isigma) += Jac_sum; else add_ghost_zone_Jacobian (ps, xp, xp.minmax_rho_ghost_zone(m_irho < 0), II, xm_irho, x_isigma, Jac_sum, Jac); } // 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 Jac(II, xp,x_irho,xm_isigma) += Jac_sum; else add_ghost_zone_Jacobian (ps, xp, xp.minmax_sigma_ghost_zone(m_isigma < 0), II, x_irho, xm_isigma, Jac_sum, Jac); } // 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 Jac(II, xp,xm_irho,xm_isigma) += Jac_rho_sigma; else add_ghost_zone_Jacobian (ps, xp, xp.corner_ghost_zone_containing_point(m_irho < 0, m_isigma < 0, xm_irho, xm_isigma), II, xm_irho, xm_isigma, Jac_rho_sigma, Jac); } } } } } } } //****************************************************************************** // // This function adds the ghost-zone Jacobian dependency contributions // for a single ghost-zone point, to a Jacobian matrix. // // Arguments: // ps = The patch system. // 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. // mol = The molecule coefficient. // Jac = The Jacobian matrix. // namespace { void add_ghost_zone_Jacobian(const patch_system& ps, const patch& xp, const ghost_zone& xmgz, int x_II, int xm_irho, int xm_isigma, fp mol, Jacobian& Jac) { 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); // on what other points ym does this molecule point xm depend // via the patch_system::synchronize() operation? const patch& ymp = ps.synchronize_Jacobian_y_patch(xmgz); const patch_edge& yme = ps.synchronize_Jacobian_y_edge (xmgz); const int min_ym_ipar_m = ps.synchronize_Jacobian_min_y_ipar_m(xmgz); const int max_ym_ipar_m = ps.synchronize_Jacobian_max_y_ipar_m(xmgz); const int ym_iperp = ps.synchronize_Jacobian_y_iperp(xmgz, xm_iperp); const int ym_ipar_posn = ps.synchronize_Jacobian_y_ipar_posn (xmgz, xm_iperp, xm_ipar); // add the Jacobian contributions from the ym points for (int ym_ipar_m = min_ym_ipar_m ; ym_ipar_m <= max_ym_ipar_m ; ++ym_ipar_m) { const int ym_ipar = ym_ipar_posn + ym_ipar_m; const int ym_irho = yme. irho_of_iperp_ipar(ym_iperp,ym_ipar); const int ym_isigma = yme.isigma_of_iperp_ipar(ym_iperp,ym_ipar); const int JJ = ps.gpn_of_patch_irho_isigma(ymp, ym_irho, ym_isigma); const fp sync_Jac = ps.synchronize_Jacobian(xmgz, xm_iperp, xm_ipar, ym_ipar_m); Jac(x_II,JJ) += mol*sync_Jac; } } }