Task: Implement tov.c for EinsteinInitialData/TOVSolver thorn

thorn_name: EinsteinInitialData/TOVSolver
src_filename: tov.c
interface: # Interface definition for thorn TOVSolver

implements: TOVSolver
inherits: ADMBase, HydroBase, Constants, StaticConformal

USES INCLUDE: constants.h

public:

CCTK_INT FUNCTION Set_Rho_ADM(              \
    CCTK_POINTER_TO_CONST IN  cctkGH,       \
    CCTK_INT              IN  size,         \
    CCTK_REAL ARRAY       OUT source,       \
    CCTK_REAL ARRAY       IN  x,            \
    CCTK_REAL ARRAY       IN  y,            \
    CCTK_REAL ARRAY       IN  z             \
    )
PROVIDES FUNCTION Set_Rho_ADM  \
         WITH TOV_Set_Rho_ADM  \
         LANGUAGE C

CCTK_INT FUNCTION Set_Momentum_Source(      \
    CCTK_POINTER_TO_CONST IN  cctkGH,       \
    CCTK_INT              IN  dir,          \
    CCTK_INT              IN  size,         \
    CCTK_REAL ARRAY       OUT source,       \
    CCTK_REAL ARRAY       IN  x,            \
    CCTK_REAL ARRAY       IN  y,            \
    CCTK_REAL ARRAY       IN  z             \
    )
PROVIDES FUNCTION Set_Momentum_Source  \
         WITH TOV_Set_Momentum_Source  \
         LANGUAGE C

CCTK_INT FUNCTION Set_Initial_Guess_for_u(  \
    CCTK_POINTER_TO_CONST IN  cctkGH,       \
    CCTK_INT              IN  size,         \
    CCTK_REAL ARRAY       OUT u,            \
    CCTK_REAL ARRAY       IN  x,            \
    CCTK_REAL ARRAY       IN  y,            \
    CCTK_REAL ARRAY       IN  z             \
    )
PROVIDES FUNCTION Set_Initial_Guess_for_u \
         WITH TOV_Set_Initial_Guess_for_u \
         LANGUAGE C

CCTK_INT FUNCTION Rescale_Sources(  \
    CCTK_POINTER_TO_CONST IN  cctkGH,       \
    CCTK_INT              IN  size,         \
    CCTK_REAL ARRAY       IN  x,            \
    CCTK_REAL ARRAY       IN  y,            \
    CCTK_REAL ARRAY       IN  z,            \
    CCTK_REAL ARRAY       IN  psi,          \
    CCTK_REAL ARRAY       IN  gxx,          \
    CCTK_REAL ARRAY       IN  gyy,          \
    CCTK_REAL ARRAY       IN  gzz,          \
    CCTK_REAL ARRAY       IN  gxy,          \
    CCTK_REAL ARRAY       IN  gxz,          \
    CCTK_REAL ARRAY       IN  gyz           \
    )
PROVIDES FUNCTION Rescale_Sources \
         WITH TOV_Rescale_Sources \
         LANGUAGE C


schedule: # Schedule definitions for thorn TOVSolver

schedule TOV_C_ParamCheck AT PARAMCHECK
{
  LANG: C
  OPTIONS: GLOBAL
} "Check parameters"

schedule TOV_C_AllocateMemory AT WRAGH
{
  LANG: C
  OPTIONS: GLOBAL
} "Allocate memory for TOVSolver_C"

if (!TOV_fake_evolution)
{
  schedule TOV_C_FreeMemory AT POSTPOSTINITIAL
  {
    LANG: C
    OPTIONS: GLOBAL
  } "Free memory from TOVSolver_C"
}

schedule GROUP TOV_Initial_Data IN HydroBase_Initial
{
#  STORAGE: TOV_Scalars
#  STORAGE: TOV_Arrays
  SYNC:ADMBase::metric,ADMBase::curv,ADMBase::lapse,ADMBase::shift
  SYNC: rho
  SYNC: press
  SYNC: eps
  SYNC: vel
  SYNC: w_lorentz
} "Group for the TOV initial data"

schedule TOV_C_Integrate_RHS IN TOV_Initial_Data
{
  LANG: C
  OPTIONS: GLOBAL
} "Integrate the 1d equations for the TOV star"

if (!CCTK_Equals(TOV_save_to_datafile,""))
{
  schedule TOV_write_1D_datafile IN TOV_Initial_Data AFTER TOV_C_Integrate_RHS BEFORE TOV_C_Exact
  {
    LANG: C
    OPTIONS: GLOBAL
  } "Save data to file and exit"
}

if (TOV_ProperPosition)
{
  schedule TOV_Set_ProperPositions in TOV_Initial_Data after TOV_C_Integrate_RHS before TOV_C_Exact
  {
    LANG: C
    Options: GLOBAL
  } "Steer NS position parameters according to proper distance"
}

if (CCTK_Equals(initial_data, "tov") || CCTK_Equals(initial_hydro, "tov") ||
    (TOV_Use_Old_Initial_Data > 0) || TOV_Enforce_Interpolation)
{
  schedule TOV_C_Exact IN TOV_Initial_Data AFTER TOV_C_Integrate_RHS
  {
    LANG: C
  } "Set up the 3d quantities for the TOV star"
}

if (TOV_fake_evolution)
{
  schedule TOV_Prepare_Fake_Evolution AFTER TOV_C_Exact
  {
    LANG: C
  } "prepare for fake evolution"
  schedule TOV_C_Exact IN MoL_PostStep AFTER HydroBase_PostStep
  {
    LANG: C
  } "use fake evolution"
}


param: # Parameter definitions for thorn TOVSolver

CCTK_INT TOV_Num_TOVs "The number of TOVs"
{
  1:* :: "Greater than 0"
} 1

CCTK_INT TOV_Solve_for_TOVs "Solve for TOVs even if no TOV initial data was requested?"
{
  0:3 :: "depreciated in favour of TOVSolver::TOV_Enforce_Interpolation"
} 3

BOOLEAN TOV_Enforce_Interpolation "Enforce the interpolation of the data onto the Hydro GFs even without tov as specified initial data" STEERABLE=always
{
} "no"

CCTK_INT TOV_Num_Radial "The number of radial points for the ODE integration"
{
  1:* :: "Greater than 0"
} 100000

CCTK_REAL TOV_Rho_Central[10] "The central density"
{
  (0.0:* :: "Must be positive"
} 1e-3

CCTK_REAL TOV_Gamma "The polytropic constant in P = K rho^Gamma"
{
  1.0: :: "The physical range at high Lorentz factors is [1,2], but otherwise higher values of gamma can also be used"
} 2.0

CCTK_REAL TOV_K "The polytropic constant in P = K rho^Gamma"
{
  (0.0:* :: "Greater than 0"
} 100.0

CCTK_REAL TOV_dr[10] "The spacing in the radial direction on the 1d grid"
{
  (0.0:* :: "Greater than 0"
} 5.e-4

BOOLEAN Perturb[10] "Add density perturbation (you should solve the IVP if true)"
{
} "no"

CCTK_REAL Pert_Amplitude[10] "Amplitude of perturbation"
{
  *:* :: "Anything"
} 0.0

CCTK_REAL TOV_Position_x[10] "Position of neutron star, x coordinate" STEERABLE=always
{
  *:* :: "real"
} 0.0

CCTK_REAL TOV_Position_y[10] "Position of neutron star, y coordinate" STEERABLE=always
{
  *:* :: "real"
} 0.0

CCTK_REAL TOV_Position_z[10] "Position of neutron star, z coordinate" STEERABLE=always
{
  *:* :: "real"
} 0.0

# contravariant fluid three velocity as measured by the Eulerian observers: v^i = u^i / (alpha u^t) + beta^i / alpha
# as used by HydroBase. Follows the Valencia formulation eg. eqs. 26 and 27 of Font et al's paper (gr-qc/9811015) or 
# below Equ. 31 in http://relativity.livingreviews.org/Articles/lrr-2008-7/articlesu1.html#x6-30002.1
CCTK_REAL TOV_Velocity_x[10] "(fixed) Velocity of neutron star, x coordinate (caution!)" STEERABLE=always
{
  *:* :: "real"
} 0.0

CCTK_REAL TOV_Velocity_y[10] "(fixed) Velocity of neutron star, y coordinate (caution!)" STEERABLE=always
{
  *:* :: "real"
} 0.0

CCTK_REAL TOV_Velocity_z[10] "(fixed) Velocity of neutron star, z coordinate (caution!)" STEERABLE=always
{
  *:* :: "real"
} 0.0

BOOLEAN TOV_ProperPosition "For use only with two NSs, atm only handles equal mass"
{
} "no"

BOOLEAN TOV_Fast_Interpolation "Use faster interpolation algorithm? Default is yes."
{
} "yes"

BOOLEAN TOV_Clear_Initial_Data "Clear initial data (spacetime)? Default is yes."
{
} "yes"

BOOLEAN TOV_Use_Old_Initial_Data "Take old initial data into account (spacetime)? Default is no."
{
} "no"

BOOLEAN TOV_Use_Old_Matter_Initial_Data "Use also old matter initial data? Default is no."
{
} "no"

BOOLEAN TOV_Conformal_Flat_Three_Metric "Use conformal factor to get the 3-metric flat. default is no"
{
} "no"

keyword TOV_Combine_Method "Which combine method should be used."
{
  "maximum" :: "Take the maximum of rho and gxx as clue for the rest as clue."
  "average" :: "Take the average of all available parts."
} "average"

CCTK_INT TOV_Populate_Timelevels "Populate that amount of timelevels" STEERABLE=always
{
  1:3 :: "1 (default) to 3"
} 1

CCTK_INT TOV_Momentum_Psi_Power "Power of Psi to be multiplied with J^i for Mom"
{
  *:* :: "anything, 0 as default"
} 0

CCTK_INT TOV_fake_evolution "Fake evolution by setting ID at every step" STEERABLE=always
{
  *:* :: "anything, 0 as off (default), everything else as on"
} 0

STRING TOV_save_to_datafile "Only save data to file and exit"
{
  ".*" :: "Any filename, not used if empty"
} ""

shares:HydroBase

EXTENDS KEYWORD initial_hydro ""
{
  "tov" :: "TOV star initial hydrobase variables"
}

shares:admbase

EXTENDS KEYWORD initial_data
{
  "tov" :: "TOV star initial metric"
}

EXTENDS KEYWORD initial_lapse
{
  "tov" :: "TOV star initial lapse; isotropic"
}

EXTENDS KEYWORD initial_shift
{
  "tov" :: "TOV star initial shift"
}

USES KEYWORD metric_type

shares:StaticConformal

USES KEYWORD conformal_storage

configuration: # Configuration definition for thorn TOVSolver

REQUIRES THORNS: Boundary CartGrid3D SpaceMask


context: tov.h:
```

void TOVSolverC_export_local_variables(CCTK_REAL **exported_TOV_press_1d, CCTK_REAL **exported_TOV_m_1d, CCTK_REAL **exported_TOV_phi_1d, CCTK_REAL **exported_TOV_rbar_1d, CCTK_REAL **exported_TOV_r_1d );

```

external.inc:
```

/* for debug output the location of the points */
void TOV_debug_input_points(CCTK_INT size,
                       const CCTK_REAL *x,
                       const CCTK_REAL *y,
                       const CCTK_REAL *z);
void TOV_debug_input_points(CCTK_INT size,
                       const CCTK_REAL *x,
                       const CCTK_REAL *y,
                       const CCTK_REAL *z)
{
    FILE *f;
    int i;
    f=fopen("test.out", "w");
    for (i=0; i<size; i++)
      fprintf(f, "%.8g %.8g %.8g\n", x[i], y[i], z[i]);
    fclose(f);
}

static inline int get_nearest_star(const CCTK_REAL *x, const CCTK_REAL *y,
                            const CCTK_REAL *z, const int i)
{
    DECLARE_CCTK_PARAMETERS
    int star = 0, star_loop;
    CCTK_REAL r_tmp, r_to_star=1.e40;  /* something very large */
    for (star_loop=0; star_loop<TOV_Num_TOVs; star_loop++)
    {
        r_tmp = sqrt( (x[i]-TOV_Position_x[star_loop]) *
                      (x[i]-TOV_Position_x[star_loop]) +
                      (y[i]-TOV_Position_y[star_loop]) *
                      (y[i]-TOV_Position_y[star_loop]) +
                      (z[i]-TOV_Position_z[star_loop]) *
                      (z[i]-TOV_Position_z[star_loop]) );
        if (r_tmp < r_to_star)
        {
            r_to_star = r_tmp;
            star = star_loop;
        }
    }
    return star;
}
/* This function provides an interface for other thorns which can input a list
 * of points (p_x, p_y, p_z) with a size of 'size' and get back the rho_adm
 * at these points (source) */
/* NOTE: when used with two or more NS, there might be problems if they are
 * very close to each other, because this function does not use the fancy
 * algorithms above to determine from which NS the data for a perticular grid
 * point should be taken, but just uses the closest one */
CCTK_INT TOV_Set_Rho_ADM(CCTK_POINTER_TO_CONST cctkGH,
                         CCTK_INT  size,
                         CCTK_REAL *source,
                         const CCTK_REAL *x,
                         const CCTK_REAL *y,
                         const CCTK_REAL *z )
{
  DECLARE_CCTK_PARAMETERS

  assert(TOV_Surface!=0);
  assert(TOV_R_Surface!=0);

  assert(TOV_r_1d!=0);
  assert(TOV_rbar_1d!=0);
  assert(TOV_press_1d!=0);
  assert(TOV_phi_1d!=0);
  assert(TOV_m_1d!=0);

  /* CCTK_INFO("Request for Sources from TOVSolver"); */
  /* TOV_debug_input_points(size,x,y,z); */
  /* loop over all points we got */

  #pragma omp parallel for
  for (CCTK_INT i=0; i<size; i++)
  {
    CCTK_REAL r_to_star, press, phi, r;
    CCTK_REAL rho, gxx, w_lorentz_2;
    CCTK_INT  star_i=0, star=0;
    /* calculate the distance to the star */
    star=get_nearest_star(x,y,z,i);
    star_i = star * TOV_Num_Radial;
    r_to_star = sqrt( (x[i]-TOV_Position_x[star]) *
                      (x[i]-TOV_Position_x[star]) +
                      (y[i]-TOV_Position_y[star]) *
                      (y[i]-TOV_Position_y[star]) +
                      (z[i]-TOV_Position_z[star]) *
                      (z[i]-TOV_Position_z[star]) );
    /* call the interpolator */
    TOV_C_interp_tov_isotropic(star,
                               &(TOV_press_1d[star_i]), &(TOV_phi_1d[star_i]),
                               &(TOV_rbar_1d[star_i]),  &(TOV_r_1d[star_i]),
                               &r_to_star, TOV_Surface[star],
                               &press, &phi, &r);
    press = (press > 0.0) ? press : 0.0;
    rho = pow(press/TOV_K, 1.0/TOV_Gamma);
    gxx = r / (r_to_star + 1.0e-30) * r / (r_to_star + 1.0e-30);
    /* velocity components as in gr-qc/9811015 */
    w_lorentz_2 = 1. / (1. - gxx*TOV_Velocity_x[star]*TOV_Velocity_x[star]
                           - gxx*TOV_Velocity_y[star]*TOV_Velocity_y[star]
                           - gxx*TOV_Velocity_z[star]*TOV_Velocity_z[star]);
    if (rho > 0.0)
      source[i]=gxx*gxx* (w_lorentz_2*rho +
                          (w_lorentz_2*TOV_Gamma/(TOV_Gamma-1.)-1.)*
                          press);
    else
      source[i]=0.0;
  }
  return 1;
}

/* This function provides an interface for other thorns which can input a list
 * of points (p_x, p_y, p_z) with a size of 'size' and get back the momentum
 * sources at these points (source) */
/* NOTE: when used with two or more NS, there might be problems if they are
 * very close to each other, because this function does not use the fancy
 * algorithms above to determine from which NS the data for a perticular grid
 * point should be taken, but just uses the closest one */
CCTK_INT TOV_Set_Momentum_Source(
                         CCTK_POINTER_TO_CONST cctkGH,
                         CCTK_INT  dir,
                         CCTK_INT  size,
                         CCTK_REAL *source,
                         const CCTK_REAL *x,
                         const CCTK_REAL *y,
                         const CCTK_REAL *z )
{
  DECLARE_CCTK_PARAMETERS
  CCTK_REAL r_to_star, press, phi, r, gxx, rho, rho_eps, psip, w_lorentz_2;
  CCTK_INT  star_i=0, star=0;
  CCTK_INT  i;

  assert(TOV_Surface!=0);
  assert(TOV_R_Surface!=0);

  assert(TOV_r_1d!=0);
  assert(TOV_rbar_1d!=0);
  assert(TOV_press_1d!=0);
  assert(TOV_phi_1d!=0);
  assert(TOV_m_1d!=0);

  /* CCTK_INFO("Request for Momentum-Sources from TOVSolver"); */
  /* loop over all points we got */

  for (i=0; i<size; i++)
  {
    /* calculate the distance to the star */
    star=get_nearest_star(x,y,z,i);
    star_i = star * TOV_Num_Radial;
    r_to_star = sqrt( (x[i]-TOV_Position_x[star]) *
                      (x[i]-TOV_Position_x[star]) +
                      (y[i]-TOV_Position_y[star]) *
                      (y[i]-TOV_Position_y[star]) +
                      (z[i]-TOV_Position_z[star]) *
                      (z[i]-TOV_Position_z[star]) );
    /* call the interpolator */
    TOV_C_interp_tov_isotropic(star,
                               &(TOV_press_1d[star_i]), &(TOV_phi_1d[star_i]),
                               &(TOV_rbar_1d[star_i]),  &(TOV_r_1d[star_i]),
                               &r_to_star, TOV_Surface[star],
                               &press, &phi, &r);
    rho = pow(press/TOV_K, 1.0/TOV_Gamma);
    gxx = r / (r_to_star + 1.0e-30) * r / (r_to_star + 1.0e-30);
    psip = pow(gxx, TOV_Momentum_Psi_Power/4.);
    /* velocity components as in gr-qc/9811015 */
    w_lorentz_2 = 1./(1. - gxx*TOV_Velocity_x[star]*TOV_Velocity_x[star]
                         - gxx*TOV_Velocity_y[star]*TOV_Velocity_y[star]
                         - gxx*TOV_Velocity_z[star]*TOV_Velocity_z[star]);
    rho_eps = w_lorentz_2 * (rho + TOV_Gamma/(TOV_Gamma-1.) * press);
    switch(dir)
    {
      case 0: source[i]=psip*rho_eps*gxx*TOV_Velocity_x[star]; break;
      case 1: source[i]=psip*rho_eps*gxx*TOV_Velocity_y[star]; break;
      case 2: source[i]=psip*rho_eps*gxx*TOV_Velocity_z[star]; break;
      default: CCTK_WARN(0,
                         "dir has to be in [0:2] in TOV_Set_Momentum_Source");
    }
    /* Only set velocity where matter is */
    if (press<1.e-15)
      source[i]=0.0;
  }
  return 1;
}

/* NOTE: when used with two or more NS, there might be problems if they are
 * very close to each other, because this function does not use the fancy
 * algorithms above to determine from which NS the data for a perticular grid
 * point should be taken, but just uses the closest one */
CCTK_INT TOV_Set_Initial_Guess_for_u(
                         CCTK_POINTER_TO_CONST cctkGH,
                         CCTK_INT  size,
                         CCTK_REAL *u,
                         const CCTK_REAL *x,
                         const CCTK_REAL *y,
                         const CCTK_REAL *z )
{
  DECLARE_CCTK_PARAMETERS
  CCTK_REAL r_to_star, press, phi, r;
  CCTK_INT  star_i=0, star=0;
  CCTK_INT  i;

  assert(TOV_Surface!=0);
  assert(TOV_R_Surface!=0);

  assert(TOV_r_1d!=0);
  assert(TOV_rbar_1d!=0);
  assert(TOV_press_1d!=0);
  assert(TOV_phi_1d!=0);
  assert(TOV_m_1d!=0);

  CCTK_INFO("Using initial guess from TOVSolver");
  /* TOV_debug_input_points(size,x,y,z); */

  /* loop over all points we got */
  for (i=0; i<size; i++)
  {
    CCTK_REAL gxx;
    /* calculate the distance to the stars and choose the closest */
    star=get_nearest_star(x,y,z,i);
    star_i = star * TOV_Num_Radial;
    r_to_star = sqrt( (x[i]-TOV_Position_x[star]) *
                      (x[i]-TOV_Position_x[star]) +
                      (y[i]-TOV_Position_y[star]) *
                      (y[i]-TOV_Position_y[star]) +
                      (z[i]-TOV_Position_z[star]) *
                      (z[i]-TOV_Position_z[star]) );
    /* call the interpolator */
    TOV_C_interp_tov_isotropic(star,
                               &(TOV_press_1d[star_i]), &(TOV_phi_1d[star_i]),
                               &(TOV_rbar_1d[star_i]),  &(TOV_r_1d[star_i]),
                               &r_to_star, TOV_Surface[star],
                               &press, &phi, &r);
    gxx = r / (r_to_star + 1.0e-30) * r / (r_to_star + 1.0e-30);
    u[i]=pow(gxx, 0.25) - 1.0;
  }
  return 1;
}


void bisection     (CCTK_REAL *vx, CCTK_REAL *vy, CCTK_REAL *vz,
                    CCTK_REAL *rhoold, CCTK_REAL *rhonew,
                    CCTK_REAL *w_lorentz_2, CCTK_REAL *v_2,
                    CCTK_REAL Gamma, CCTK_REAL K,
                    CCTK_REAL source,
                    CCTK_REAL mom_source_x,
                    CCTK_REAL mom_source_y,
                    CCTK_REAL mom_source_z,
                    CCTK_REAL my_psi4,
                    CCTK_REAL x, CCTK_REAL y, CCTK_REAL z, CCTK_REAL rho_orig);
void bisection     (CCTK_REAL *vx, CCTK_REAL *vy, CCTK_REAL *vz,
                    CCTK_REAL *rhoold, CCTK_REAL *rhonew,
                    CCTK_REAL *w_lorentz_2, CCTK_REAL *v_2,
                    CCTK_REAL Gamma, CCTK_REAL K,
                    CCTK_REAL source,
                    CCTK_REAL mom_source_x,
                    CCTK_REAL mom_source_y,
                    CCTK_REAL mom_source_z,
                    CCTK_REAL my_psi4,
                    CCTK_REAL x, CCTK_REAL y, CCTK_REAL z, CCTK_REAL rho_orig)
{
    CCTK_REAL lb, ub, f;
    lb=0.0;
    ub=2*rho_orig;
    do
    {
        *rhonew = (ub-lb)/2.;
        *vx = mom_source_x/my_psi4/
              (source/my_psi4/my_psi4+K * pow(*rhonew, Gamma));
        *vy = mom_source_y/my_psi4/
              (source/my_psi4/my_psi4+K * pow(*rhonew, Gamma));
        *vz = mom_source_z/my_psi4/
              (source/my_psi4/my_psi4+K * pow(*rhonew, Gamma));
        *v_2 = my_psi4*(*vx**vx+*vy**vy+*vz**vz);
        /* velocity components as in gr-qc/9811015 */
        *w_lorentz_2 = 1./(1.- *v_2);
        f = my_psi4*my_psi4*(
            *w_lorentz_2 * *rhonew +
            (*w_lorentz_2*Gamma/(Gamma-1.)-1.) *
            K * pow(*rhonew, Gamma)) - source;
        if (f < 0.0)
          ub = *rhonew;
        else
          lb = *rhonew;
        *rhoold = *rhonew;
    } while (ub-lb < 1.e-10);
}

void newton_raphson(CCTK_REAL *vx, CCTK_REAL *vy, CCTK_REAL *vz,
                    CCTK_REAL *rhoold, CCTK_REAL *rhonew,
                    CCTK_REAL *w_lorentz_2, CCTK_REAL *v_2,
                    CCTK_REAL Gamma, CCTK_REAL K,
                    CCTK_REAL source,
                    CCTK_REAL mom_source_x,
                    CCTK_REAL mom_source_y,
                    CCTK_REAL mom_source_z,
                    CCTK_REAL my_psi4,
                    CCTK_REAL x, CCTK_REAL y, CCTK_REAL z, CCTK_REAL rho_orig);
void newton_raphson(CCTK_REAL *vx, CCTK_REAL *vy, CCTK_REAL *vz,
                    CCTK_REAL *rhoold, CCTK_REAL *rhonew,
                    CCTK_REAL *w_lorentz_2, CCTK_REAL *v_2,
                    CCTK_REAL Gamma, CCTK_REAL K,
                    CCTK_REAL source,
                    CCTK_REAL mom_source_x,
                    CCTK_REAL mom_source_y,
                    CCTK_REAL mom_source_z,
                    CCTK_REAL my_psi4,
                    CCTK_REAL x, CCTK_REAL y, CCTK_REAL z, CCTK_REAL rho_orig)
{
    int count=0;
    CCTK_REAL d_w_lorentz_2, f, df;
      do
      {
        count++;
        *vx = mom_source_x/my_psi4/
              (source/my_psi4/my_psi4+K * pow(*rhonew, Gamma));
        *vy = mom_source_y/my_psi4/
              (source/my_psi4/my_psi4+K * pow(*rhonew, Gamma));
        *vz = mom_source_z/my_psi4/
              (source/my_psi4/my_psi4+K * pow(*rhonew, Gamma));
        *v_2 = my_psi4*(*vx**vx+*vy**vy+*vz**vz);
        if (count > 100)
        {
          CCTK_VWarn(1, __LINE__, __FILE__, CCTK_THORNSTRING, \
                     "Failed to converge IVP rescaling, trying slow bisection method\n"\
                     "  at (%.3g %.3g %.3g)\n"\
                     "  orig_rho:%g rho:%g, v^2=%g, rel_diff=%g", \
                     x, y, z, rho_orig, *rhonew, *v_2,
                     fabs(*rhonew-*rhoold)/fabs(*rhonew));
          bisection(vx,vy,vz,rhoold,rhonew,w_lorentz_2,v_2,
                    Gamma, K,
                    source, mom_source_x, mom_source_y, mom_source_z,
                    my_psi4, x, y, z, rho_orig);
          return;
        }
        /* velocity components as in gr-qc/9811015 */
        *w_lorentz_2 = 1./(1.- *v_2);
        f = my_psi4*my_psi4*(
            *w_lorentz_2 * *rhonew +
            (*w_lorentz_2*Gamma/(Gamma-1.)-1.) *
            K * pow(*rhonew, Gamma)) - source;
        /* d_w_lorentz_2/drhonew */
        d_w_lorentz_2 =
             -2 * (*w_lorentz_2)*(*w_lorentz_2) * 
             (*v_2) * K*Gamma*pow(*rhonew, Gamma-1) / 
             (source/my_psi4/my_psi4 + K*pow(*rhonew, Gamma));
        df = my_psi4*my_psi4*(
             d_w_lorentz_2**rhonew + *w_lorentz_2 +
             d_w_lorentz_2*Gamma/(Gamma-1.)*
              K * pow(*rhonew, Gamma) +
             (*w_lorentz_2*Gamma/(Gamma-1.)-1.) *
               K * Gamma * pow(*rhonew, Gamma-1.));
        *rhoold = *rhonew;
        *rhonew = *rhoold - f/df;
      }
      while ( (fabs(*rhonew - *rhoold)/fabs(*rhonew) > 1.e-12) &&
              (fabs(*rhonew - *rhoold) > 1.e-12)
            );
}

CCTK_INT TOV_Rescale_Sources(
    CCTK_POINTER_TO_CONST cctkGH,
    CCTK_INT              size,
    const CCTK_REAL       *x,
    const CCTK_REAL       *y,
    const CCTK_REAL       *z,
    const CCTK_REAL       *psi,
    const CCTK_REAL       *gxx,
    const CCTK_REAL       *gyy,
    const CCTK_REAL       *gzz,
    const CCTK_REAL       *gxy,
    const CCTK_REAL       *gxz,
    const CCTK_REAL       *gyz)
{
  DECLARE_CCTK_PARAMETERS
  CCTK_REAL *rho, *press, *eps, *w_lorentz, *vel0, *vel1, *vel2;
  CCTK_REAL *rho_p, *press_p, *eps_p, *w_lorentz_p, *vel0_p, *vel1_p, *vel2_p;
  CCTK_REAL *rho_p_p, *press_p_p, *eps_p_p, *w_lorentz_p_p,
            *vel0_p_p, *vel1_p_p, *vel2_p_p;
  CCTK_REAL *source, *mom_source[3];
  CCTK_REAL psip, my_psi4, vx, vy, vz, v_2,
            w_lorentz_2;
  int       type;
  CCTK_REAL rhoold, rhonew;
  int i3D;
  static int debug = 1;
  FILE *debugfile = NULL;

  CCTK_INFO("Rescaling Sources");

  /* get Hydro variables */
  rho      =(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 0, "HydroBase::rho");
  press    =(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 0, "HydroBase::press");
  eps      =(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 0, "HydroBase::eps");
  w_lorentz=(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 0, "HydroBase::w_lorentz");
  vel0     =(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 0, "HydroBase::vel[0]");
  vel1     =(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 0, "HydroBase::vel[1]");
  vel2     =(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 0, "HydroBase::vel[2]");

  rho_p    =(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 1, "HydroBase::rho");
  press_p  =(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 1, "HydroBase::press");
  eps_p    =(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 1, "HydroBase::eps");
  w_lorentz_p=(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 1, "HydroBase::w_lorentz");
  vel0_p   =(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 1, "HydroBase::vel[0]");
  vel1_p   =(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 1, "HydroBase::vel[1]");
  vel2_p   =(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 1, "HydroBase::vel[2]");

  rho_p_p  =(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 2, "HydroBase::rho");
  press_p_p=(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 2, "HydroBase::press");
  eps_p_p  =(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 2, "HydroBase::eps");
  w_lorentz_p_p=(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 2, "HydroBase::w_lorentz");
  vel0_p_p =(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 2, "HydroBase::vel[0]");
  vel1_p_p =(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 2, "HydroBase::vel[1]");
  vel2_p_p =(CCTK_REAL*)CCTK_VarDataPtr(cctkGH, 2, "HydroBase::vel[2]");

  /* get the old source */
  source=calloc(size, sizeof(CCTK_REAL));
  TOV_Set_Rho_ADM(cctkGH, size, source, x, y, z);
  mom_source[0]=calloc(size, sizeof(CCTK_REAL));
  mom_source[1]=calloc(size, sizeof(CCTK_REAL));
  mom_source[2]=calloc(size, sizeof(CCTK_REAL));
  TOV_Set_Momentum_Source(cctkGH, 0, size, mom_source[0], x, y, z);
  TOV_Set_Momentum_Source(cctkGH, 1, size, mom_source[1], x, y, z);
  TOV_Set_Momentum_Source(cctkGH, 2, size, mom_source[2], x, y, z);

  if (debug)
  {
    debugfile=fopen("tovdebug.dat", "w");
    fprintf(debugfile, "# 1:x 2:y 3:psi4 4:source 5-7:mom_source_?, 8,9: ham-source, 10,11: momx-source\n");
  }
  /* loop over all points we got */
  for (i3D=0; i3D<size; i3D++)
  {
    if (source[i3D]>0.0)
    {
      if (psi != NULL)
        my_psi4 = pow(psi[i3D], 4.0)*gxx[i3D];
      else
        my_psi4 = gxx[i3D];
      /* We store the original J^i in mom_source[i] */
      psip = pow(my_psi4, TOV_Momentum_Psi_Power/4.);
      mom_source[0][i3D] /= psip;
      mom_source[1][i3D] /= psip;
      mom_source[2][i3D] /= psip;
      if (debug && (fabs(z[i3D])<1.e-15))
      {
        fprintf(debugfile,
                "%.15g %.15g %.15g %.15g %.15g %.15g %.15g ",
                x[i3D], y[i3D], my_psi4, source[i3D],
                mom_source[0][i3D], mom_source[1][i3D], mom_source[1][i3D]);
      }

      if (rho[i3D] < 1.e-10)
          rho[i3D] = 1.e-10;
      rhoold = rhonew = rho[i3D];
#ifdef OLD_NR
      int count=0;
      do
      {
        CCTK_REAL f, df;
        count++;
        vx = mom_source[0][i3D]/my_psi4/
             (source[i3D]/my_psi4/my_psi4+TOV_K * pow(rhonew, TOV_Gamma));
        vy = mom_source[1][i3D]/my_psi4/
             (source[i3D]/my_psi4/my_psi4+TOV_K * pow(rhonew, TOV_Gamma));
        vz = mom_source[2][i3D]/my_psi4/
             (source[i3D]/my_psi4/my_psi4+TOV_K * pow(rhonew, TOV_Gamma));
        v_2 = my_psi4*(vx*vx+vy*vy+vz*vz);
        if (count > 100)
          CCTK_VWarn(count<110, __LINE__, __FILE__, CCTK_THORNSTRING, \
                     "Failed to converge IVP rescaling\nat (%.3g %.3g %.3g)\n"\
                     " orig_rho:%g rho:%g, v^2=%g, rel_diff=%g", \
                     x[i3D], y[i3D], z[i3D], rho[i3D], rhonew, v_2,
                     fabs(rhonew-rhoold)/fabs(rhonew));
        /* velocity components as in gr-qc/9811015 */
        w_lorentz_2 = 1./(1.- v_2);
        /*
        f = TOV_K / (TOV_Gamma-1.0) * pow(rhonew, TOV_Gamma) +
            rhonew - source[i3D];
        df = TOV_K * TOV_Gamma / (TOV_Gamma-1.0) *
             pow(rhonew, TOV_Gamma-1.0) + 1.0;
        */
        f = my_psi4*my_psi4*(
            w_lorentz_2 * rhonew +
            (w_lorentz_2*TOV_Gamma/(TOV_Gamma-1.)-1.) *
            TOV_K * pow(rhonew, TOV_Gamma)) - source[i3D];
        /*
        df= w_lorentz_2 +
            (w_lorentz_2*TOV_Gamma/(TOV_Gamma-1.)-1.) *
            TOV_K * TOV_Gamma * pow(rhonew, TOV_Gamma-1.);
        */
        /* d_w_lorentz_2/drhonew */
        CCTK_REAL d_w_lorentz_2 =
             -2 * w_lorentz_2*w_lorentz_2 *
             v_2  * TOV_K*TOV_Gamma*pow(rhonew, TOV_Gamma-1) /
             (source[i3D]/my_psi4/my_psi4 + TOV_K*pow(rhonew, TOV_Gamma));
        df = my_psi4*my_psi4*(
             d_w_lorentz_2*rhonew + w_lorentz_2 +
             d_w_lorentz_2*TOV_Gamma/(TOV_Gamma-1.)*
              TOV_K * pow(rhonew, TOV_Gamma) +
             (w_lorentz_2*TOV_Gamma/(TOV_Gamma-1.)-1.) *
               TOV_K * TOV_Gamma * pow(rhonew, TOV_Gamma-1.));
        /*
        df= 1. + my_psi4*v_2/(rhonew*rhonew) +
            ((TOV_Gamma+(2.-TOV_Gamma)*my_psi4*v_2/(rhonew*rhonew))
             / (TOV_Gamma-1.) - 1.)
            * TOV_K * TOV_Gamma * pow(rhonew, TOV_Gamma-1.);
        */

        rhoold = rhonew;
        rhonew = rhoold - f/df;
      }
      while ( (fabs(rhonew-rhoold)/fabs(rhonew) > 1.e-12) &&
              (fabs(rhonew - rhoold) > 1.e-12)
            );
      if (count==1)
          printf("Fast rescale! %g %g %g %g\n", my_psi4, my_psi4,
                 mom_source[0][i3D]/my_psi4/
                  (source[i3D]/my_psi4/my_psi4+TOV_K*
                  pow(rhonew, TOV_Gamma)),
                 mom_source[0][i3D]/my_psi4/
                  (source[i3D]/my_psi4/my_psi4+TOV_K*
                  pow(rhonew, TOV_Gamma)));
#else
      newton_raphson(&vx,&vy,&vz,&rhoold,&rhonew,&w_lorentz_2,&v_2,
                     TOV_Gamma, TOV_K,
                     source[i3D],
                     mom_source[0][i3D],mom_source[1][i3D],mom_source[2][i3D],
                     my_psi4, x[i3D], y[i3D], z[i3D], rho[i3D]);
#endif
      rho[i3D]   = rhonew;
      press[i3D] = TOV_K * pow(rhonew, TOV_Gamma);
      eps[i3D]   = TOV_K * pow(rhonew, TOV_Gamma - 1.0) /
                              (TOV_Gamma - 1.0);
      w_lorentz[i3D] = sqrt(w_lorentz_2);
      vel0[i3D] = vx;
      vel1[i3D] = vy;
      vel2[i3D] = vz;

      if (debug && (fabs(z[i3D])<1.e-15))
      {
        fprintf(debugfile,
                "%.15g %.15g %.15g %.15g\n", /*7*/
                gxx[i3D]*gxx[i3D]*( /* ham - source*/
                  w_lorentz_2*rho[i3D]+
                  (w_lorentz_2*TOV_Gamma/(TOV_Gamma-1.)-1.)*press[i3D]),
                gxx[i3D]*gxx[i3D]*( /* ham - source */
                  w_lorentz_2*(rho[i3D]*(1.+eps[i3D])+press[i3D])-press[i3D]),
                (w_lorentz_2 * rho[i3D] + /* momx - source */
                 w_lorentz_2*TOV_Gamma/(TOV_Gamma-1.) * press[i3D])*
                 gxx[i3D]*vel0[i3D],
                w_lorentz_2 * (rho[i3D]* /* momx - source */
                 (1.+eps[i3D])+ press[i3D])*
                 gxx[i3D]*vel0[i3D]
                );
      }

    }
  }
  if (debug)
    fclose(debugfile);

  switch(*(const CCTK_INT*)CCTK_ParameterGet("TOV_Populate_Timelevels",
                                             "TOVSolver", &type))
  {
    case 3:
        TOV_Copy(size, rho_p_p,  rho);
        TOV_Copy(size, press_p_p,press);
        TOV_Copy(size, eps_p_p,  eps);
        TOV_Copy(size, vel0_p_p, vel0);
        TOV_Copy(size, vel1_p_p, vel1);
        TOV_Copy(size, vel2_p_p, vel2);
        TOV_Copy(size, w_lorentz_p_p, w_lorentz);
    case 2:
        TOV_Copy(size, rho_p,  rho);
        TOV_Copy(size, press_p,press);
        TOV_Copy(size, eps_p,  eps);
        TOV_Copy(size, vel0_p, vel0);
        TOV_Copy(size, vel1_p, vel1);
        TOV_Copy(size, vel2_p, vel2);
        TOV_Copy(size, w_lorentz_p, w_lorentz);
  }
  free(source);
  return 1;
}

void TOV_write_1D_datafile(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_ARGUMENTS
  DECLARE_CCTK_PARAMETERS
  int i;

  if (TOV_Num_TOVs > 1)
    CCTK_WARN(0, "Writing a data file for multiple stars is not (yet) "
                 "supported");
  FILE *file;
  file = fopen(TOV_save_to_datafile, "w");
  fprintf(file, "TOVSolver data file\n");
  fprintf(file, "version 1.0\n");
  fprintf(file, "TOV_Num_Radial %d\n", (int)TOV_Num_Radial);
  fprintf(file, "\n");
  for (i=0; i < TOV_Num_Radial; i++)
  {
      fprintf(file, "%g %g %g %g %g\n",
              TOV_rbar_1d[i],
              TOV_r_1d[i],
              TOV_m_1d[i],
              TOV_phi_1d[i],
              TOV_press_1d[i]);
  }
  fclose(file);
  CCTK_WARN(0, "Simulation stopped as requested after writing data to file");
}

void TOVSolverC_export_local_variables(CCTK_REAL **exported_TOV_press_1d, CCTK_REAL **exported_TOV_m_1d, CCTK_REAL **exported_TOV_phi_1d, CCTK_REAL **exported_TOV_rbar_1d, CCTK_REAL **exported_TOV_r_1d)
{
  *exported_TOV_press_1d = TOV_press_1d;
  *exported_TOV_m_1d     = TOV_m_1d;
  *exported_TOV_phi_1d   = TOV_phi_1d;
  *exported_TOV_rbar_1d  = TOV_rbar_1d;
  *exported_TOV_r_1d     = TOV_r_1d;
}


```

make.code.defn:
```
# Main make.code.defn file for thorn TOVSolverC

# Source files in this directory
SRCS = tov.c

# Subdirectories containing source files
SUBDIRS = 


```

utils.inc:
```

void TOV_C_AllocateMemory(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_ARGUMENTS;
  DECLARE_CCTK_PARAMETERS;

  assert(TOV_Surface==0);
  assert(TOV_R_Surface==0);
  assert(TOV_RProp_Surface==0);

  assert(TOV_r_1d==0);
  assert(TOV_rbar_1d==0);
  assert(TOV_press_1d==0);
  assert(TOV_phi_1d==0);
  assert(TOV_m_1d==0);
  assert(TOV_mbary_1d==0);
  assert(TOV_rprop_1d==0);

  TOV_Surface = malloc(TOV_Num_TOVs * sizeof(CCTK_REAL));
  TOV_R_Surface = malloc(TOV_Num_TOVs * sizeof(CCTK_REAL));
  TOV_RProp_Surface = malloc(TOV_Num_TOVs * sizeof(CCTK_REAL));

  TOV_r_1d = malloc(TOV_Num_Radial * TOV_Num_TOVs * sizeof(CCTK_REAL));
  TOV_rbar_1d = malloc(TOV_Num_Radial * TOV_Num_TOVs * sizeof(CCTK_REAL));
  TOV_press_1d = malloc(TOV_Num_Radial * TOV_Num_TOVs * sizeof(CCTK_REAL));
  TOV_phi_1d = malloc(TOV_Num_Radial * TOV_Num_TOVs * sizeof(CCTK_REAL));
  TOV_m_1d = malloc(TOV_Num_Radial * TOV_Num_TOVs * sizeof(CCTK_REAL));
  TOV_mbary_1d = malloc(TOV_Num_Radial * TOV_Num_TOVs * sizeof(CCTK_REAL));
  TOV_rprop_1d = malloc(TOV_Num_Radial * TOV_Num_TOVs * sizeof(CCTK_REAL));
}

void TOV_C_FreeMemory (CCTK_ARGUMENTS)
{
  DECLARE_CCTK_ARGUMENTS;
  DECLARE_CCTK_PARAMETERS;

  assert(TOV_Surface!=0);
  assert(TOV_R_Surface!=0);
  assert(TOV_RProp_Surface!=0);

  assert(TOV_r_1d!=0);
  assert(TOV_rbar_1d!=0);
  assert(TOV_press_1d!=0);
  assert(TOV_phi_1d!=0);
  assert(TOV_m_1d!=0);

  free(TOV_Surface);
  free(TOV_R_Surface);
  free(TOV_RProp_Surface);

  free(TOV_r_1d);
  free(TOV_rbar_1d);
  free(TOV_press_1d);
  free(TOV_phi_1d);
  free(TOV_m_1d);
  free(TOV_mbary_1d);
  free(TOV_rprop_1d);

  TOV_Surface=0;
  TOV_R_Surface=0;
  TOV_RProp_Surface=0;

  TOV_r_1d=0;
  TOV_rbar_1d=0;
  TOV_press_1d=0;
  TOV_phi_1d=0;
  TOV_m_1d=0;
  TOV_mbary_1d=0;
  TOV_rprop_1d=0;
}

/* - utility routine
   - fills an real-array 'var' of size 'i' with value 'r' */
void TOV_C_fill(CCTK_REAL *var, CCTK_INT i, CCTK_REAL r);
void TOV_C_fill(CCTK_REAL *var, CCTK_INT i, CCTK_REAL r)
{
  for (i-- ;i >= 0; i--)
    var[i]=r;
}

void TOV_Copy(CCTK_INT size, CCTK_REAL *var_p, CCTK_REAL *var);
void TOV_Copy(CCTK_INT size, CCTK_REAL *var_p, CCTK_REAL *var)
{
#pragma omp parallel for
    for(int i=0; i<size; i++)
        var_p[i] = var[i];
}

```

make.code.deps:
```
tov.o: utils.inc external.inc

```

util/mode_spectrum.py:
```
#!/usr/bin/python
# -*- coding: utf-8 -*-

# © Frank Löffler <knarf@cct.lsu.edu>
# License: GPLv2+

# Compute power spectral density of given data
from plot_defaults import *
from matplotlib.mlab import detrend_linear

xlim = (0,10)
ylim = (-140,-100)

# load data
Fx, Fy = np.loadtxt("hydrobase::rho.maximum.asc.bz2", comments="#", usecols=(1,2), unpack=True)
dt = M_to_ms/(Fx[1]-Fx[0])

# slice data if wanted
start = 0.0
end   = 1.
Fx = Fx[int(start*len(Fx)): int(end*len(Fx))]
Fy = Fy[int(start*len(Fy)): int(end*len(Fy))]

# Plot basics
fig = plt.figure(figsize=(6, 3))
fig.subplots_adjust(top=0.92,bottom=0.16, left=0.11,right=0.98)
ax = fig.add_subplot(1,1,1)

# mode names and frequencies for non-rotating Gamma=2 K=100 TOV stars
modes = {
"F" : 1.44252691528028,
"H1": 3.95408396916149,
"H2": 5.91494894170517,
"H3": 7.77382493405710,
"H4": 9.58768293806276,
"H5": 11.3772129983097,
"H6": 13.1520241905666,
"H7": 14.9172321735655,
}

# plot modes as vertical lines and label them
for mode, freq in modes.items():
  ax.plot((freq,freq), ylim, 'b--')
  ax.text(freq-0.1, ylim[1]+1, mode, fontsize=fontsize)
#ax.plot((2*modes["F"], 2*modes["F"]), (-200,0), 'g--')
#ax.plot((3*modes["F"], 3*modes["F"]), (-200,0), 'g--')
#ax.plot((4*modes["F"], 4*modes["F"]), (-200,0), 'g--')

# plot PSD
frac = 0.5 * len(Fy)
ax.psd(Fy, NFFT=int(frac), pad_to=5*int(frac), noverlap=int(frac*0.5), detrend=detrend_linear, Fs=dt, linestyle='-', color='black', scale_by_freq=False)
#ax.psd(Fy, NFFT=int(len(Fy)*frac), pad_to=10*int(len(Fy)*frac), noverlap=int(len(Fy)*frac*0.50), detrend=detrend_linear, Fs=M_to_ms/(Fx[1]-Fx[0]), linestyle='-', marker='x', color='red')

# plot properties
ax.set_xlim(xlim)
ax.set_ylim(ylim) 

#plt.title('PSD', fontsize=fontsize)
ax.set_xlabel(r'$f$ [kHz]')
ax.set_ylabel(r'PSD [dB/Hz]')

ax.xaxis.set_major_locator(mticker.MaxNLocator(10))
ax.xaxis.set_minor_locator(mticker.MaxNLocator(20))
ax.xaxis.grid(False)
ax.yaxis.set_major_locator(mticker.MaxNLocator(5))
ax.yaxis.set_minor_locator(mticker.MaxNLocator(9))
ax.yaxis.grid(False)
set_tick_sizes(ax, 8, 4)


#plt.show()
plt.savefig('mode_spectrum.pdf')


```


combined_doc_context: ## README (README):
```
Cactus Code Thorn TOVSolver
Author(s)    : Ian Hawke
               Frank Löffler
               Luca Baiotti
               Christian David Ott
               Erik Schnetter
               Bruno Giacomazzo
Maintainer(s): Einstein Toolkit
Licence      : GPLv2+
--------------------------------------------------------------------------

1. Purpose

This thorn provides initial data for TOV star(s) in isotropic coordinates.


```

## Documentation (.tex files):
Files: documentation.tex

```tex
=== documentation.tex ===
% *======================================================================*
%  Cactus Thorn template for ThornGuide documentation
%  Author: Ian Kelley
%  Date: Sun Jun 02, 2002
%
%  Thorn documentation in the latex file doc/documentation.tex
%  will be included in ThornGuides built with the Cactus make system.
%  The scripts employed by the make system automatically include
%  pages about variables, parameters and scheduling parsed from the
%  relevant thorn CCL files.
%
%  This template contains guidelines which help to assure that your
%  documentation will be correctly added to ThornGuides. More
%  information is available in the Cactus UsersGuide.
%
%  Guidelines:
%   - Do not change anything before the line
%       % START CACTUS THORNGUIDE",
%     except for filling in the title, author, date, etc. fields.
%        - Each of these fields should only be on ONE line.
%        - Author names should be separated with a \\ or a comma.
%   - You can define your own macros, but they must appear after
%     the START CACTUS THORNGUIDE line, and must not redefine standard
%     latex commands.
%   - To avoid name clashes with other thorns, 'labels', 'citations',
%     'references', and 'image' names should conform to the following
%     convention:
%       ARRANGEMENT_THORN_LABEL
%     For example, an image wave.eps in the arrangement CactusWave and
%     thorn WaveToyC should be renamed to CactusWave_WaveToyC_wave.eps
%   - Graphics should only be included using the graphicx package.
%     More specifically, with the "\includegraphics" command.  Do
%     not specify any graphic file extensions in your .tex file. This
%     will allow us to create a PDF version of the ThornGuide
%     via pdflatex.
%   - References should be included with the latex "\bibitem" command.
%   - Use \begin{abstract}...\end{abstract} instead of \abstract{...}
%   - Do not use \appendix, instead include any appendices you need as
%     standard sections.
%   - For the benefit of our Perl scripts, and for future extensions,
%     please use simple latex.
%
% *======================================================================*
%
% Example of including a graphic image:
%    \begin{figure}[ht]
% 	\begin{center}
%    	   \includegraphics[width=6cm]{MyArrangement_MyThorn_MyFigure}
% 	\end{center}
% 	\caption{Illustration of this and that}
% 	\label{MyArrangement_MyThorn_MyLabel}
%    \end{figure}
%
% Example of using a label:
%   \label{MyArrangement_MyThorn_MyLabel}
%
% Example of a citation:
%    \cite{MyArrangement_MyThorn_Author99}
%
% Example of including a reference
%   \bibitem{MyArrangement_MyThorn_Author99}
%   {J. Author, {\em The Title of the Book, Journal, or periodical}, 1 (1999),
%   1--16. {\tt http://www.nowhere.com/}}
%
% *======================================================================*

% If you are using CVS use this line to give version information


\documentclass{article}


% Use the Cactus ThornGuide style file
% (Automatically used from Cactus distribution, if you have a
%  thorn without the Cactus Flesh download this from the Cactus
%  homepage at www.cactuscode.org)
\usepackage{../../../../doc/latex/cactus}


\begin{document}

% The author of the documentation
\author{I.~Hawke,\\
 F.~Loeffler \textless loeffler@sissa.it \textgreater } 
%A.~Nagar \textless alessandro.nagar@polito.it\textgreater}

% The title of the document (not necessarily the name of the Thorn)
\title{TOVSolver}

% the date your document was last changed, if your document is in CVS,
% please use:
%    \date{$ $Date: 2009-09-17 15:39:33 -0500 (Thu, 17 Sep 2009) $ $}

\date{\today}

\maketitle

% Do not delete next line
% START CACTUS THORNGUIDE

% Add all definitions used in this documentation here
%   \def\mydef etc

\begin{abstract}
  This thorn solves the Tolman-Oppenheimer-Volkov equations of hydrostatic equilibrium
  for a spherically symmetric static star. 
\end{abstract}


%---------------------
\section{Introduction}
\label{sec:intro}
%---------------------

The Tolman-Oppenheimer-Volkoff solution is a static perfect fluid
``star''. It is frequently used as a test of relativistic hydro
codes. Here it is intended for use without evolving the matter
terms. This provides a compact strong field solution which is static
but does not contain singularities.


%------------------
\section{Equations}
\label{sec:eqn}
%------------------

The equations for a TOV star~\cite{Tolman39,OppVol39,mtw} are usually
derived in Schwarzschild coordinates. In these coordinates, the metric can
be brought into the form
\begin{equation}
ds^2 = -e^{2\phi}dt^2 + \left(1-\dfrac{2m}{r}\right)^{-1}dr^2 + r^2 d\Omega^2 \ .
\end{equation}


This thorn is based on the notes of Thomas Baumgarte~\cite{Baumgarte-file} that 
have been partially included in this documentation. However the notation for the
fluid quantities follows~\cite{Font00a}. 
Here we are assuming that the stress energy tensor is given by
\begin{equation}
  \label{eq:Tmunu}
  T^{\mu\nu} = (\mu + P)u^{\mu}u^{\nu} + Pg^{\mu\nu},
\end{equation}
where $\mu$ is the total energy, $P$ the pressure, $u^{\mu}$ the fluid
four velocity, $\rho$ the rest-mass density, $\epsilon$ the specific
internal energy, and
\begin{eqnarray}
  \label{eq:fluidquantities}
  \mu & = & \rho (1 + \epsilon), \\
  P & = & (\Gamma - 1)\rho\epsilon, \\
  P & = & K \rho^{\Gamma}.
\end{eqnarray}
This enforces a polytropic equation of state. We note that in Cactus
the units are $c = G = M_{\odot} = 1$.

The equations to give the initial data are solved (as usual) in the
Schwarzschild-like coordinates with the areal radius labelled $r$.
The equations of the relativistic hydrostatic equilibrium are
\begin{eqnarray}
  \label{eq:TOViso}
  \frac{d P}{d r} & = & -(\mu + P) \frac{m + 4\pi r^3 P}{r(r - 2m)}, \\
%
  \frac{d m}{d r} & = & 4 \pi r^2 \mu, \\
%
  \frac{d \phi}{d r} & = & \frac{m + 4\pi r^3 P}{r(r -
    2m)} \ . \\
\end{eqnarray}
Here $m$ is the gravitational mass inside the sphere radius $r$, and
$\phi$ the logarithm of the lapse. Once the integration is done for the 
interior of the star we match to the exterior (see below). In the exterior 
we have
\begin{align}
  \label{eq:TOVexterior}
     P & =  {\tt TOV\_atmosphere}, \\
     m & =  M, \\
  \phi & = \dfrac{1}{2} \log(1-2M / r).
\end{align}

In order to impose initial data in cartesian coordinates, we want to transform
this solution to isotropic coordinates, in which the metric takes the form
\begin{equation}
\label{eq:metr_iso}
ds^2 = -e^{2\phi}dt^2+e^{2\Lambda}\left(d\bar{r}^2+\bar{r}^2d\Omega^2\right) \ .
\end{equation}
Here $\bar{r}$ denotes the isotropic radius. Matching the two metrics, one
obviously finds
\begin{align}
r^2                                    &= e^{2\Lambda}\bar{r}^2 \ , \\
\left(1-\dfrac{2m}{r}\right)^{-1} dr^2 &= e^{2\Lambda}d\bar{r}^2 \ .
\end{align}
As a result, we have an additional differential equation to solve in order
to have $\bar{r}(r)$, that is
\begin{equation}
\label{eq:rbar}
\frac{d (\log(\bar{r} / r))}{\partial r} =  \frac{r^{1/2} - (r-2m)^{1/2}}{r(r-2m)^{1/2}} \ .
\end{equation}
Given such a solution, the missing metric potential is simply given by
\begin{equation}
e^{\Lambda} = \dfrac{r}{\bar{r}} \ .
\end{equation}
In the following section we concentrate on solving Eq.~(\ref{eq:rbar}) in the
exterior and in the interior of the star.

Then, given these one-dimensional data we interpolate to get data on 
the three-dimensional Cactus grid; that is, we interpolate on the three dimensional 
{\tt r} given by the {\tt x, y, z} variables the physical hydro and spacetime
quantities that are function of the isotropic radius $\bar{r}$ computed above. 
Only linear interpolation is used. This avoids problems at the surface of the 
star, and does not cause problems if the number of points in the one dimensional 
array is sufficient ($1\times 10^5$ is the default, which should be sufficient for 
medium-sized grids).

%--------------------
\subsection{Exterior}
\label{sbsc:exterior}
%--------------------

In the exterior of the star, $r>R$, the mass $M\equiv m(R)$ is constant, and 
Eq.~(\ref{eq:rbar}) can be solved analytically up to a constant of integration. 
Fixing this constant such that $r$ and $\bar{r}$ agree at infinity, we find
\begin{equation}
\bar{r} = \dfrac{1}{2}\left(\sqrt{r^2-2Mr}+r -M\right) \ ,
\end{equation}
or, solving for $r$ [cfr. Exercise 31.7 of MTW~\cite{mtw}]
\begin{equation}
r=\bar{r}\left(1+\dfrac{M}{2\bar{r}}\right)^2 \ .
\end{equation}
The metric potential as a function of $\bar{r}$ is obviously
\begin{equation}
e^{2\Lambda} = \left(1+\dfrac{M}{2\bar{r}}\right)^2 \ .
\end{equation}

%--------------------
\subsection{Interior}
\label{sbsc:interior}
%--------------------

In the interior, Eq,~(\ref{eq:rbar}) can not be integrated analytically, because
$m$ is now a function of $r$. Instead, we have to integrate
\begin{equation}
\int_0^{\bar{r}} \dfrac{d\bar{r}}{\bar{r}} = \int_0^r\left(1-\dfrac{2m}{r}\right)^{-1/2}\dfrac{dr}{r} \ .
\end{equation}
The left hand side can be integrated analytically, and has a singular point at
$\bar{r}=0$. The right hand side cannot be integrated analytically, but will also
be singular at $r=0$, which poses problems when trying to integrate the equation
numerically. We therefore rewrite the right hand side by adding and subracting
a term $1/r$, which yields
\begin{equation}
\int_0^r\dfrac{1}{r(1-2m/r)^{1/2}}dr = \int_0^r\dfrac{1-(1-2m/r)^{1/2}}{r(1-2m/r)^{1/2}}dr+\int_0^r\dfrac{dr}{r} \ .
\end{equation}
Since $m\sim r^3$ close to the origin, the first term on the right hand side is now
regular and the second one can be integrated analytically. As a result, we find
\begin{equation}
\int_0^{\bar{r}}d\ln\bar{r}-\int_0^rd\ln r=\int_0^r\dfrac{1-(1-2m/r)^{1/2}}{r(1-2m/r)^{1/2}}dr \ .
\end{equation}
Replacing the lower limits ($r=\bar{r}=0$) temporarily with $r_0$ and $\bar{r}_0$, we can integrate
the right hand side and find
\begin{equation}
\ln\left(\dfrac{\bar{r}}{r}\right)-\ln\left(\dfrac{\bar{r}_0}{r_0}\right)=\int_0^r\dfrac{1-(1-2m/r)^{1/2}}{r(1-2m/r)^{1/2}}dr \ ,
\end{equation}
or
\begin{equation}
\bar{r} = C r \exp\left[\int_0^r\dfrac{1-(1-2m/r)^{1/2}}{r(1-2m/r)^{1/2}}dr\right] \ .
\end{equation}
Here the constant of integration $C$ is related to the ratio $\bar{r}_0/r_0$ evaluated at
the origin (which is perfectly regular). It can be chosen such that the interior solution
matches the exterior solution at the surface of the star. This requirement implies
\begin{equation}
C = \dfrac{1}{2R}\left(\sqrt{R^2-2MR}+R-M\right)\exp\left[\int_0^R\dfrac{1-(1-2m/r)^{1/2}}{r(1-2m/r)^{1/2}}dr\right] \ .
\end{equation}
In this respect, let us recall how we do initial data for the system of 
equations at $r=0$.  Given a value of the central density $\rho_c$ (in
Cactus units) we pose $P_0 = K\rho_c^{\Gamma}$, $m_0=0$, $\phi_0=0$ and 
$\bar{r}_0 = {\tt TOV\_Tiny}$, $r_0 = {\tt TOV\_Tiny}$. The {\tt TOV\_Tiny} 
number is hardwired into the code to avoid divide by zero errors; it is $10^{-20}$. 
Also the default parameters will give the TOV star used for the long term evolutions
in~\cite{Font02a}. That is, a nonrotating $N=1$ ($\gamma=1+1/N=2$) polytropic star 
with gravitational mass $M=1.4M_{\odot}$, circumferential radius $R=14.15$km, central 
rest-mass density $\rho_c=1.28 \times 10^{-3}$ and $K=100$.


%--------------------------
\section{Use of this thorn}
\label{sec:use}
%--------------------------

To use this thorn to provide initial data for the {\tt ADMBase}
variables $\alpha$, $\beta$, $g$ and $K$ just activate the thorn and
set {\tt ADMBase:initial\_data = ``TOV''}.

There are two ways of coupling the matter sources to the thorn that
evolves the Einstein equations. One is to use the {\tt CalcTmunu}
interface. This will give the components of the stress energy tensor
\emph{pointwise} across the grid. For an example of this, see thorn
{\tt ADM} in {\tt CactusEinstein}.

To use the {\tt CalcTmunu} interface you should
\begin{itemize}
\item put the lines
\begin{verbatim}
friend: ADMCoupling

USES INCLUDE: CalcTmunu.inc
USES INCLUDE: CalcTmunu_temps.inc
USES INCLUDE: CalcTmunu_rfr.inc
\end{verbatim}
in your {\tt interface.ccl}
\item In any routine requiring the matter terms, put 
  \begin{itemize}
  \item {\tt \#include ``CalcTmunu\_temps.inc''} in the variable
    declarations
  \item declare {\tt CCTK\_REAL}s {\tt Ttt, Ttx, Tty, Ttz, Txx, Txy,
      Txz, Tyy, Tyz, Tzz}.
  \item Inside an {\tt i,j,k} loop put {\tt \#include
      ``CalcTmunu.inc''}. {\bf This must be a Fortran routine} (We
    could probably fix this if requested).
  \end{itemize}
\item You then use the real numbers {\tt Ttt} etc.~as the stress
  energy tensor at a point.
\end{itemize}

As an alternative you can use the grid functions {\tt StressEnergytt,
  StressEnergytx}, etc.~directly to have the stress energy tensor over
the entire grid. To do this you just need the line {\tt friend:
  ADMCoupling} in your {\tt interface.ccl}. Although this seems much
simpler, you will now \emph{only} get the contributions from the {\tt
  TOVSolver} thorn. If you want to use other matter sources, most of
the current thorns ({\tt CosmologicalConstant}, the hydro code, the
scalar field code) all use the {\tt CalcTmunu} interface.

You also have the possibility to use a parameter
{\tt GRHydrotovsolver::TOV\_Separation} to obtain a spacetime consisting
of one TOV-system for $x>0$ and a second (similar) for $x<0$. This parameter
sets the separation of the centers of two neutron stars, has to be positive
and should be larger than twice the radius of one star.\\
Be aware that the spacetime obtained by this is no physical spacetime and
no solution of Einsteins Equations and therefore an IVP-run has to follow.
This parameter was only introduced for testing purposes of the IVP-Solver
and should only be considered as such. There would be better (and also easy)
ways to obtain initial data for two TOVs than that.





\begin{thebibliography}{10}

\bibitem{Tolman39}
R.~C. Tolman, Phys. Rev. {\bf 55}, 364 (1939).
%
\bibitem{OppVol39}
J.~R. Oppenheimer and G. Volkoff, Physical Review {\bf 55}, 374 (1939).
%
\bibitem{mtw}
C.W.~Misner, K.S.~Thorn and J.A.~Wheeler, Gravitation (Freeman and co. NY, 1973).
%
\bibitem{Baumgarte-file}
T.~W. Baumgarte. There is a copy of his notes in this directory: \\
TOVSolver/doc.
%
\bibitem{Font00a}
J.~A. Font, M. Miller, W. Suen and M. Tobias, Phys. Rev. {\bf D61},
044011 (2000).
%
\bibitem{Font02a}
J.~A. Font, T. Goodale, S. Iyer, M. Miller, L. Rezzolla, E. Seidel,
N. Stergioulas, W. Suen and M. Tobias, Phys. Rev. {\bf D65},
084024 (2002).


\end{thebibliography}

% Do not delete next line
% END CACTUS THORNGUIDE


\end{document}

```



Reference answer:
src_code: /* file    tov.c
 * author  Frank Loeffler, converted from fortran thorn by Ian Hawke
 * date    2002/10/21
 * desc    TOV initial data
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>

#include <cctk.h>
#include <cctk_Arguments.h>
#include <cctk_Parameters.h>

#include "constants.h"

#include "tov.h"

#define NUMVARS 6

#define velx (&vel[0*cctk_lsh[0]*cctk_lsh[1]*cctk_lsh[2]])
#define vely (&vel[1*cctk_lsh[0]*cctk_lsh[1]*cctk_lsh[2]])
#define velz (&vel[2*cctk_lsh[0]*cctk_lsh[1]*cctk_lsh[2]])
#define velx_p (&vel_p[0*cctk_lsh[0]*cctk_lsh[1]*cctk_lsh[2]])
#define vely_p (&vel_p[1*cctk_lsh[0]*cctk_lsh[1]*cctk_lsh[2]])
#define velz_p (&vel_p[2*cctk_lsh[0]*cctk_lsh[1]*cctk_lsh[2]])
#define velx_p_p (&vel_p_p[0*cctk_lsh[0]*cctk_lsh[1]*cctk_lsh[2]])
#define vely_p_p (&vel_p_p[1*cctk_lsh[0]*cctk_lsh[1]*cctk_lsh[2]])
#define velz_p_p (&vel_p_p[2*cctk_lsh[0]*cctk_lsh[1]*cctk_lsh[2]])

#define sx (&scon[0*cctk_lsh[0]*cctk_lsh[1]*cctk_lsh[2]])
#define sy (&scon[1*cctk_lsh[0]*cctk_lsh[1]*cctk_lsh[2]])
#define sz (&scon[2*cctk_lsh[0]*cctk_lsh[1]*cctk_lsh[2]])
#define sx_p (&scon_p[0*cctk_lsh[0]*cctk_lsh[1]*cctk_lsh[2]])
#define sy_p (&scon_p[1*cctk_lsh[0]*cctk_lsh[1]*cctk_lsh[2]])
#define sz_p (&scon_p[2*cctk_lsh[0]*cctk_lsh[1]*cctk_lsh[2]])
#define sx_p_p (&scon_p_p[0*cctk_lsh[0]*cctk_lsh[1]*cctk_lsh[2]])
#define sy_p_p (&scon_p_p[1*cctk_lsh[0]*cctk_lsh[1]*cctk_lsh[2]])
#define sz_p_p (&scon_p_p[2*cctk_lsh[0]*cctk_lsh[1]*cctk_lsh[2]])

CCTK_REAL * TOV_Surface=0;
CCTK_REAL * TOV_R_Surface=0;
CCTK_REAL * TOV_RProp_Surface=0;

CCTK_REAL * TOV_r_1d=0;
CCTK_REAL * TOV_rbar_1d=0;
CCTK_REAL * TOV_press_1d=0;
CCTK_REAL * TOV_phi_1d=0;
CCTK_REAL * TOV_m_1d=0;
CCTK_REAL * TOV_mbary_1d=0;
CCTK_REAL * TOV_rprop_1d=0;

#include "utils.inc"

void TOV_C_ParamCheck(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_ARGUMENTS
  DECLARE_CCTK_PARAMETERS

    if (TOV_Solve_for_TOVs != 3) 
      {
	if (TOV_Solve_for_TOVs == 2)
	  {
	    CCTK_WARN(1, "TOV_Solve_for_TOVs is depreciated. "
                   "Use TOV_Enforce_Interpolation=\"yes\" instead.\n");
	    if (CCTK_ParameterSet("TOV_Enforce_Interpolation",
                            "TOVSolver",
                            "true"))
	      CCTK_WARN(0, "Error while steering this parameter.\n");
	    else
	      CCTK_WARN(1, "Steered this parameter for now accordingly.\n");
	  }
	else
	  CCTK_WARN(1, "TOV_Solve_for_TOVs is depreciated. "
                   "Use TOV_Enforce_Interpolation instead.\n");
      }
  if (TOV_ProperPosition)
  {
    if (TOV_Num_TOVs != 2)
      CCTK_WARN(0, "TOV_ProperPosition atm only works for TOV_Num_TOVs==2");
  }
}

/* centered differencing with one-sided differencing at the boundary */
#define DIFF_X(a) (((i==0)?(a[CCTK_GFINDEX3D(cctkGH, i+1, j, k)] - \
                            a[CCTK_GFINDEX3D(cctkGH, i  , j, k)]): \
                    (i==(cctk_lsh[0]-1))?                          \
                           (a[CCTK_GFINDEX3D(cctkGH, i  , j, k)] - \
                            a[CCTK_GFINDEX3D(cctkGH, i-1, j, k)]): \
                       0.5*(a[CCTK_GFINDEX3D(cctkGH, i+1, j, k)] - \
                            a[CCTK_GFINDEX3D(cctkGH, i-1, j, k)]))/\
                   CCTK_DELTA_SPACE(0))
#define DIFF_Y(a) (((j==0)?(a[CCTK_GFINDEX3D(cctkGH, i, j+1, k)] - \
                            a[CCTK_GFINDEX3D(cctkGH, i, j  , k)]): \
                    (j==(cctk_lsh[1]-1))?                          \
                           (a[CCTK_GFINDEX3D(cctkGH, i, j  , k)] - \
                            a[CCTK_GFINDEX3D(cctkGH, i, j-1, k)]): \
                       0.5*(a[CCTK_GFINDEX3D(cctkGH, i, j+1, k)] - \
                            a[CCTK_GFINDEX3D(cctkGH, i, j-1, k)]))/\
                    CCTK_DELTA_SPACE(1))
#define DIFF_Z(a) (((k==0)?(a[CCTK_GFINDEX3D(cctkGH, i, j, k+1)] - \
                            a[CCTK_GFINDEX3D(cctkGH, i, j, k  )]): \
                    (k==(cctk_lsh[2]-1))?                          \
                           (a[CCTK_GFINDEX3D(cctkGH, i, j, k  )] - \
                            a[CCTK_GFINDEX3D(cctkGH, i, j, k-1)]): \
                       0.5*(a[CCTK_GFINDEX3D(cctkGH, i, j, k+1)] - \
                            a[CCTK_GFINDEX3D(cctkGH, i, j, k-1)]))/\
                    CCTK_DELTA_SPACE(2))


/*@@
   @routine    TOV_Source_RHS
   @date       Thu Oct 24 14:30:00 2002
   @author     Frank Loeffler - converted fortran routine by Ian Hawke
   @desc
      The source terms for the ODEs. These are equations (2), (3), (4)
      and (18) from the Baumgarte notes.
      That is the vector in order is (P, m, phi, rbar).
   @enddesc
   @calls
   @calledby
   @history
   @endhistory
@@*/
void TOV_C_Source_RHS(CCTK_REAL r, CCTK_REAL K, CCTK_REAL Gamma,
                      CCTK_REAL old_data[NUMVARS], CCTK_REAL source_data[NUMVARS]);
void TOV_C_Source_RHS(CCTK_REAL r, CCTK_REAL K, CCTK_REAL Gamma,
                      CCTK_REAL old_data[NUMVARS], CCTK_REAL source_data[NUMVARS])
{
  CCTK_REAL LOCAL_TINY, PI;
  CCTK_REAL press, rho, eps, mu, m;
  CCTK_REAL r_minus_two_m;

  LOCAL_TINY = 1.0e-35;
  PI=4.0*atan(1.0);

  press           = old_data[0];
  if (press < LOCAL_TINY)
    press = LOCAL_TINY;
  m               = old_data[1];

  rho = pow(press / K, 1.0 / Gamma);
  eps = press / (Gamma - 1.0) / rho;
  mu  = rho * (1.0 + eps);

  r_minus_two_m = r - 2.0 * m;

  if ((r<=0.0) && (m<=0.0))
  {
    source_data[1] = 0.0;
    source_data[2] = 0.0;
    source_data[3] = 0.0;
    source_data[4] = 0.0;
    source_data[5] = 0.0;
  }
  else
  {
    source_data[2] = (m + 4*PI * r*r*r * press) / r_minus_two_m / r;
    /* source_data[0] = -(press + mu) * source_data[2]; */
    source_data[0] = -(press + mu) *
                     (m + 4*PI * r*r*r * press) / r_minus_two_m / r;
    source_data[1] = 4*PI * r*r * mu;
    source_data[3] = (sqrt(r) - sqrt(r_minus_two_m)) / r / sqrt(r_minus_two_m);
    source_data[5] = 1.0/sqrt(1.0-2.0*m/r);
    source_data[4] = source_data[5] * 4*PI * rho * r*r;
  }
}

/*@@
   @routine    TOV_Integrate_RHS
   @date       Thu Oct 24 14:30:00 2002
   @author     Frank Loeffler, converted fortran routine by Ian Hawke
   @desc
      Integrates the ODEs using RK4.
      We rescale at the end to match to a Schwarzschild exterior.
   @enddesc
   @calls
   @calledby
   @history
   @endhistory
@@*/
void TOV_C_Integrate_RHS(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_ARGUMENTS
  DECLARE_CCTK_PARAMETERS

  CCTK_REAL LOCAL_TINY;

  CCTK_INT star, star_i, i, TOV_Surface_Index;
  CCTK_REAL old_data[NUMVARS], source_data[NUMVARS],
            in_data[NUMVARS], new_data[NUMVARS],
            k1[NUMVARS], k2[NUMVARS], k3[NUMVARS], k4[NUMVARS];
  CCTK_REAL Surface_Mass, factor, local_rho;

  LOCAL_TINY = 1.0e-20;

  assert(TOV_Surface!=0);
  assert(TOV_R_Surface!=0);
  assert(TOV_RProp_Surface!=0);

  assert(TOV_r_1d!=0);
  assert(TOV_rbar_1d!=0);
  assert(TOV_press_1d!=0);
  assert(TOV_phi_1d!=0);
  assert(TOV_m_1d!=0);
  assert(TOV_mbary_1d!=0);
  assert(TOV_rprop_1d!=0);

  /* do it for all stars */
  for (star=0; star < TOV_Num_TOVs; star++)
  {
    /* remember array index */
    star_i = star * TOV_Num_Radial;
    const CCTK_REAL rho_central=TOV_Rho_Central[star];

    /* Set conformal state like set in parameter file if we do not use
     * the old initial data. In this case we have to use what we get */
    if (!TOV_Use_Old_Initial_Data)
      if(CCTK_EQUALS(metric_type, "static conformal"))
      {
        *conformal_state=1;
        if (CCTK_EQUALS(conformal_storage,"factor+derivs"))
          *conformal_state = 2;
        else if (CCTK_EQUALS(conformal_storage,"factor+derivs+2nd derivs"))
          *conformal_state = 3;
        CCTK_VInfo(CCTK_THORNSTRING, "conformal_state set to %d",
                   (int)*conformal_state);
      }

    /* clear arrays first */
    TOV_C_fill(&(TOV_press_1d[star_i]), TOV_Num_Radial, 0.0);
    TOV_C_fill(&(TOV_m_1d    [star_i]), TOV_Num_Radial, 0.0);
    TOV_C_fill(&(TOV_phi_1d  [star_i]), TOV_Num_Radial, 0.0);
    TOV_C_fill(&(TOV_rbar_1d [star_i]), TOV_Num_Radial, 0.0);
    TOV_C_fill(&(TOV_r_1d    [star_i]), TOV_Num_Radial, 0.0);
    TOV_C_fill(&(TOV_mbary_1d[star_i]), TOV_Num_Radial, 0.0);
    TOV_C_fill(&(TOV_rprop_1d[star_i]), TOV_Num_Radial, 0.0);

    /* set start values */
    TOV_press_1d[star_i] = TOV_K *
                            pow(rho_central, TOV_Gamma);
    /* TOV_r_1d    [star_i] = LOCAL_TINY;
    TOV_rbar_1d [star_i] = LOCAL_TINY;*/

    /* build TOV_r_1d[] */
    for (i=star_i+1; i < star_i+TOV_Num_Radial; i++)
      TOV_r_1d[i] = TOV_r_1d[i-1] + TOV_dr[star];

    TOV_Surface[star] = -1.0;
    TOV_Surface_Index = -1.0;

#define RKLOOP for (int rk=0; rk<NUMVARS; rk++)
    /* loop over all radii */
    for (i=star_i; (i < star_i+TOV_Num_Radial-1) &&
                            (TOV_Surface[star] < 0.0); i++)
    {
      /* set up RK arrays */
      old_data[0] = TOV_press_1d[i];
      old_data[1] = TOV_m_1d[i];
      old_data[2] = TOV_phi_1d[i];
      if (fabs(TOV_rbar_1d[i] - TOV_r_1d[i]) < LOCAL_TINY)
        old_data[3] = 0.0;
      else
        old_data[3] = log(TOV_rbar_1d[i] / TOV_r_1d[i]);
      old_data[4] = TOV_mbary_1d[i];
      old_data[5] = TOV_rprop_1d[i];

      /* usual RK4 */
      RKLOOP in_data[rk] = old_data[rk];
      TOV_C_fill(source_data, 6, 0.0);

      TOV_C_Source_RHS(TOV_r_1d[i],
                     TOV_K, TOV_Gamma,
                     in_data, source_data);

      RKLOOP k1[rk] = TOV_dr[star] * source_data[rk];
      RKLOOP in_data[rk] = old_data[rk] + 0.5 * k1[rk];
      TOV_C_Source_RHS(TOV_r_1d[i]+ 0.5 * TOV_dr[star],
                       TOV_K, TOV_Gamma,
                       in_data, source_data);

      RKLOOP k2[rk] = TOV_dr[star] * source_data[rk];
      RKLOOP in_data[rk] = old_data[rk] + 0.5 * k2[rk];
      TOV_C_Source_RHS(TOV_r_1d[i]+ 0.5 * TOV_dr[star],
                       TOV_K, TOV_Gamma,
                       in_data, source_data);

      RKLOOP k3[rk] = TOV_dr[star] * source_data[rk];
      RKLOOP in_data[rk] = old_data[rk] + k3[rk];
      TOV_C_Source_RHS(TOV_r_1d[i]+ TOV_dr[star],
                       TOV_K, TOV_Gamma,
                       in_data, source_data);
      RKLOOP k4[rk] = TOV_dr[star] * source_data[rk];
      RKLOOP new_data[rk] = old_data[rk] + (k1[rk] + k4[rk] + 2.0 * (k2[rk] + k3[rk])) /6.0;

      TOV_press_1d[i+1] = new_data[0];
      TOV_m_1d    [i+1] = new_data[1];
      TOV_phi_1d  [i+1] = new_data[2];
      TOV_rbar_1d [i+1] = TOV_r_1d[i+1] * exp(new_data[3]);
      TOV_mbary_1d[i+1] = new_data[4];
      TOV_rprop_1d[i+1] = new_data[5];

      /* otherwise the code crashes later */
      if (TOV_press_1d[i+1] < 0.0)
          TOV_press_1d[i+1] = 0.0;

      local_rho = pow(TOV_press_1d[i+1] / TOV_K, 1.0 / TOV_Gamma);

      /* scan for the surface */
      if ( (local_rho <= 0.0) ||
           (TOV_press_1d[i+1] <= 0.0) )
      {
        TOV_Surface[star]   = TOV_r_1d[i];
        TOV_R_Surface[star] = TOV_rbar_1d[i];
        TOV_RProp_Surface[star] = TOV_rprop_1d[i];
        TOV_Surface_Index = i;
      }
    }
    if (TOV_Surface[star] < 0.0)
      CCTK_WARN(0, "Did not integrate out to surface of the star! "
                   "Increase TOV_dr or TOV_Num_Radial and rerun");

    Surface_Mass = TOV_m_1d[TOV_Surface_Index];
    factor = 0.5 * (sqrt(TOV_Surface[star] *
                         (TOV_Surface[star] - 2.00 * Surface_Mass)) +
                    TOV_Surface[star] - Surface_Mass) /
                    TOV_rbar_1d[TOV_Surface_Index];

    TOV_R_Surface[star] *= factor;
    for (i=star_i; i < star_i+TOV_Num_Radial; i++)
    {
      TOV_rbar_1d[i] *= factor;
      TOV_phi_1d[i]  -= TOV_phi_1d[TOV_Surface_Index] -
                        0.5 * log(1.0 - 2.0 * Surface_Mass / TOV_Surface[star]);
      /* match to Schwarzschield */
      if (i > TOV_Surface_Index)
      {
        TOV_press_1d[i] = 0.0;
        TOV_rbar_1d [i] = 0.5 *
                          (sqrt(TOV_r_1d[i]*(TOV_r_1d[i] - 2.0*Surface_Mass)) +
                          TOV_r_1d[i] - Surface_Mass);
        TOV_m_1d[i]     = Surface_Mass;
        TOV_phi_1d[i]   = 0.5 * log( 1.0 - 2.0 * Surface_Mass / TOV_r_1d[i]);
        TOV_mbary_1d[i] = TOV_mbary_1d[TOV_Surface_Index];
      }
    }
  }
  CCTK_INFO("Integrated TOV equation");
  /* do some info */
  CCTK_VInfo(CCTK_THORNSTRING, "Information about the TOVs used:");
  CCTK_VInfo("", "TOV    radius    mass  bary_mass mass(g) cent.rho rho(cgi)        K   K(cgi)    Gamma");
  for (i=0; i<TOV_Num_TOVs; i++)
    if (fabs(TOV_Gamma - 2.0) < LOCAL_TINY)
      CCTK_VInfo("","  %d  %8g %8g %8g %8.3g %8g %8.3g %8g %8.3g %8g",
                 (int)i+1, TOV_R_Surface[i],
                 TOV_m_1d[(i+1)*TOV_Num_Radial-1],
                 TOV_mbary_1d[(i+1)*TOV_Num_Radial-1],
                 TOV_m_1d[(i+1)*TOV_Num_Radial-1]*CONSTANT_Msolar_cgi,
                 TOV_Rho_Central[i],
                 TOV_Rho_Central[i]/pow(CONSTANT_G_cgi,3.0)/
                                    pow(CONSTANT_Msolar_cgi,2.0)*
                                    pow(CONSTANT_c_cgi,6.0),
                 TOV_K,
                 TOV_K*pow(CONSTANT_G_cgi,3.0)*
                          pow(CONSTANT_Msolar_cgi,2.0)/
                          pow(CONSTANT_c_cgi,4.0),
                 TOV_Gamma);
    else
      CCTK_VInfo("","  %d  %8g %8g %8.3g %8g %8.3g %8g %8g",
                 (int)i+1, TOV_R_Surface[i],
                 TOV_m_1d[(i+1)*TOV_Num_Radial-1],
                 TOV_m_1d[(i+1)*TOV_Num_Radial-1]*CONSTANT_Msolar_cgi,
                 TOV_Rho_Central[i],
                 TOV_Rho_Central[i]/pow(CONSTANT_G_cgi,3.0)/
                                    pow(CONSTANT_Msolar_cgi,2.0)*
                                    pow(CONSTANT_c_cgi,6.0),
                 TOV_K, TOV_Gamma);

}

/*----------------------------------------------------------------------------*/

/* utility routine
 * recursive search-routine for arrays
 * here used to look for the last index in an ordered array with its
 * value < goal
 */
CCTK_INT TOV_C_find_index(CCTK_INT   array_size,
                          CCTK_REAL *array,
                          CCTK_REAL  goal,
                          CCTK_INT   lower_index,
                          CCTK_INT   upper_index);
CCTK_INT TOV_C_find_index(CCTK_INT   array_size,
                          CCTK_REAL *array,
                          CCTK_REAL  goal,
                          CCTK_INT   lower_index,
                          CCTK_INT   upper_index)
{
  CCTK_INT middle_index;

  if (lower_index >= (upper_index-1))
      return lower_index;

  middle_index = (lower_index + upper_index) /2;

  if (array[middle_index] < goal)
    return TOV_C_find_index(array_size, array, goal, middle_index, upper_index);
  else
    return TOV_C_find_index(array_size, array, goal, lower_index, middle_index);
}

/* utility rountine
 * interpolates from (thorn-internal) 1D-data to Cactus 3D-grid */
/* input is all but *press_point *phi_point and *r_point */
void TOV_C_interp_tov_isotropic(
                                CCTK_INT  star,
                                CCTK_REAL *TOV_press_1d_local,
                                CCTK_REAL *TOV_phi_1d_local,
                                CCTK_REAL *TOV_rbar_1d_local,
                                CCTK_REAL *TOV_r_1d_local,
                                CCTK_REAL *r,
                                CCTK_REAL surface,
                                CCTK_REAL *press_point,
                                CCTK_REAL *phi_point,
                                CCTK_REAL *r_point);
void TOV_C_interp_tov_isotropic(
                                CCTK_INT  star,
                                CCTK_REAL *TOV_press_1d_local,
                                CCTK_REAL *TOV_phi_1d_local,
                                CCTK_REAL *TOV_rbar_1d_local,
                                CCTK_REAL *TOV_r_1d_local,
                                CCTK_REAL *r,
                                CCTK_REAL surface,
                                CCTK_REAL *press_point,
                                CCTK_REAL *phi_point,
                                CCTK_REAL *r_point)
{
  DECLARE_CCTK_PARAMETERS
  CCTK_INT  left_index;
  CCTK_REAL h, M;

  if (*r < 0.0)
    CCTK_WARN(0, "Negative radius found");
  if (*r < TOV_rbar_1d_local[1])
    *r=TOV_rbar_1d_local[1];
  if (*r > TOV_rbar_1d_local[TOV_Num_Radial-2])
    {
	{
	  *press_point= 0.0;
	  M = 0.5 * TOV_r_1d_local[TOV_Num_Radial-1] *
	    (1.0 - exp(2.0*TOV_phi_1d_local[TOV_Num_Radial-1]));
	  *r_point=(2* *r+M)*(2* *r+M)*0.25/ *r;
	  *phi_point=0.5*log(1-2*M/ *r_point);
	  return;
	}
    }

  if (TOV_Fast_Interpolation)
    left_index = TOV_C_find_index(TOV_Num_Radial-1, TOV_rbar_1d_local, *r, 0,
                                  TOV_Num_Radial-1);
  else
  {
    left_index=0;
    while( (left_index < TOV_Num_Radial-2) &&
           (TOV_rbar_1d_local[left_index+1] < *r) )
      left_index++;
  }

  h = (*r - TOV_rbar_1d_local[left_index]) /
      (TOV_rbar_1d_local[left_index+1] - TOV_rbar_1d_local[left_index]);
  *r_point     = (1.0 - h) * TOV_r_1d_local[left_index] +
                        h  * TOV_r_1d_local[left_index+1];
  *phi_point   = (1.0 - h) * TOV_phi_1d_local[left_index] +
                        h  * TOV_phi_1d_local[left_index+1];
  if (*r_point < surface)
    *press_point = (1.0 - h) * TOV_press_1d_local[left_index] +
                          h  * TOV_press_1d_local[left_index+1];
  else
    *press_point = 0.0;
}

/*@@
   @routine    TOV_Exact
   @date       Thu Oct 24 14:30:00 2002
   @author     Frank Loeffler, converted fortran routine by Ian Hawke
   @desc
       Schedule routine for interpolation of 1D to 3D grid
   @enddesc
   @calls
   @calledby
   @history
   @endhistory
@@*/
void TOV_C_Exact(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_ARGUMENTS
  DECLARE_CCTK_PARAMETERS

  CCTK_REAL *press_point, *rho_point, *eps_point,
            *mu_point, *phi_point, *r_point;

  CCTK_INT  LSH_MAX_I;

  CCTK_INT i,j,k, i3D, star;
  CCTK_REAL *r_to_star;
  CCTK_REAL g_diag, max_g_diag, max_rho;
  CCTK_REAL my_velx, my_vely, my_velz, my_psi4;
  CCTK_REAL PI, local_tiny;

  CCTK_INT tov_lapse, tov_shift;

  tov_lapse = CCTK_EQUALS(initial_lapse, "tov");
  tov_shift = CCTK_EQUALS(initial_shift, "tov");

  PI=4.0*atan(1.0);
  local_tiny=1.0e-14;
  /* remember index of last member of array */
  LSH_MAX_I = CCTK_GFINDEX3D(cctkGH,
                             cctk_lsh[0]-1, cctk_lsh[1]-1, cctk_lsh[2]-1);

  assert(TOV_Surface!=0);
  assert(TOV_R_Surface!=0);

  assert(TOV_r_1d!=0);
  assert(TOV_rbar_1d!=0);
  assert(TOV_press_1d!=0);
  assert(TOV_phi_1d!=0);
  assert(TOV_m_1d!=0);

  /* allocate local arrays */
  r_to_star   = (CCTK_REAL *) calloc (TOV_Num_TOVs, sizeof(CCTK_REAL));
  press_point = (CCTK_REAL *) calloc (TOV_Num_TOVs, sizeof(CCTK_REAL));
  rho_point   = (CCTK_REAL *) calloc (TOV_Num_TOVs, sizeof(CCTK_REAL));
  eps_point   = (CCTK_REAL *) calloc (TOV_Num_TOVs, sizeof(CCTK_REAL));
  mu_point    = (CCTK_REAL *) calloc (TOV_Num_TOVs, sizeof(CCTK_REAL));
  phi_point   = (CCTK_REAL *) calloc (TOV_Num_TOVs, sizeof(CCTK_REAL));
  r_point     = (CCTK_REAL *) calloc (TOV_Num_TOVs, sizeof(CCTK_REAL));

  /* clear initial data */
  if (TOV_Clear_Initial_Data > 0 && !(TOV_Use_Old_Initial_Data))
  {
    TOV_C_fill(kxx, LSH_MAX_I+1, 0.0);
    TOV_C_fill(kxy, LSH_MAX_I+1, 0.0);
    TOV_C_fill(kxz, LSH_MAX_I+1, 0.0);
    TOV_C_fill(kyy, LSH_MAX_I+1, 0.0);
    TOV_C_fill(kyz, LSH_MAX_I+1, 0.0);
    TOV_C_fill(kzz, LSH_MAX_I+1, 0.0);
    TOV_C_fill(gxx, LSH_MAX_I+1, 0.0);
    TOV_C_fill(gyy, LSH_MAX_I+1, 0.0);
    TOV_C_fill(gzz, LSH_MAX_I+1, 0.0);
    TOV_C_fill(gxy, LSH_MAX_I+1, 0.0);
    TOV_C_fill(gxz, LSH_MAX_I+1, 0.0);
    TOV_C_fill(gyz, LSH_MAX_I+1, 0.0);
    TOV_C_fill(alp, LSH_MAX_I+1, 1.0);

    if (*shift_state != 0)
    {
      TOV_C_fill(betax, LSH_MAX_I+1, 0.0);
      TOV_C_fill(betay, LSH_MAX_I+1, 0.0);
      TOV_C_fill(betaz, LSH_MAX_I+1, 0.0);
    }

    if (*conformal_state != 0)
    {
      TOV_C_fill(psi, LSH_MAX_I+1, 1.0);
      if (*conformal_state > 1)
      {
        TOV_C_fill(psix, LSH_MAX_I+1, 0.0);
        TOV_C_fill(psiy, LSH_MAX_I+1, 0.0);
        TOV_C_fill(psiz, LSH_MAX_I+1, 0.0);
        if (*conformal_state > 2)
        {
          TOV_C_fill(psixx, LSH_MAX_I+1, 0.0);
          TOV_C_fill(psixy, LSH_MAX_I+1, 0.0);
          TOV_C_fill(psixz, LSH_MAX_I+1, 0.0);
          TOV_C_fill(psiyy, LSH_MAX_I+1, 0.0);
          TOV_C_fill(psiyz, LSH_MAX_I+1, 0.0);
          TOV_C_fill(psizz, LSH_MAX_I+1, 0.0);
        }
      }
    }
  }
  if (!TOV_Use_Old_Matter_Initial_Data)
  {
    CCTK_INFO("Not using old matter initial data");
    TOV_C_fill(rho,        LSH_MAX_I+1, 0.0);
    TOV_C_fill(eps,        LSH_MAX_I+1, 0.0);
    TOV_C_fill(press,      LSH_MAX_I+1, 0.0);
    TOV_C_fill(w_lorentz,  LSH_MAX_I+1, 0.0);
    TOV_C_fill(velx,       LSH_MAX_I+1, 0.0);
    TOV_C_fill(vely,       LSH_MAX_I+1, 0.0);
    TOV_C_fill(velz,       LSH_MAX_I+1, 0.0);
  }
  /* use the fast interpolation? only useful for testing this */
  if (TOV_Fast_Interpolation == 0)
      CCTK_INFO("Interpolating the slow way.");

  /* go over all 3D-grid points */
  for(i=0; i<cctk_lsh[0]; i++)
   for(j=0; j<cctk_lsh[1]; j++)
    for(k=0; k<cctk_lsh[2]; k++)
    {
      i3D=CCTK_GFINDEX3D(cctkGH, i, j, k);
      /* remember the old conformal factor to the power of 4 */
      if (*conformal_state != 0)
        my_psi4=pow(psi[i3D], 4.0);
      else
        my_psi4=1.0;

      for (star=0; star<TOV_Num_TOVs; star++)
      {
        r_to_star[star] =
          sqrt( (x[i3D]-TOV_Position_x[star]) *
                (x[i3D]-TOV_Position_x[star]) +
                (y[i3D]-TOV_Position_y[star]) *
                (y[i3D]-TOV_Position_y[star]) +
                (z[i3D]-TOV_Position_z[star]) *
                (z[i3D]-TOV_Position_z[star]) );
        int star_i = star * TOV_Num_Radial;

        /* do the actual interpolation */
        TOV_C_interp_tov_isotropic(star,
                               &(TOV_press_1d[star_i]), &(TOV_phi_1d[star_i]),
                               &(TOV_rbar_1d[star_i]), &(TOV_r_1d[star_i]),
                               &(r_to_star[star]), TOV_Surface[star],
                               &(press_point[star]),
                               &(phi_point[star]), &(r_point[star]));

        /* is some perturbation wanted? */
        if (Perturb[star] == 0)
          rho_point[star] = pow(press_point[star]/TOV_K,
                                1.0/TOV_Gamma);
        else
          rho_point[star] = pow(press_point[star]/TOV_K,
                                1.0/TOV_Gamma) *
                            (1.0 +
                             Pert_Amplitude[star] *
                               cos(PI/2.0 * r[i3D] / TOV_R_Surface[star]));

        if (rho_point[star] > local_tiny)
          eps_point[star] = press_point[star] / (TOV_Gamma - 1.0)
                                              /  rho_point[star];
        else
          eps_point[star] = 0.0;
        mu_point[star]  = rho_point[star] * (1.0 + eps_point[star]);
      }
      /* find out from which star we want to have the data */
      if (CCTK_EQUALS(TOV_Combine_Method, "maximum"))
      {
        /* to do this, we use here simply the max of the gxx-value */
        star=0;
        max_g_diag = 0.0;
        max_rho = rho_point[0];
        for (int star_i=0; star_i<TOV_Num_TOVs; star_i++)
        {
          g_diag = (r_point[star_i] / (r_to_star[star_i] + 1.0e-30)) *
                   (r_point[star_i] / (r_to_star[star_i] + 1.0e-30));
          if ((g_diag - max_g_diag) > local_tiny)
          {
            max_g_diag=g_diag;
            star=star_i;
          }
          if ((rho_point[star_i] - max_rho) > local_tiny)
          {
            max_rho=rho_point[star_i];
            star=star_i;
          }
        }
        /* handle initial data */
        if (TOV_Use_Old_Initial_Data)
        {
          /* check metric */
          if ((my_psi4 * gxx[i3D] < max_g_diag) &&
              (my_psi4 * gyy[i3D] < max_g_diag) &&
              (my_psi4 * gzz[i3D] < max_g_diag))
          {
            if (TOV_Conformal_Flat_Three_Metric)
            {
              psi[i3D] = pow(max_g_diag, 0.25);
              my_psi4 = max_g_diag;
            }
            else
            {
              gxx[i3D] = max_g_diag/my_psi4;
              gyy[i3D] = max_g_diag/my_psi4;
              gzz[i3D] = max_g_diag/my_psi4;
              gxy[i3D] = gxz[i3D] = gyz[i3D] = 0.0;
            }
          }
          /* check matter */
          if (TOV_Use_Old_Matter_Initial_Data)
          {
            if (rho[i3D] > max_rho)
            {
              /* we do not need this array element anymore, since we use
               * the initial data, so lets use it */
              star=0;
              max_rho          =rho[i3D];
              eps_point[star]  =eps[i3D];
              press_point[star]=press[i3D];
              my_velx=velx[i3D];
              my_vely=vely[i3D];
              my_velz=velz[i3D];
            }
            else
            {
              if (tov_lapse)
                alp[i3D] = exp(phi_point[star]);
              if (tov_shift)
              {
                betax[i3D] = 0.0;
                betay[i3D] = 0.0;
                betaz[i3D] = 0.0;
              }
              my_velx=TOV_Velocity_x[star];
              my_vely=TOV_Velocity_y[star];
              my_velz=TOV_Velocity_z[star];
            }
          }
          else
          {
            if (tov_lapse)
              alp[i3D] = exp(phi_point[star]);
            if (tov_shift)
            {
              betax[i3D] = 0.0;
              betay[i3D] = 0.0;
              betaz[i3D] = 0.0;
            }
            my_velx=TOV_Velocity_x[star];
            my_vely=TOV_Velocity_y[star];
            my_velz=TOV_Velocity_z[star];
          }
        }
        else /* do not use old initial data */
        {
          /* no psi, since it is 1.0 here */
          /* but maybe we want to have it != 1.0 */
          if (TOV_Conformal_Flat_Three_Metric)
          {
            psi[i3D] = pow(max_g_diag, 0.25);
            my_psi4 = max_g_diag;
            gxx[i3D] = gyy[i3D] = gzz[i3D] = 1.0;
            gxy[i3D] = gxz[i3D] = gyz[i3D] = 0.0;
          }
          else
          {
            gxx[i3D] = max_g_diag;
            gyy[i3D] = max_g_diag;
            gzz[i3D] = max_g_diag;
            gxy[i3D] = gxz[i3D] = gyz[i3D] = 0.0;
          }
          if (tov_lapse)
            alp[i3D] = exp(phi_point[star]);
          if (tov_shift)
          {
            betax[i3D] = 0.0;
            betay[i3D] = 0.0;
            betaz[i3D] = 0.0;
          }
          my_velx=TOV_Velocity_x[star];
          my_vely=TOV_Velocity_y[star];
          my_velz=TOV_Velocity_z[star];
        }

        /* set to defined velocity. default is 0.0 because other velocities
         * violate Einsteins equations */
        velx[i3D] = my_velx;
        vely[i3D] = my_vely;
        velz[i3D] = my_velz;

        w_lorentz[i3D] = 1/sqrt(1.0-(
                                  gxx[i3D] * velx[i3D] * velx[i3D]+
                                  gyy[i3D] * vely[i3D] * vely[i3D]+
                                  gzz[i3D] * velz[i3D] * velz[i3D]+
                                2*gxy[i3D] * velx[i3D] * vely[i3D]+
                                2*gxz[i3D] * velx[i3D] * velz[i3D]+
                                2*gyz[i3D] * vely[i3D] * velz[i3D])*
                                my_psi4);

        rho[i3D] = max_rho;
        eps[i3D] = eps_point[star];
        press[i3D] = press_point[star];

      }
      else if (CCTK_EQUALS(TOV_Combine_Method, "average"))
      {
        /* here we 'average' all values in a more intelligent way */
        if (TOV_Use_Old_Matter_Initial_Data)
          max_rho=rho[i3D];
        else
        {
          max_rho=0.0;
          rho[i3D] = 0.0;
        }
        star=-1;
        for (int star_i=0; star_i<TOV_Num_TOVs; star_i++)
        {
          if (tov_lapse)
            alp[i3D] *= exp(phi_point[star_i]);
          if (tov_shift)
          {
            betax[i3D] = 0.0;
            betay[i3D] = 0.0;
            betaz[i3D] = 0.0;
          }
          if (TOV_Conformal_Flat_Three_Metric)
          {
            /* This is a hack, since it does not check if the input data is
             * really conformally flat. It simply assumes this by only using
             * gxx */
            my_psi4 = (r_point[star_i] * r_point[star_i] /
                       (r_to_star[star_i] * r_to_star[star_i] + 1.0e-30)) /
                      my_psi4 + pow(psi[i3D], 4.0) * gxx[i3D];
            psi[i3D] = pow(my_psi4, 0.25);
            if (!TOV_Use_Old_Initial_Data)
            {
              gxx[i3D] = gyy[i3D] = gzz[i3D] = 1.0;
              gxy[i3D] = gxz[i3D] = gyz[i3D] = 0.0;
            }
          }
          else
            gxx[i3D] += (r_point[star_i] * r_point[star_i] /
                         (r_to_star[star_i] * r_to_star[star_i] + 1.0e-30)) /
                        my_psi4;
          rho[i3D] += rho_point[star_i];
          eps[i3D] += eps_point[star_i];
          press[i3D] += press_point[star_i];
          /* we still have to know if we are inside one star - and which */
          if (rho_point[star_i] > max_rho)
          {
            max_rho=rho_point[star_i];
            star=star_i;
          }
        }

        if (TOV_Conformal_Flat_Three_Metric)
        {
          my_psi4 -= ((TOV_Num_TOVs+TOV_Use_Old_Initial_Data-1)/my_psi4);
          psi[i3D] = pow(my_psi4, 0.25);
        }
        else
        {
          gxx[i3D] -= ((TOV_Num_TOVs+TOV_Use_Old_Initial_Data-1)/my_psi4);
          gyy[i3D] = gxx[i3D];
          gzz[i3D] = gxx[i3D];
        }

        /* set to defined velocity. default is 0.0 because other velocities
         * violate the constraints */
        if (star > -1)
        {
          velx[i3D] = TOV_Velocity_x[star];
          vely[i3D] = TOV_Velocity_y[star];
          velz[i3D] = TOV_Velocity_z[star];
        }

        w_lorentz[i3D] = 1/sqrt(1.0-(
                                  gxx[i3D] * velx[i3D] * velx[i3D]+
                                  gyy[i3D] * vely[i3D] * vely[i3D]+
                                  gzz[i3D] * velz[i3D] * velz[i3D]+
                                2*gxy[i3D] * velx[i3D] * vely[i3D]+
                                2*gxz[i3D] * velx[i3D] * velz[i3D]+
                                2*gyz[i3D] * vely[i3D] * velz[i3D]) * my_psi4);
      }
    }


  /* if used, recalculate the derivatives of the conformal factor */
  if (*conformal_state > 1)
    /* go again over all 3D-grid points */
    for(i=0; i<cctk_lsh[0]; i++)
     for(j=0; j<cctk_lsh[1]; j++)
      for(k=0; k<cctk_lsh[2]; k++)
      {
        i3D=CCTK_GFINDEX3D(cctkGH, i, j, k);
        psix[i3D]=(((i==0)?
                    (psi[CCTK_GFINDEX3D(cctkGH, i+1, j, k)] -
                     psi[CCTK_GFINDEX3D(cctkGH, i  , j, k)]):
                   (i==(cctk_lsh[0]-1))?
                    (psi[CCTK_GFINDEX3D(cctkGH, i  , j, k)] -
                     psi[CCTK_GFINDEX3D(cctkGH, i-1, j, k)]):
                0.5*(psi[CCTK_GFINDEX3D(cctkGH, i+1, j, k)] -
                     psi[CCTK_GFINDEX3D(cctkGH, i-1, j, k)])));
        psix[i3D] = DIFF_X(psi);
        psiy[i3D] = DIFF_Y(psi);
        psiz[i3D] = DIFF_Z(psi);
      }
  if (*conformal_state > 2)
    /* go again over all 3D-grid points */
    for(i=0; i<cctk_lsh[0]; i++)
     for(j=0; j<cctk_lsh[1]; j++)
      for(k=0; k<cctk_lsh[2]; k++)
      {
        i3D=CCTK_GFINDEX3D(cctkGH, i, j, k);
        psixx[i3D] = DIFF_X(psix)/psi[i3D];
        psiyy[i3D] = DIFF_Y(psiy)/psi[i3D];
        psizz[i3D] = DIFF_Z(psiz)/psi[i3D];
        psixy[i3D] = DIFF_X(psiy)/psi[i3D];
        psiyz[i3D] = DIFF_Y(psiz)/psi[i3D];
        psixz[i3D] = DIFF_Z(psix)/psi[i3D];
      }
  if (*conformal_state > 1)
    /* go again over all 3D-grid points */
    for(i=0; i<cctk_lsh[0]; i++)
     for(j=0; j<cctk_lsh[1]; j++)
      for(k=0; k<cctk_lsh[2]; k++)
      {
        i3D=CCTK_GFINDEX3D(cctkGH, i, j, k);
        psix[i3D] /= psi[i3D];
        psiy[i3D] /= psi[i3D];
        psiz[i3D] /= psi[i3D];
      }
  i3D = cctk_lsh[2]*cctk_lsh[1]*cctk_lsh[0];
  switch(TOV_Populate_Timelevels)
  {
    case 3:
        TOV_Copy(i3D, gxx_p_p,  gxx);
        TOV_Copy(i3D, gyy_p_p,  gyy);
        TOV_Copy(i3D, gzz_p_p,  gzz);
        TOV_Copy(i3D, gxy_p_p,  gxy);
        TOV_Copy(i3D, gxz_p_p,  gxz);
        TOV_Copy(i3D, gyz_p_p,  gyz);
        TOV_Copy(i3D, rho_p_p,  rho);
        TOV_Copy(i3D, eps_p_p,  eps);
        TOV_Copy(i3D, velx_p_p, velx);
        TOV_Copy(i3D, vely_p_p, vely);
        TOV_Copy(i3D, velz_p_p, velz);
        TOV_Copy(i3D, w_lorentz_p_p, w_lorentz);
        // fall through
    case 2:
        TOV_Copy(i3D, gxx_p,  gxx);
        TOV_Copy(i3D, gyy_p,  gyy);
        TOV_Copy(i3D, gzz_p,  gzz);
        TOV_Copy(i3D, gxy_p,  gxy);
        TOV_Copy(i3D, gxz_p,  gxz);
        TOV_Copy(i3D, gyz_p,  gyz);
        TOV_Copy(i3D, rho_p,  rho);
        TOV_Copy(i3D, eps_p,  eps);
        TOV_Copy(i3D, velx_p, velx);
        TOV_Copy(i3D, vely_p, vely);
        TOV_Copy(i3D, velz_p, velz);
        TOV_Copy(i3D, w_lorentz_p, w_lorentz);
        // fall through
    case 1:
        break;
    default:
        CCTK_VWarn(CCTK_WARN_ABORT, __LINE__, __FILE__, CCTK_THORNSTRING,
                   "Unsupported number of TOV_Populate_TimelevelsL: %d",
                   (int)TOV_Populate_Timelevels);
        break;
  }
  CCTK_INFO("Done interpolation.");

  /* free local arrays */
  free(r_to_star);
  free(press_point);
  free(rho_point);
  free(eps_point);
  free(mu_point);
  free(phi_point);
  free(r_point);
}

void TOV_Prepare_Fake_Evolution(CCTK_ARGUMENTS)
{
    if (CCTK_ParameterSet("TOV_Populate_Timelevels",
                          "TOVSolver",
                          "1"))
        CCTK_WARN(0,
                  "Could not prepare for fake evolution - steering failed\n");
}

inline static CCTK_REAL calc_coord_dist(CCTK_REAL prop_dist, CCTK_REAL M, CCTK_REAL R)
{
    CCTK_REAL xnp = prop_dist;
    CCTK_REAL xn, f, fp;
    do {
      xn = xnp;
      f  = xn - prop_dist + M * log(xn/R - 1);
      fp = 1 + M / (xn-R);
      xnp = xn - f/fp;
    } while (fabs((xnp-xn)/xn) > 10.e-14);
    return xnp;
}

/* Only works for equal-mass binary NS systems atm */
void TOV_Set_ProperPositions(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_ARGUMENTS
  DECLARE_CCTK_PARAMETERS

  /* The specified parameters are given in proper distance */
  CCTK_REAL prop_dist = sqrt((TOV_Position_x[0]-TOV_Position_x[1])*
                             (TOV_Position_x[0]-TOV_Position_x[1])+
                             (TOV_Position_y[0]-TOV_Position_y[1])*
                             (TOV_Position_y[0]-TOV_Position_y[1])+
                             (TOV_Position_z[0]-TOV_Position_z[1])*
                             (TOV_Position_z[0]-TOV_Position_z[1]));
  /* Now we need to calculate the coordinate distance from that */
  CCTK_REAL coord_dist = calc_coord_dist(prop_dist,
                                         TOV_m_1d[TOV_Num_Radial-1],
                                         TOV_RProp_Surface[0]);
  /* Now steer the parameters before the stars are interpolated onto
   * the Cactus grid using those very same variables */
  /* We do that by scaling every variable by the same factor */
  CCTK_REAL factor = coord_dist / prop_dist;
  char tmp[1024];
  snprintf(tmp, 1023, "%g", coord_dist/2);
  if (CCTK_ParameterSet("par_b", "TwoPunctures", tmp))
    CCTK_WARN(0, "Could not set par_b");
  snprintf(tmp, 1023, "%g", TOV_Position_x[0] * factor);
  if (CCTK_ParameterSet("TOV_Position_x[0]", "TOVSolver", tmp))
    CCTK_WARN(0, "Could not set x[0]");
  snprintf(tmp, 1023, "%g", TOV_Position_x[1] * factor);
  if (CCTK_ParameterSet("TOV_Position_x[1]", "TOVSolver", tmp))
    CCTK_WARN(0, "Could not set x[1]");

  // printf("proper distance: %g, coordinate distance: %g\n", prop_dist, coord_dist);
}

#include "external.inc"