Task: Implement BrillLindquist.c for EinsteinInitialData/IDAnalyticBH thorn

thorn_name: EinsteinInitialData/IDAnalyticBH
src_filename: BrillLindquist.c
interface: # Interface definition for thorn IDAnalyticBH
# $Header$

implements: idanalyticbh
inherits: ADMBase, StaticConformal, Grid

private:

#real misner_workspace TYPE = GF
#{
#  csch,
#  coth,
#  r1,
#  r2
#} "Workspace arrays for Misner_standard"

schedule: # Schedule definitions for thorn IDAnalyticBH
# $Header$

if (CCTK_Equals(initial_data,"schwarzschild") ||
    CCTK_Equals(initial_data,"kerr") ||
    CCTK_Equals(initial_data,"bl_bh") ||
    CCTK_Equals(initial_data,"misner_bh") ||
    CCTK_Equals(initial_data,"multiple_misner"))
{
  schedule IDAnalyticBH_ParamChecker at CCTK_PARAMCHECK
  {
    LANG: C
    OPTIONS: global
  } "Construct parameters for analytic black hole solutions"
}

if (CCTK_Equals(initial_data,"schwarzschild")) 
{ 
	
   schedule Schwarzschild in ADMBase_InitialData
   {
     STORAGE: confac[1]
     LANG: C
   } "Construct initial data for a single Schwarzschild black hole"
}
else if (CCTK_Equals(initial_data,"kerr")) 
{ 

# The Kerr stuff uses the conformal factor internally, so
# so make sure that the conformal factor has storage during
# this routine
   schedule KerrID in ADMBase_InitialData
   {	
     STORAGE: confac[1]
     LANG: C
   } "Construct initial data for a single Kerr black hole"

}
else if (CCTK_Equals(initial_data,"bl_bh")) 
{ 
# The Brill-Lindquist stuff uses the conformal factor internally, so
# so make sure that the conformal factor has storage during
# this routine
   schedule BrillLindquist in ADMBase_InitialData
   {
     STORAGE: confac[1]
     LANG: C
   } "Construct initial data for Brill Lindquist black holes"

}
else if (CCTK_Equals(initial_data,"misner_bh")) 
{ 
# The Misner stuff uses the conformal factor internally, so
# so make sure that the conformal factor has storage during
# this routine
   schedule Misner_standard in ADMBase_InitialData
   {
     STORAGE: confac[1]
     LANG: C
   } "Construct initial data for two Misner black holes"

}
else if (CCTK_Equals(initial_data,"multiple_misner_bh")) 
{ 
   schedule Misner_multiple in ADMBase_InitialData
   {
     STORAGE: confac[1]
     LANG: C
   } "Construct initial data for multiple Misner black holes"

}

param: # Parameter definitions for thorn IDAnalyticBH
# $Header$

private:

# Schwarzschild parameters
# ------------------------
REAL mass "Mass of black hole"
{
: :: "Not sure if it can be negative or not"
} 2.0

# Kerr parameters
# ------------------------
REAL a_Kerr "Angular momentum parameter of black hole"
{
-1:1 :: "Between +1 and -1"
} 0.1

# Multiple Misner Parameters
# --------------------------
REAL mu "Misner mu value"
{
0: :: "Non-negative"
} 1.2

INT nmax "Number of terms to include for Misner series"
{
0: :: "Non-negative"
} 30

INT misner_nbh "Number of Misner black holes"
{
 1:10 :: "Not more than 10"
} 1

# Brill Lindquist Parameters
# --------------------------
INT bl_nbh "Number of Brill Lindquist black holes"
{
 1:4 :: "Between one and four holes implemented"
} 1
REAL bl_x0_1 "x-position of 1st BL hole"
{
: :: "Anything"
} 0.0
REAL bl_y0_1 "y-position of 1st BL hole"
{
: :: "Anything"
} 0.0
REAL bl_z0_1 "z-position of 1st BL hole"
{
: :: "Anything"
} 0.0
REAL bl_M_1 "Mass of 1st BL hole"
{
: :: "Anything"
} 1.0
REAL bl_x0_2 "x-position of 2nd BL hole"
{
: :: "Anything"
} 0.0
REAL bl_y0_2 "y-position of 2nd BL hole"
{
: :: "Anything"
} 0.0
REAL bl_z0_2 "z-position of 2nd BL hole"
{
: :: "Anything"
} 0.0
REAL bl_M_2 "Mass of 2nd BL hole"
{
: :: "Anything"
} 1.0
REAL bl_x0_3 "x-position of 3nd BL hole"
{
: :: "Anything"
} 0.0
REAL bl_y0_3 "y-position of 3nd BL hole"
{
: :: "Anything"
} 0.0
REAL bl_z0_3 "z-position of 3nd BL hole"
{
: :: "Anything"
} 0.0
REAL bl_M_3 "Mass of 3rd BL hole"
{
: :: "Anything"
} 1.0
REAL bl_x0_4 "x-position of 4th BL hole"
{
: :: "Anything"
} 0.0
REAL bl_y0_4 "y-position of 4th BL hole"
{
: :: "Anything"
} 0.0
REAL bl_z0_4 "z-position of 4th BL hole"
{
: :: "Anything"
} 0.0
REAL bl_M_4 "Mass of 4th BL hole"
{
: :: "Anything"
} 1.0

# Common parameters
# -----------------
REAL epsilon "Fudge factor"
{
  0.0:* :: ""
} 1.e-16


shares: ADMBase

EXTENDS KEYWORD initial_data 
{
  "schwarzschild"      :: "One Schwarzschild black hole"
  "bl_bh"              :: "Brill Lindquist black holes"
  "misner_bh"          :: "Misner black holes"
  "multiple_misner_bh" :: "Multiple Misner black holes"
  "kerr"	       :: "One Kerr black hole"	
} 

EXTENDS KEYWORD initial_lapse 
{
  "schwarzschild" :: "Set lapse to Schwarzschild"
  "schwarz"       :: "Set lapse to Schwarzschild"
  "cadez"         :: "Set lapse to Misner"
  "kerr"          :: "Set lapse to Kerr"
}

EXTENDS KEYWORD initial_shift 
{
  "kerr" :: "Set shift to Kerr"
}

USES KEYWORD metric_type 

shares: StaticConformal

USES KEYWORD conformal_storage


context: Kerr.c:
```
 /*@@
   @file      Kerr.c
   @date      August 2000
   @author    John Baker
   @desc 
      Set up initial data for a spinning black holes
   @enddesc 
   @version $Header$
 @@*/

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

#include "cctk.h"
#include "cctk_Arguments.h"
#include "cctk_Parameters.h"

#include "IDAnalyticBH.h"

static const char *rcsid = "$Header$";

CCTK_FILEVERSION(CactusEinstein_IDAnalyticBH_Kerr_c)


 /*@@
   @routine    KerrID
   @date       August 2000
   @author     John Baker
   @desc 
   Create Kerr Initital data
   @enddesc 
   @calls     
   @calledby   
   @history 
   @hdate Fri Apr 26 10:04:05 2002 @hauthor Tom Goodale
   @hdesc Changed to use new StaticConformal stuff
   @endhistory 
 
 @@*/
void KerrID(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_ARGUMENTS
  DECLARE_CCTK_PARAMETERS

  int i, do_lapse=0, do_shift=0;
  int npoints;
  CCTK_REAL rK,R,cth,cth_2,sth,sth_2,r_2,R_2,R_3,rho,rho_2,xx,yy,zz;
  CCTK_REAL Sigma,sqrt_Delta,p2,lapse,beta_phi,shift_phi;
  CCTK_REAL tmp, tmp2, inv_psi;
  CCTK_REAL Phi4,fourPhi3,Phi,Chi2;
  CCTK_REAL Phi_R,Phi_RR,Phi_Rq,Phi_q,Phi_qq;
  CCTK_REAL Phi4_R,Phi4_RR,Phi4_Rq,Phi4_q,Phi4_qq;
  /* CCTK_REAL Delta,Krj,gRR,gqq,gjj; */
  CCTK_REAL KRj,Kqj;
  
  CCTK_REAL dRdx,dRdy,dRdz,dqdx,dqdy,dqdz,djdx,djdy;
  CCTK_REAL d2Rdxx,d2Rdxy,d2Rdxz,d2Rdyy,d2Rdyz,d2Rdzz;
  CCTK_REAL d2qdxx,d2qdxy,d2qdxz,d2qdyy,d2qdyz,d2qdzz;
  CCTK_REAL m=mass,a=a_Kerr,a_2=a*a,m2_a2=m*m-a_2;
  int make_conformal_derivs = 0;

  CCTK_VInfo(CCTK_THORNSTRING,
             "setting up Kerr initial data");

  /* total number of points on this processor */
  npoints = cctk_lsh[0] * cctk_lsh[1] * cctk_lsh[2];

  if (CCTK_Equals(initial_lapse,"Kerr"))
  {
    do_lapse=1;    
    CCTK_INFO("Initialise with Kerr lapse");
  }
  if (CCTK_Equals(initial_shift,"Kerr"))
  {
    do_shift=1;    
    CCTK_INFO("Initialise with Kerr shift");
  }


  /* Check if we should create and store conformal factor stuff */
  if(CCTK_EQUALS(metric_type, "static conformal"))
  {
    if      (CCTK_EQUALS(conformal_storage,"factor"))
    {
      *conformal_state = 1;
      make_conformal_derivs = 0;
    }
    else if (CCTK_EQUALS(conformal_storage,"factor+derivs"))
    {
      *conformal_state = 2;
      make_conformal_derivs = 1;
    }
    else if (CCTK_EQUALS(conformal_storage,"factor+derivs+2nd derivs"))
    {
      *conformal_state = 3;
      make_conformal_derivs = 1;
    }
    else
    {
      CCTK_VWarn(0, __LINE__, __FILE__, CCTK_THORNSTRING,
"KerrID(): impossible value for conformal_storage=\"%s\"!",
                 conformal_storage);                            /*NOTREACHED*/
    }
  }      
  else
  {
    *conformal_state = 0;
  }



  /* printf("npoints: %i\n",npoints); */
  for(i = 0; i < npoints; i++)
  {
    /* if((i/1000)*1000==i||i>35930)printf("i=%i\n",i); */
    /* Define coordinate functions */
    xx=x[i];  yy=y[i];  zz=z[i];
    rho_2=xx*xx+yy*yy;
    rho=sqrt(rho_2);
    if (rho<epsilon) {
      xx=epsilon;
      rho_2=xx*xx+yy*yy;
      rho=sqrt(rho_2);
    }
    R_2=rho_2+zz*zz;
    R=sqrt(R_2);
    R_3=R*R_2;
    cth=zz/R;
    cth_2=cth*cth;
    sth_2=rho_2/R_2;
    sth=rho/R;

    /* Special to Kerr */
    rK=R+m+m2_a2/4/R;
    r_2=rK*rK;
    sqrt_Delta=R-m2_a2/4/R;
    /*Delta=sqrt_Delta*sqrt_Delta;*/
    Sigma=r_2+a_2*cth_2;
    beta_phi=-2*m*rK*a*sth_2/Sigma;
    p2=a_2+r_2-a*beta_phi;
    /* drdR=sqrt_Delta/R; */
    lapse=sqrt_Delta/sqrt(p2);
    shift_phi=-2*m*rK*a/(p2*Sigma);
    
    /* Kerr metric in quasi-isotropic coordinates */
    /* ds^2=Phi4*(dR^2+R^2*dth^2+R^2*Chi^2*sin(th)^2*dphi^2) */
    Phi4=Sigma/R_2;
    Chi2=p2/Sigma;
    /* gRR=1; */
    /* gqq=R_2; */
    /* gjj=R_2*sth_2*Chi2; */
    
    /* extrinsic curvature */
    /* tmp=p2+a*beta_phi/2; */
    /* Krj=(beta_phi*rK)/(2/lapse/p2)*(1+(2/Sigma-1/R_2)*tmp); */
    /* Kqj=(-2*m*a*sth*cth/Sigma)/lapse*((r_2+a_2)/(p2*Sigma)*tmp-.5); */
    /* transform r to R */
    /* KRj=drdR*Krj; */
    tmp=sqrt(p2)*Sigma;
    KRj=a*m*sth_2/R/tmp*(r_2-a_2+2*r_2*(r_2+a_2)/Sigma);
    Kqj=sqrt_Delta*beta_phi*a_2*sth*cth/tmp;

    /* Conformal factor derivatives */
    {
      double drdR=sqrt_Delta/R;
      double d2rdR2=m2_a2/2/R_3;
      double dSigmadR=2*rK*drdR;
      double d2SigmadR2=2*(drdR*drdR+rK*d2rdR2);
      Phi4_R=dSigmadR/R_2-2*Sigma/R_3;
      Phi4_RR=d2SigmadR2/R_2-4*dSigmadR/R_3+6*Sigma/R_2/R_2;
    }
    /* Phi4_R =2/R_3*(rK*sqrt_Delta-Sigma); */
    /* Phi4_RR=(m2_a2*rK/R+2*(Delta+Sigma)-8*rK*sqrt_Delta)/R_2/R_2; */
    Phi4_q =-2*a_2*cth*sth/R_2;
    Phi4_Rq=-2*Phi4_q/R;
    Phi4_qq=2*a_2*(-cth_2+sth_2)/R_2;

    Phi=pow(Phi4,.25);

    fourPhi3=4*Phi*Phi*Phi;
    
    Phi_R =Phi4_R/fourPhi3;
    Phi_RR=(Phi4_RR-3*Phi4_R*Phi4_R/Phi4/4)/fourPhi3;
    Phi_q =Phi4_q/fourPhi3;
    Phi_Rq=(Phi4_Rq-3*Phi4_R*Phi4_q/Phi4/4)/fourPhi3;
    Phi_qq=(Phi4_qq-3*Phi4_q*Phi4_q/Phi4/4)/fourPhi3;

    /* Now we set the cactus variables */
    psi [i] = Phi;

    if(do_lapse)
    {
      alp[i]=lapse;
    }

    /* transform to cartesian coordinates */
    dRdx=xx/R;           
    dRdy=yy/R;            
    dRdz=zz/R;
    dqdx=xx*zz/rho/R_2;  
    dqdy=yy*zz/rho/R_2;  
    dqdz=-rho/R_2;
    djdx=-yy/rho_2;       
    djdy=xx/rho_2;      

    if(make_conformal_derivs)
    {
      d2Rdxx=(1-xx*xx/R_2)/R;
      d2Rdxy=-xx*yy/R_3;
      d2Rdxz=-xx*zz/R_3;
      d2Rdyy=(1-yy*yy/R_2)/R;
      d2Rdyz=-zz*yy/R_3;
      d2Rdzz=(1-zz*zz/R_2)/R;
      tmp2 = zz/rho/R_2;
      tmp=2/R_2+1/rho_2;
      d2qdxx=tmp2 * (1. - xx*xx*tmp);
      d2qdxy=-dqdx*yy*tmp;
      d2qdyy=tmp2 * (1. - yy*yy*tmp);
      d2qdzz=-2*dqdz*zz/R_2;
      tmp=(1./rho/R_2)*(1. - 2.*zz*zz/R_2);
      d2qdxz= xx * tmp;
      d2qdyz= yy * tmp;

      /* conformal factor partial derivatives */
      /* note second derivatives are not tensors */
      psix[i] = dRdx*Phi_R + dqdx*Phi_q;
      psiy[i] = dRdy*Phi_R + dqdy*Phi_q;
      psiz[i] = dRdz*Phi_R + dqdz*Phi_q;

      if(*conformal_state > 2)
      {
        psixx[i] = dRdx*dRdx*Phi_RR + d2Rdxx*Phi_R + dqdx*dqdx*Phi_qq 
            + d2qdxx*Phi_q + 2*dRdx*dqdx*Phi_Rq;
        psixy[i] = dRdy*dRdx*Phi_RR + d2Rdxy*Phi_R + dqdy*dqdx*Phi_qq 
            + d2qdxy*Phi_q + (dRdx*dqdy+dRdy*dqdx)*Phi_Rq;
        psixz[i] = dRdz*dRdx*Phi_RR + d2Rdxz*Phi_R + dqdz*dqdx*Phi_qq 
            + d2qdxz*Phi_q + (dRdx*dqdz+dRdz*dqdx)*Phi_Rq;
        psiyy[i] = dRdy*dRdy*Phi_RR + d2Rdyy*Phi_R + dqdy*dqdy*Phi_qq 
            + d2qdyy*Phi_q + 2*dRdy*dqdy*Phi_Rq;
        psiyz[i] = dRdz*dRdy*Phi_RR + d2Rdyz*Phi_R + dqdz*dqdy*Phi_qq 
            + d2qdyz*Phi_q + (dRdy*dqdz+dRdz*dqdy)*Phi_Rq;
        psizz[i] = dRdz*dRdz*Phi_RR + d2Rdzz*Phi_R + dqdz*dqdz*Phi_qq 
            + d2qdzz*Phi_q + 2*dRdz*dqdz*Phi_Rq;
      }

    /*     Cactus convention
     *     -----------------
     */
      inv_psi = 1 / psi[i];

      psix[i]  *= inv_psi;
      psiy[i]  *= inv_psi;
      psiz[i]  *= inv_psi;

      if(*conformal_state > 2)
      {
        psixx[i] *= inv_psi;
        psixy[i] *= inv_psi;
        psixz[i] *= inv_psi;
        psiyy[i] *= inv_psi;
        psiyz[i] *= inv_psi;
        psizz[i] *= inv_psi;
      }
    }

    /* metric */
    tmp=(Chi2-1)*R_2*sth_2;
    gxx[i] = 1 + djdx*djdx*tmp;
    gxy[i] = djdx*djdy*tmp;
    gxz[i] = 0;
    gyy[i] = 1 + djdy*djdy*tmp;
    gyz[i] = 0;
    gzz[i] = 1;
  

    /* extrinsic curvature */
    kxx[i] = 2*(dRdx*KRj+dqdx*Kqj)*djdx;
    kxy[i] = (dRdx*KRj+dqdx*Kqj)*djdy + (dRdy*KRj+dqdy*Kqj)*djdx;
    kxz[i] = (dRdz*KRj+dqdz*Kqj)*djdx;
    kyy[i] = 2*(dRdy*KRj+dqdy*Kqj)*djdy;
    kyz[i] = (dRdz*KRj+dqdz*Kqj)*djdy;
    kzz[i] = 0;

    /*    probable convention to define conformal extrinsic curvature:
          if (*conformal_state == CONFORMAL_METRIC)
          {
          tmp=Psi*Psi;
          kxx[i] *=tmp;
          kxy[i] *=tmp;
          kxz[i] *=tmp;
          kyy[i] *=tmp;
          kyz[i] *=tmp;
      }*/

    /* shift */
    if(do_shift)
    {
      betax[i]=-yy*shift_phi;
      betay[i]=xx*shift_phi;
      betaz[i]=0;
    }
  }

  /*     Metric depends on conformal state
   *     ---------------------------------
   */
  
  if(CCTK_EQUALS(metric_type, "physical"))
  {
    for(i = 0; i < npoints; i++)
    {
      /* if((i/1000)*1000==i||i>35930)printf("i=%i\n",i); */
      tmp=psi[i];tmp*=tmp;tmp*=tmp;
      gxx[i] *= tmp;
      gxy[i] *= tmp;
      gyy[i] *= tmp;
      gzz[i] *= tmp;
      psi[i]  = 1;
    }
    IDAnalyticBH_zero_CCTK_REAL_array(npoints, gxz);
    IDAnalyticBH_zero_CCTK_REAL_array(npoints, gyz);

    if (psix && *conformal_state < 2)
    {
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psix);
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psiy);
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psiz);
    }
    if (psixx && *conformal_state < 3)
    {
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psixx);
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psixy);
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psixz);
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psiyy);
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psiyz);
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psizz);
    }
  }
  
  return;
}

```

IDAnalyticBH.h:
```
 /*@@
   @file      IDAnalyticBH.h
   @date      23.Mar.2003
   @author    Jonathan Thornburg <jthorn@aei.mpg.de>
   @desc      Overall header file for this thorn; defines prototypes
              externally visible routines.
   @enddesc 
   @version $Header$
   @history
   @hdate 6.May.2003
   @hauthor Jonathan Thornburg
   @hdesc   add lots more prototypes
   @endhistory
 @@*/

/******************************************************************************/

#define SQR(a) ((a)*(a))

/******************************************************************************/

/*
 * prototypes for routines called directly by the Cactus scheduler
 */

void IDAnalyticBH_ParamChecker(CCTK_ARGUMENTS);         /* ParamChecker.c */
void Schwarzschild(CCTK_ARGUMENTS);                     /* Schwarzschild.c */
void KerrID(CCTK_ARGUMENTS);                            /* Kerr.c */
void BrillLindquist(CCTK_ARGUMENTS);                    /* BrillLindquist.c */
void Misner_standard(CCTK_ARGUMENTS);                   /* Misner_standard.c */
void Misner_multiple(CCTK_ARGUMENTS);                   /* Misner_multiple.c */

/******************************************************************************/

/*
 * prototypes for other routines visible outside their source files
 */

/* Misner_points.c */
void Misner_init(int n, CCTK_REAL mu, int terms);
CCTK_REAL MisnerEvalPsi(CCTK_REAL x, CCTK_REAL y, CCTK_REAL z);

/* misc.c */
void IDAnalyticBH_zero_CCTK_REAL_array(int N, CCTK_REAL A[]);

```

Misner_standard.c:
```
 /*@@
   @file      Misner_standard.c
   @date      March 1997
   @author    Joan Masso
   @desc
              Set up initial data for two Misner black holes
   @enddesc
   @history
   @hdate Sun Oct 17 11:05:48 1999 @hauthor Tom Goodale
   @hdesc Converted to C
   @endhistory
   @version   $Header$
 @@*/

#include <math.h>
#include <string.h>
#include <stdlib.h>

#include "cctk.h"
#include "cctk_Arguments.h"
#include "cctk_Parameters.h"

#include "IDAnalyticBH.h"

static const char *rcsid = "$Header$";

CCTK_FILEVERSION(CactusEinstein_IDAnalyticBH_Misner_standard_c)

 /*@@
   @routine    Misner_standard
   @date
   @author     Joan Masso, Ed Seidel
   @desc
            Initialize the metric with a time symmetrical
            black hole spacetime containing
            two axially symmetric misner black holes with a
            mass/length parameter mu. The mass is computed.
            The spacetime line element has the form:
                 $$ ds^2 = -dt^2 + \Psi^4 (dx^2+dy^2+dz^2) $$
            and only $\Psi$ differs.
            (Conformal factor from Karen Camarda)
   @enddesc

   @par      mu
   @pdesc    Misner parameter.
   @ptype    real
   @pcomment Values less than 1.8 do not really correspond to two
            black holes, as there is an initial single event horizon
            surrounding the throats. So, with low values of mu we also
            have distorted single black holes.
   @endpar

   @par      nmax
   @pdesc    Summation limit for the misner series in the 'twobh' case.
   @ptype    integer
   @endpar
   @history
   @hdate Fri Apr 26 10:04:05 2002 @hauthor Tom Goodale
   @hdesc Changed to use new StaticConformal stuff
   @endhistory 

@@*/
void Misner_standard(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_ARGUMENTS
  DECLARE_CCTK_PARAMETERS

  int i, n;
  int npoints;
  CCTK_REAL *csch, *coth, inv_r1, inv_r2;
  CCTK_REAL x_squared, y_squared, xy_squared;
  CCTK_REAL inv_r1_cubed, inv_r2_cubed;
  CCTK_REAL inv_r1_5, inv_r2_5;
  CCTK_REAL inv_psi;
  CCTK_INT powfac;
  CCTK_REAL adm_mass;
  const CCTK_REAL zero = 0.0, one = 1.0, three = 3.0;
  int make_conformal_derivs = 0;

  CCTK_VInfo(CCTK_THORNSTRING,
             "setting up Misner initial data");

  /* Check if we should create and store conformal factor stuff */
  if(CCTK_EQUALS(metric_type, "static conformal"))
  {
    if      (CCTK_EQUALS(conformal_storage,"factor"))
    {
      *conformal_state = 1;
      make_conformal_derivs = 0;
    }
    else if (CCTK_EQUALS(conformal_storage,"factor+derivs"))
    {
      *conformal_state = 2;
      make_conformal_derivs = 1;
    }
    else if (CCTK_EQUALS(conformal_storage,"factor+derivs+2nd derivs"))
    {
      *conformal_state = 3;
      make_conformal_derivs = 1;
    }
    else
    {
      CCTK_VWarn(0, __LINE__, __FILE__, CCTK_THORNSTRING,
"Misner_Standard(): impossible value for conformal_storage=\"%s\"!",
                 conformal_storage);                            /*NOTREACHED*/
    }
  }      

  /* total number of points on this processor */
  npoints = cctk_lsh[0] * cctk_lsh[1] * cctk_lsh[2];


  /*     Initialize so we can accumulate
   *     -------------------------------
   */
  if (make_conformal_derivs)
  {
    IDAnalyticBH_zero_CCTK_REAL_array(npoints, psix);
    IDAnalyticBH_zero_CCTK_REAL_array(npoints, psiy);
    IDAnalyticBH_zero_CCTK_REAL_array(npoints, psiz);

    if(*conformal_state > 2)
    {
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psixx);
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psixy);
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psixz);
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psiyy);
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psiyz);
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psizz);
    }
  }

  csch = (CCTK_REAL *) malloc (2 * (nmax + 1) * sizeof (CCTK_REAL));
  coth = csch + nmax + 1;

  /*     compute the ADM mass
   *     --------------------
   */
  adm_mass = zero;
  for(n = 1; n <= nmax; n++)
  {
    csch[n] = one / sinh(mu*n);
    coth[n] = one / tanh(mu*n);
    adm_mass   += 4.0 * csch[n];
  }
  CCTK_VInfo(CCTK_THORNSTRING, "ADM mass is %f", (double) adm_mass);


  for(i = 0; i < npoints; i++)
  {
    psi [i] = one;

    x_squared  = SQR(x[i]);
    y_squared  = SQR(y[i]);
    xy_squared = x_squared + y_squared;

    for(n = nmax; n >= 1; n--)
    {
      inv_r1 = one / sqrt(xy_squared+SQR(z[i]+coth[n]));
      inv_r2 = one / sqrt(xy_squared+SQR(z[i]-coth[n]));

      psi[i] += csch[n]*(inv_r1 + inv_r2);

      if (make_conformal_derivs)
      {
        inv_r1_cubed = inv_r1 * inv_r1 * inv_r1;
        inv_r2_cubed = inv_r2 * inv_r2 * inv_r2;
        inv_r1_5     = inv_r1 * inv_r1 * inv_r1_cubed;
        inv_r2_5     = inv_r2 * inv_r2 * inv_r2_cubed;
        psix[i]  +=  -x[i] * (inv_r2_cubed + inv_r1_cubed) * csch[n];
        psiy[i]  +=  -y[i] * (inv_r2_cubed + inv_r1_cubed) * csch[n];
        psiz[i]  +=  (-(z[i]-coth[n])*inv_r2_cubed - (z[i]+coth[n])*inv_r1_cubed) * csch[n];


        if(*conformal_state > 2)
        {
          psixx[i] +=  (three*x_squared*(inv_r1_5 + inv_r2_5)
                        - inv_r1_cubed - inv_r2_cubed) * csch[n];
          psixy[i] +=  three*x[i]*y[i]*(inv_r1_5 + inv_r2_5) * csch[n];
          psixz[i] +=  (three*x[i]*(z[i] - coth[n])*inv_r2_5
                        + three*x[i]*(z[i] + coth[n])*inv_r1_5) * csch[n];
          psiyy[i] +=  (three*y_squared*(inv_r1_5 + inv_r2_5)
                        - inv_r1_cubed - inv_r2_cubed) * csch[n];
          psiyz[i] +=  (three*y[i]*(z[i] - coth[n])*inv_r2_5
                      + three*y[i]*(z[i] + coth[n])*inv_r1_5) * csch[n];
          psizz[i] += (-inv_r2_cubed+three*SQR(z[i] - coth[n])*inv_r2_5
                       + three*SQR(z[i] + coth[n])*inv_r1_5 - inv_r1_cubed) * csch[n];
        }
      }
    }

    /*     Cactus convention
     *     -----------------
     */
    if (make_conformal_derivs)
    {
      inv_psi = one / psi[i];

      psix[i]  *= inv_psi;
      psiy[i]  *= inv_psi;
      psiz[i]  *= inv_psi;

      if(*conformal_state > 2)
      {
        psixx[i] *= inv_psi;
        psixy[i] *= inv_psi;
        psixz[i] *= inv_psi;
        psiyy[i] *= inv_psi;
        psiyz[i] *= inv_psi;
        psizz[i] *= inv_psi;
      }
    }
  }

  /*     Should initialize lapse to Cadez value if possible
   *     --------------------------------------------------
   */

  if (CCTK_Equals(initial_lapse,"cadez"))
  {
    CCTK_INFO("Initialise with cadez lapse");

    for(i = 0; i < npoints; i++)
    {
      xy_squared = SQR(x[i]) + SQR(y[i]);

      alp[i] = one;

      powfac = 1;

      for(n = 1; n <= nmax; n++)
      {
        inv_r1 = one / sqrt(xy_squared+SQR(z[i]+coth[n]));
        inv_r2 = one / sqrt(xy_squared+SQR(z[i]-coth[n]));
        powfac = -powfac;

        alp[i] += powfac * csch[n] * (inv_r1 + inv_r2);
      }

      alp[i] /= psi[i];
    }
  }

  /*     Metric depends on conformal state
   *     ---------------------------------
   */

  if (CCTK_EQUALS(metric_type, "static conformal"))
  {
    for(i = 0; i < npoints; i++)
    {
      gxx[i] = one;
      gyy[i] = one;
      gzz[i] = one;
    }
  }
  else
  {
    for(i = 0; i < npoints; i++)
    {
      gxx[i] = psi[i] * psi[i] * psi[i] * psi[i];
      gyy[i] = gxx[i];
      gzz[i] = gxx[i];
    }
  }
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, gxy);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, gxz);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, gyz);

  /*     Time-symmetric data
   *     -------------------
   */
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kxx);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kxy);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kxz);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kyy);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kyz);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kzz);

  if (csch)
  {
    free (csch);
  }
}

```

ParamChecker.c:
```
 /*@@
   @file      ParamChecker.F
   @date      March 1999
   @author    Gabrielle Allen
   @desc 
      Check parameters for black hole initial data and give some
      information
   @enddesc 
   @version $Header$
 @@*/

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

#include "cctk.h"
#include "cctk_Arguments.h"
#include "cctk_Parameters.h"

#include "IDAnalyticBH.h"

static const char *rcsid = "$Header$";

CCTK_FILEVERSION(CactusEinstein_IDAnalyticBH_ParamChecker_c)

/********************************************************************
 *********************     Local Data Types   ***********************
 ********************************************************************/

/********************************************************************
 ********************* Local Routine Prototypes *********************
 ********************************************************************/

/********************************************************************
 ***************** Scheduled Routine Prototypes *********************
 ********************************************************************/

/********************************************************************
 ********************* Other Routine Prototypes *********************
 ********************************************************************/

/********************************************************************
 *********************     Local Data   *****************************
 ********************************************************************/

/********************************************************************
 *********************     External Routines   **********************
 ********************************************************************/

 /*@@
   @routine    IDAnalytic_ParamChecker
   @date       March 1999
   @author     Gabrielle Allen
   @desc 
      Check parameters for black hole initial data and give some
      information
   @enddesc 
   @calls     
   @history 
   @hdate Fri Apr 26 09:48:24 2002 @hauthor Tom Goodale
   @hdesc Modified to new ADMBase and StaticConformal stuff 
   @endhistory 

@@*/

void IDAnalyticBH_ParamChecker(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_ARGUMENTS
  DECLARE_CCTK_PARAMETERS

  char *message;

  /* Do we know how to deal with this type of metric ? */
  if(! CCTK_EQUALS(metric_type, "physical") &&
     ! CCTK_EQUALS(metric_type, "static conformal"))
  {
    CCTK_PARAMWARN("Unknown ADMBase::metric_type - known types are \"physical\" and \"static conformal\"");
  }


  if (CCTK_Equals(initial_data,"schwarzschild") == 1)
  {
    CCTK_INFO("Schwarzschild black hole");
    message = (char *)malloc(200*sizeof(char));
    sprintf(message,"  throat at %f",mass/2.0);
    CCTK_INFO(message);
    free(message);
  }
  if (CCTK_Equals(initial_data,"kerr") == 1)
  {
    CCTK_REAL tmp;
    CCTK_INFO("Kerr black hole");
    message = (char *)malloc(200*sizeof(char));
    sprintf(message,"  m = %f, a = %f",mass,a_Kerr);
    CCTK_INFO(message);
    tmp=pow((mass*mass-a_Kerr*a_Kerr)/4.0,.5);
    sprintf(message,"  throat at %f",tmp);
    CCTK_INFO(message);
    free(message);
  }
  else if (CCTK_Equals(initial_data,"bl_bh") == 1)
  {
    CCTK_INFO("Brill Lindquist black holes");
    message = (char *)malloc(200*sizeof(char));
    sprintf(message,"  %d black holes",(int)bl_nbh);
    CCTK_INFO(message);
    if (bl_nbh > 0)
    {
      sprintf(message,  "  mass %f at (%f,%f,%f)",
              bl_M_1,bl_x0_1,bl_y0_1,bl_z0_1);
      CCTK_INFO(message);
    }
    if (bl_nbh > 1)
    {
      sprintf(message,  "  mass %f at (%f,%f,%f)",
              bl_M_2,bl_x0_2,bl_y0_2,bl_z0_2);
      CCTK_INFO(message);
    }
    if (bl_nbh > 2)
    {
      sprintf(message,  "  mass %f at (%f,%f,%f)",
              bl_M_3,bl_x0_3,bl_y0_3,bl_z0_3);
      CCTK_INFO(message);
    }
    if (bl_nbh > 3)
    {
      sprintf(message,  "  mass %f at (%f,%f,%f)",
              bl_M_4,bl_x0_4,bl_y0_4,bl_z0_4);
      CCTK_INFO(message);
    }
    free(message);
  }
  else if (CCTK_Equals(initial_data,"multiple_misner") == 1)
  {
    CCTK_INFO("Setting up Misner solution for multiple holes");
  }
  else if (CCTK_Equals(initial_data,"misner_bh")==1)
  {
    CCTK_INFO("Two Misner black holes (on z-axis)");
    message = (char *)malloc(200*sizeof(char));
    sprintf(message,"  mu is %f",mu);
    CCTK_INFO(message);
    free(message);
  }

  /*     Remind users about the conformal metric
   *     ---------------------------------------
   */
  
  if (CCTK_EQUALS(metric_type, "static conformal"))
  {
    CCTK_INFO("Black hole initial data uses conformal metric");
    if (CCTK_EQUALS(conformal_storage,"factor+derivs") ||
        CCTK_EQUALS(conformal_storage,"factor+derivs+2nd derivs"))
    {
      CCTK_INFO("  and conformal derivatives");
    }
    else
    {
      CCTK_INFO("  but no conformal derivatives");
    }
  }
  else
  { 
    CCTK_INFO("Implements non-conformal metric");
    CCTK_INFO("  (Not usually a good idea!)");
  }        

}

```

make.code.defn:
```
# Main make.code.defn file for thorn IDAnalyticBH
# $Header$

# Source files in this directory
SRCS = 	ParamChecker.c\
	Schwarzschild.c\
	Kerr.c\
	BrillLindquist.c\
        Misner_standard.c\
	Misner_multiple.c\
	Misner_points.c \
        misc.c

# Subdirectories containing source files
SUBDIRS = 


```

Misner_multiple.c:
```
 /*@@
   @file      Misner_multiple.F
   @date      
   @author    Carsten Gundlach
   @desc 
              Set up initial data for multiple Misner black holes
   @enddesc 
   @version   $Header$
 @@*/

#include <string.h>

#include "cctk.h"
#include "cctk_Arguments.h"
#include "cctk_Parameters.h"

#include "IDAnalyticBH.h"

static const char *rcsid = "$Header$";

CCTK_FILEVERSION(CactusEinstein_IDAnalyticBH_Misner_multiple_c)

 /*@@
   @routine    Misner_multiple
   @date       
   @author     Carsten Gundlach
   @desc 
               Set up initial data for multiple Misner black holes
   @enddesc 
   @calls      MisnerEvalPsi
   @history
   @hdate Fri Apr 26 10:04:05 2002 @hauthor Tom Goodale
   @hdesc Changed to use new StaticConformal stuff
   @hdate 6.May.2003   @hauthor Jonathan Thornburg
   @hdesc code cleanups
   @endhistory 
@@*/
void Misner_multiple(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_ARGUMENTS
  DECLARE_CCTK_PARAMETERS

  int i, npoints;
  CCTK_REAL xval, yval, zval;
  CCTK_REAL inv_psi, tmp0, tmp1, tmp2, tmp3, tmp4;
  const CCTK_REAL nm_eps             = 1e-6;   /* finite differencing step*/
  const CCTK_REAL halved_inv_nm_eps  = 0.5 / nm_eps;
  const CCTK_REAL inv_nm_eps_squared = 1.0 / SQR(nm_eps);
  int make_conformal_derivs = 0;

  CCTK_VInfo(CCTK_THORNSTRING,
             "setting up Misner initial data for %d black holes",
             (int)misner_nbh);

  /* Check if we should create and store conformal factor stuff */
  if(CCTK_EQUALS(metric_type, "static conformal"))
  {
    if      (CCTK_EQUALS(conformal_storage,"factor"))
    {
      *conformal_state = 1;
      make_conformal_derivs = 0;
    }
    else if (CCTK_EQUALS(conformal_storage,"factor+derivs"))
    {
      *conformal_state = 2;
      make_conformal_derivs = 1;
    }
    else if (CCTK_EQUALS(conformal_storage,"factor+derivs+2nd derivs"))
    {
      *conformal_state = 3;
      make_conformal_derivs = 1;
    }
    else
    {
      CCTK_VWarn(0, __LINE__, __FILE__, CCTK_THORNSTRING,
"Misner_multiple(): impossible value for conformal_storage=\"%s\"!",
                 conformal_storage);                            /*NOTREACHED*/
    }
  }


  npoints = cctk_lsh[0] * cctk_lsh[1] * cctk_lsh[2];

  /*     Initialize C global variables
   *     -----------------------------
   */

  Misner_init(misner_nbh, mu, nmax);

  /*     Get value of psi pointwise from a C function
   *     --------------------------------------------
   */

  for(i = 0; i < npoints; i++)
  {
    xval = x[i];
    yval = y[i];
    zval = z[i];

    tmp0 = MisnerEvalPsi(xval, yval, zval);
    psi[i] = tmp0;
           
    /*              Only calculate derivatives of psi if required
     *              ---------------------------------------------
     */
    if (make_conformal_derivs)
    {
      tmp1 = MisnerEvalPsi(xval+nm_eps,yval,zval);
      tmp2 = MisnerEvalPsi(xval-nm_eps,yval,zval);
      psix[i] = (tmp1-tmp2) * halved_inv_nm_eps;

      if(*conformal_state > 2)
      {
        psixx[i] = (tmp1+tmp2-2.0*tmp0) * inv_nm_eps_squared;
      }

      tmp1 = MisnerEvalPsi(xval,yval+nm_eps,zval);
      tmp2 = MisnerEvalPsi(xval,yval-nm_eps,zval);
      psiy[i]  = (tmp1-tmp2) * halved_inv_nm_eps;

      if(*conformal_state > 2)
      {
        psiyy[i] = (tmp1+tmp2-2.0*tmp0) * inv_nm_eps_squared;
      }

      tmp1 = MisnerEvalPsi(xval,yval,zval+nm_eps);
      tmp2 = MisnerEvalPsi(xval,yval,zval-nm_eps);
      psiz[i] = (tmp1-tmp2) * halved_inv_nm_eps;

      if(*conformal_state > 2)
      {
        psizz[i] = (tmp1+tmp2-2.0*tmp0) * inv_nm_eps_squared;
      }

      if(*conformal_state > 2)
      {
        tmp1 = MisnerEvalPsi(xval+nm_eps,yval+nm_eps,zval);
        tmp2 = MisnerEvalPsi(xval+nm_eps,yval-nm_eps,zval);
        tmp3 = MisnerEvalPsi(xval-nm_eps,yval+nm_eps,zval);
        tmp4 = MisnerEvalPsi(xval-nm_eps,yval-nm_eps,zval);
        psixy[i] = 0.25*(tmp1-tmp2-tmp3+tmp4) * inv_nm_eps_squared;

        tmp1 = MisnerEvalPsi(xval,yval+nm_eps,zval+nm_eps);
        tmp2 = MisnerEvalPsi(xval,yval-nm_eps,zval+nm_eps);
        tmp3 = MisnerEvalPsi(xval,yval+nm_eps,zval-nm_eps);
        tmp4 = MisnerEvalPsi(xval,yval-nm_eps,zval-nm_eps);
        psiyz[i] = 0.25*(tmp1-tmp2-tmp3+tmp4) * inv_nm_eps_squared;

        tmp1 = MisnerEvalPsi(xval+nm_eps,yval,zval+nm_eps);
        tmp2 = MisnerEvalPsi(xval+nm_eps,yval,zval-nm_eps);
        tmp3 = MisnerEvalPsi(xval-nm_eps,yval,zval+nm_eps);
        tmp4 = MisnerEvalPsi(xval-nm_eps,yval,zval-nm_eps);
        psixz[i] = 0.25*(tmp1-tmp2-tmp3+tmp4) * inv_nm_eps_squared;
      }
    }
  }

  /*     Cactus conventions
   *     ------------------
   */

  if (make_conformal_derivs)
  {
    for(i = 0; i < npoints; i++)
    {
      inv_psi = 1.0 / psi[i];

      psix[i]  *= inv_psi;
      psiy[i]  *= inv_psi;
      psiz[i]  *= inv_psi;
      if(*conformal_state > 2)
      {
        psixx[i] *= inv_psi;
        psixy[i] *= inv_psi;
        psixz[i] *= inv_psi;
        psiyy[i] *= inv_psi;
        psiyz[i] *= inv_psi;
        psizz[i] *= inv_psi;
      }
    }
  }

  /*     Metric depends on conformal state
   *     ---------------------------------
   */

  if (CCTK_EQUALS(metric_type, "static conformal"))
  {
    for(i = 0; i < npoints; i++)
    {
      gxx[i] = 1.0;
      gyy[i] = 1.0;
      gzz[i] = 1.0;
    }
  }
  else 
  {
    for(i = 0; i < npoints; i++)
    {
      gxx[i] = psi[i] * psi[i] * psi[i] * psi[i];
      gyy[i] = gxx[i];
      gzz[i] = gxx[i];
    }
  }
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, gxy);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, gxz);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, gyz);

  /*     Time-symmetric data
   *     -------------------
   */

  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kxx);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kxy);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kxz);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kyy);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kyz);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kzz);

}

```

misc.c:
```
 /*@@
   @file      misc.c
   @date      23.Mar.2003
   @author    Jonathan Thornburg <jthorn@aei.mpg.de>
   @desc 
      Misc support routines for this thorn.
   @enddesc 
   @version $Header$
 @@*/

#include "cctk.h"
#include "cctk_Arguments.h"

#include "IDAnalyticBH.h"

static const char *rcsid = "$Header$";

CCTK_FILEVERSION(CactusEinstein_IDAnalyticBH_Misc_c)


/*@@
  @routine    IDAnalyticBH_zero_CCTK_REAL_array
  @date       23.Mar.2003
  @author     Jonathan Thornburg <jthorn@aei.mpg.de>
  @desc       Sets a CCTK_REAL array to all (floating-point) zeros.
  @enddesc 
  @@*/
void IDAnalyticBH_zero_CCTK_REAL_array(int N, CCTK_REAL A[])
{
int i;
  for (i = 0 ; i < N ; ++i)
  {
    A[i] = 0.0;
  }
}

```

Misner_points.c:
```
 /*@@
   @file      Misner_points.c
   @date
   @author    Steve Brandt
   @desc
              This calculates the conformal factor for nbh black holes,
              with naked mass m0 = 2 csch(mu) each, and placed on a circle in the
              xy plane around the origin, of radius coth(mu).
              One of them sits on the positive x axis, the others are evenly spaced.
              Naked mass here corresponds to the term m0 / (2 |r - r0|) in the expansion.
   @history
   @hdate 6.May.2003
   @hauthor Jonathan Thornburg
   @hdesc code cleanup, add prototypes
   @endhistory
   @enddesc
 @@*/

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

#include <math.h>

#include "cctk.h"
#include "cctk_Arguments.h"
#include "cctk_Parameters.h"

#include "IDAnalyticBH.h"

static const char *rcsid = "$Header$";

CCTK_FILEVERSION(CactusEinstein_IDAnalyticBH_Misner_points_c)

/******************************************************************************/

/*
 * data structures local to this file
 */

/* Basic data about a brill-lindquist black hole term. */
struct bhole {
  CCTK_REAL x,y;
  CCTK_REAL mass;

  /* i gives either the number of the seed
     black hole we are starting with, or
     the number of the seed black hole that
     was used to isometrize this term. */
  int i;

  /* isos points to an array of nbhole (or is it nbhole-1??)
     structures, malloc()-ed in  fill_iso() .
     At present this array is never freed, and in fact the pointers may
     be overwritten with other malloc()s in fill_iso() recursive calls! :( */
  struct bhole *isos;
};

/******************************************************************************/

/*
 * static data
 */

static int nbholes;

#define MAXBHOLES 10
/* The seed black holes. */
struct bhole bholes[MAXBHOLES];

/******************************************************************************/

/*
 * prototypes for functions local to this file
 */
static CCTK_REAL csch(CCTK_REAL theta);
static CCTK_REAL coth(CCTK_REAL theta);
static void iso(struct bhole *a1, struct bhole *a2, struct bhole *a3);
static void fill_iso(struct bhole *b, int n);
static CCTK_REAL eval_bh_psi(const struct bhole *b,
                             CCTK_REAL x, CCTK_REAL y, CCTK_REAL z);

/******************************************************************************/
/***** functions visible outside this file ************************************/
/******************************************************************************/

 /*@@
   @routine    Misner_init
   @date
   @author     Steve Brandt
   @desc
      Initialises the black holes then makes the isometry black holes
   @enddesc
   @calls
   @history
   @endhistory
@@*/
void Misner_init(int n, CCTK_REAL mu, int terms)
{

  int i;
  CCTK_REAL pi,ang;

  if (! ((nbholes=n) < MAXBHOLES))
    CCTK_VWarn (0, __LINE__, __FILE__,"IDAnalyticBH", "Number of black holes exceed maximum number of black holes");

  pi = 4.0*atan(1.);

  ang = 2.*pi/(n);

  for(i=0;i<n;i++)
  {
    bholes[i].x = coth(mu)*cos(ang*i);
    bholes[i].y = coth(mu)*sin(ang*i);
    bholes[i].mass = csch(mu);
    bholes[i].i = i;
    bholes[i].isos = 0;
  }

  CCTK_VWarn(1, __LINE__, __FILE__, CCTK_THORNSTRING,
"\n"
"   Misner_init(): warning: about to call  fill_iso() ; at present\n"
"                  this routine seems to leak quasi-infinite amounts of\n"
"                  memory at the rate of O(100 megabytes/second) :( :( :(\n");

  for(i=0;i<n;i++)
    fill_iso(&bholes[i],terms);

}

/******************************************************************************/

 /*@@
   @routine    MisnerEvalPsi
   @date
   @author     Steve Brandt
   @desc
      Evaluate psi at a point
   @enddesc
   @calls      eval_bh_psi

   @history
   @hdate      6.May.2003
   @hauthor    Jonathan Thornburg <jthorn@aei.mpg.de>
   @hdesc      Return result directly rather than via a pointer argument.
   @endhistory
 @@*/
CCTK_REAL MisnerEvalPsi(CCTK_REAL x, CCTK_REAL y, CCTK_REAL z)
{
  int i;
  CCTK_REAL sum = 1.0;

  for(i=0;i<nbholes;i++)
  {
    sum += eval_bh_psi(&bholes[i],x,y,z);
  }

  return sum;
}

/******************************************************************************/
/***** functions local to this file *******************************************/
/******************************************************************************/

static CCTK_REAL csch(CCTK_REAL theta) {
  return 1.0/sinh(theta);
}

/******************************************************************************/

static CCTK_REAL coth(CCTK_REAL theta) {
  return cosh(theta)/sinh(theta);
}

/******************************************************************************/

 /*@@
   @routine    iso
   @date
   @author     Steve Brandt
   @desc
               Isometrize black hole a1 through hole a2
   @enddesc
   @calls      iso
   @history

   @endhistory

@@*/

static void iso(struct bhole *a1, struct bhole *a2, struct bhole *a3)
{
  CCTK_REAL rad,radtwo;
  radtwo=(
      (a1->x - a2->x)*(a1->x - a2->x)+
      (a1->y - a2->y)*(a1->y - a2->y)
    );
  rad=sqrt(radtwo);
  a3->mass = a1->mass*a2->mass/rad;
  a3->x = a2->x+(a2->mass*a2->mass)*(a1->x - a2->x)/radtwo;
  a3->y = a2->y+(a2->mass*a2->mass)*(a1->y - a2->y)/radtwo;
}

/******************************************************************************/

 /*@@
   @routine    fill_iso
   @date
   @author     Steve Brandt
   @desc
               Fills in the iso structure of a given black hole.
               Applies recursively to the number of terms desired.
   @enddesc
   @calls      fill_iso
   @history

   @endhistory

@@*/

static void fill_iso(struct bhole *b, int n)
{
  int i,j;
  if(n==0)
  {
    b->isos = 0;
    return;
  }
  b->isos = (struct bhole *)malloc(sizeof(struct bhole)*(nbholes-1));
  if (! (b->isos != 0))
    CCTK_VWarn (0, __LINE__, __FILE__,"IDAnalyticBH", "error in function fill_iso");
  for(j=0, i=0;i<nbholes;i++)
  {
    if(i != b->i) {
      iso(b,&bholes[i],&b->isos[j]);
      b->isos[j].i = i;
      fill_iso(&b->isos[j],n-1);
      j++;
    }
  }
}

/******************************************************************************/

 /*@@
   @routine    eval_bh_psi
   @date
   @author     Steve Brandt
   @desc

   @enddesc
   @calls      eval_bh_psi
   @history

   @endhistory

@@*/
static CCTK_REAL eval_bh_psi(const struct bhole *b,
                             CCTK_REAL x, CCTK_REAL y, CCTK_REAL z)
{
  int i;
  CCTK_REAL res;
  res = 0.0;
  if(b->isos != 0)
  {
    for(i=0;i<nbholes-1;i++)
    {
      res += eval_bh_psi(&b->isos[i],x,y,z);
    }
  }
  res += b->mass/sqrt(
      (x - b->x)*(x - b->x)+
      (y - b->y)*(y - b->y)+
      z*z
    );
  return res;
}

```

Schwarzschild.c:
```

 /*@@
   @file      Schwarzschild.c
   @date      Sun Oct 17 10:35:41 1999
   @author    Tom Goodale
   @desc
              C version of Scwhwarzschild lapse routine
   @enddesc
   @version   $Id$
 @@*/

#include <math.h>
#include <string.h>

#include "cctk.h"
#include "cctk_Arguments.h"
#include "cctk_Parameters.h"

#include "IDAnalyticBH.h"

static const char *rcsid = "$Header$";
CCTK_FILEVERSION(CactusEinstein_IDAnalyticBH_Schwarzschild_c)


void Schwarzschild(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_ARGUMENTS
  DECLARE_CCTK_PARAMETERS

  const CCTK_REAL zero = 0.0, one = 1.0, two = 2.0, three = 3.0;
  CCTK_REAL tmp, r_squared, r_cubed;
  int make_conformal_derivs = 0;
  int i, npoints;

  CCTK_VInfo(CCTK_THORNSTRING,
             "setting up Schwarzschild initial");

  /* Check if we should create and store conformal factor stuff */
  if(CCTK_EQUALS(metric_type, "static conformal"))
  {
    if      (CCTK_EQUALS(conformal_storage,"factor"))
    {
      *conformal_state = 1;
      make_conformal_derivs = 0;
    }
    else if (CCTK_EQUALS(conformal_storage,"factor+derivs"))
    {
      *conformal_state = 2;
      make_conformal_derivs = 1;
    }
    else if (CCTK_EQUALS(conformal_storage,"factor+derivs+2nd derivs"))
    {
      *conformal_state = 3;
      make_conformal_derivs = 1;
    }
    else
    {
      CCTK_VWarn(0, __LINE__, __FILE__, CCTK_THORNSTRING,
"Schwarzschild(): impossible value for conformal_storage=\"%s\"!",
                 conformal_storage);                            /*NOTREACHED*/
    }
  }      


  npoints = cctk_lsh[0] * cctk_lsh[1] * cctk_lsh[2];

  if(CCTK_EQUALS(metric_type, "static conformal"))
  {
    for (i = 0; i < npoints; i++)
    {
      /*        Compute conformal factor */
      CCTK_REAL const rr = pow(pow(r[i], 4) + pow(epsilon, 4), 0.25);

      psi[i] = (one + mass / (two*rr));

      if(make_conformal_derivs)
      {
        /*        derivatives of psi / psi */
        r_squared = rr*rr;
        r_cubed   = rr*r_squared;
        tmp = mass / (two*r_cubed*psi[i]);


        psix[i] = -x[i]*tmp;
        psiy[i] = -y[i]*tmp;
        psiz[i] = -z[i]*tmp;

        if(*conformal_state > 2)
        {
          tmp = mass/(two*r_squared*r_cubed*psi[i]);
          psixy[i] = three*x[i]*y[i]*tmp;
          psixz[i] = three*x[i]*z[i]*tmp;
          psiyz[i] = three*y[i]*z[i]*tmp;
      
          psixx[i]  = (three*x[i]*x[i] - r_squared)*tmp;
          psiyy[i]  = (three*y[i]*y[i] - r_squared)*tmp;
          psizz[i]  = (three*z[i]*z[i] - r_squared)*tmp;
        }
      }
      gxx[i] = one;
      gyy[i] = one;
      gzz[i] = one;
      gxy[i] = zero;
      gxz[i] = zero;
      gyz[i] = zero;
    }
  }
  else
  {
    for (i = 0; i < npoints; i++)
    {
      CCTK_REAL const rr = pow(pow(r[i], 4) + pow(epsilon, 4), 0.25);

      tmp = one + mass / (two*rr);
      gxx[i] = tmp*tmp*tmp*tmp;
      gyy[i] = gxx[i];
      gzz[i] = gxx[i];
      gxy[i] = zero;
      gxz[i] = zero;
      gyz[i] = zero;
    }
  }
  
  /*     If the initial lapse is not one ... */
  if (CCTK_Equals(initial_lapse,"schwarzschild")
      || CCTK_Equals(initial_lapse,"schwarz"))
  {
    CCTK_INFO("Initialise with Schwarzschild lapse");

    for (i = 0; i < npoints; i++)
    {
      alp[i] = (two*r[i] - mass) / (two*r[i] + mass);
    }
  }

  /*     time symmetric initial slice */
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kxx);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kxy);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kxz);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kyy);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kyz);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kzz);
}

```


combined_doc_context: ## README (README):
```
Cactus Code Thorn IDAnalyticBH
Author(s)    : Steve Brandt
               Carsten Gundlach
               Joan Masso
               Ed Seidel
               Paul Walker
Maintainer(s): Cactus team
Licence      : LGPL
--------------------------------------------------------------------------

1. Purpose

This thorn calculates analytic initial data for the Einstein grid functions
(lapse, shift, metric, curv) for various black hole spacetimes:

	Schwarzschild black hole
 	2 Misner black holes placed on the z-axis
	n Misner black holes placed in a circle in the x-y plane
	Brill-Lindquist data



```

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

```tex
=== documentation.tex ===
% /*@@
%   @file      documentation.tex
%   @date      28 April 2002
%   @author    Denis Pollney
%   @desc 
%              IDAnalyticBH users guide.
%   @enddesc 
%   @version $Header$
% @@*/

%
% FIXME: Is the non-conformal schwarzschild correct in the source
% FIXME:   code???
% FIXME: Add a reference to the quasi-isotropic Kerr coordinates?
% FIXME: What is the Cadez lapse for Misner?
% FIXME: The distance calculation (tmp1) seems wrong in the
% FIXME:   brill-lindquist code.
% FIXME: Check that figure was included for Cartoon docs.


\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}

\title{Using \texttt{IDAnalyticBH}}
\author{Denis Pollney}
\date{$ $Date$ $}

\maketitle

% Do not delete next line
% START CACTUS THORNGUIDE

\begin{abstract}
The \texttt{IDAnalyticBH} thorn contains a number of initial data
sets for black-hole evolutions which can be specified analytically
as metric and extrinsic curvature components. The initial data which
is included in this thorn include single Schwarzschild and Kerr black
holes, and multiple black hole Misner and Brill-Lindquist solutions.
\end{abstract}


\section{Background}

The \texttt{IDAnalyticBH} thorn exists as a central location to place any
initial dataset for black hole evolution that can be specified
analytically in terms of the metric, $g_{ab}$, and extrinsic
curvature, $K_{ab}$.

The thorn extends the \texttt{admbase::initial\_data} parameter by
adding the following datasets:
\begin{description}
  \item[\texttt{schwarzschild}] Schwarzschild, in isotropic
  coordinates;
  \item[\texttt{kerr}] Kerr, in Boyer-Lindquist coordinates;
  \item[\texttt{misner\_bh}] 2~Misner black holes;
  \item[\texttt{multiple\_misner\_bh}] $N \ge 2$ Misner black holes;
	(Alas this code is currently broken; see the level~1
	\verb|CCTK_VWarn()| warning in \verb|src/Misner_points.c|,
	function \verb|Misner_init()|.)

  \item[\texttt{bl\_bh}] Multiple Brill-Lindquist black holes.
\end{description}
Initial data for lapse and shift can also be specified in
this thorn.\\

The Cactus grid-functions corresponding to the initial data are
inherited from the thorn \texttt{CactusEinstein/ADMBase}, along with
the conformal factor grid-function, \texttt{psi} from \texttt{CactusEinstein/StaticConformal}, and its derivatives
which are optionally set based on the value of the parameters
\texttt{admbase::metric\_type} and \texttt{staticconformal::conformal\_storage}.\\

The \texttt{IDAnalyticBH} has been written and augmented over an number of
years by many Cactus authors. These include John Baker, Steve Brandt,
Carsten Gundlach, Joan Masso, Ed Seidel, Jonathan Thornburg, and
Paul Walker.
The following sections describe each of the initial datasets
and their associated parameters in turn.

\section{Schwarzschild}

The Schwarzschild metric corresponds to a single, static, black hole.
If the Cactus metric is specified as a conformal metric (by setting
\texttt{admbase::metric\_type="yes"}), then the metric is
set using isotropic coordinates \cite{CactusEinstein_IDAnalyticBH_mtw-isotropic}:
\begin{equation}
  ds^2 = -\left(\frac{2r - M}{2r + M}\right)^2
  + \left(1 + \frac{M}{2r}\right)^4 \left(dr^2 + r^2(d\theta^2
  + \sin^2\theta d\phi^2)\right),
\end{equation}
with the Schwarzschild mass given by the single free parameter $M$.
Thus, the three metric and extrinsic curvature have the values:
\begin{eqnarray}
  \hat{g}_{ab} & = & \psi^4 \delta_{ab}, \\
  \psi & = & (1 + \frac{M}{2r}), \\
  K_{ab} & = & 0.
\end{eqnarray}

The mass is specified using the parameter
\texttt{idanalyticbh::mass}. The black hole is assumed to reside at
the origin of the grid, corresponding to the location $x=y=z=0$.\\

If the \texttt{admbase::metric\_type} parameter has been set to {\tt static conformal}, then
the metric grid-functions (\texttt{admbase::gxx}, $\ldots$,
\texttt{admbase::gzz}) are given as $\delta_{ab}$, and the conformal
factor \texttt{staticconformal::psi} is set to the value specified
above. The derivatives of the conformal factor
(\texttt{staticconformal::psix}, etc.) are determined analytically.

In order to give the lapse an initial profile which corresponds to
isotropic lapse of the $4$-metric specified above, use the parameter
\begin{verbatim}
  admbase::initial_lapse = "schwarz"
\end{verbatim}
This will cause the \texttt{admbase::alp} grid-function to be
initialised to the value:
\begin{equation}
  \alpha = \frac{2r - M}{2r + M}.
\end{equation}\\


Note that the Schwarzschild data has the following non-standard
behaviour in response to the \texttt{admbase::metric\_type}
parameter. If the \emph{physical} metric is requested
(ie. \texttt{metric\_type} is set to \texttt{"physical"}) then a
\emph{different} form of the Schwarzschild metric is set:
Schwarzschild coordinates are set instead of the isotropic
coordinates:
\begin{equation}
  g_{xx} = g_{yy} = g_{zz} = 1 + 2M/r.
\end{equation}\\


In order to carry out an evolution of a single Schwarzschild
black hole of mass $m=1$, using an initial lapse of $\alpha=1$, you
could modify your parameter file as follows:

\begin{verbatim}
  ActiveThorns = "... ADMBase StaticConformal IDAnalyticBH ..."

  admbase::metric_type = "static conformal"

  admbase::initial_data = "schwarzschild"
  admbase::initial_lapse = "one"            # or "schwarz" for isotropic lapse

  idanalyticbh::mass = 1.0
\end{verbatim}


\section{Kerr}

Kerr initial data for an isolated rotating black hole is specified
using the ``quasi-isotropic'' coordinates \cite{CactusEinstein_IDAnalyticBH_brandt-seidel:1996}:
\begin{equation}
  ds^2 = \psi^4 (dr^2 + r^2(d\theta^2 + \chi^2\sin^2\theta d\phi^2)),
\end{equation}
where
\begin{eqnarray}
  \psi^4 & = & - 2\frac{a^2}{r^2}\cos\theta\sin\theta, \\
  \chi^2 & = & p^2 / \Sigma, \\
  p^2 & = & a^2 + {r_k}^2 - a B_\phi, \\
  r_k & = & r + M + \frac{M^2 - a^2}{4r}, \\
  B_\phi & = & -2 M r_k a \sin^2\theta / \Sigma,\\
  \Sigma & = & {r_k}^2 + a^2 \cos^2\theta.
\end{eqnarray}
The two free parameters are the Kerr mass, $M$, and angular momentum,
$a$ (assumed to be aligned with the z-axis).
These are specified using the parameters
\texttt{idanalyticbh::mass} and \texttt{idanalyticbh::a\_kerr}
respectively. \emph{(Note that the default values for these parameters
are $M=2$ and $a=0.1$.)} The black hole is assumed to reside at the
centre of the coordinate system, at $x=y=z=0$.

The \texttt{admbase::metric\_type} parameter can be used to specify
whether the metric should be conformal or not. If the metric is
conformal, then $\psi$ is initialised as a separate grid function, and
it's first and second derivatives are calculated analytically and also
stored as grid functions. Otherwise, the conformal factor is
multiplied through in the expression for the 3-metric before the
values of the \texttt{admbase::metric} variables are set. The
extrinsic curvature is also determined analytically.

The gauge can be set to the Kerr lapse and shift with the parameters
\begin{verbatim}
  idanalyticbh::initial_lapse = "kerr"
  idanalyticbh::initial_shift = "kerr"
\end{verbatim}
in which case the formulas
\begin{eqnarray}
  \alpha & = &\sqrt{\frac{\Delta}{p^2}}, \\
  \beta^\phi & = & -2 m r_k a / (p^2 \Sigma),
\end{eqnarray}
where
\begin{equation}
  \sqrt{\Delta} = r - \frac{m^2 - a^2}{4r}.
\end{equation}

A set of parameters which initialise an evolution to use the Kerr
intial data with mass $M=1$ and angular momentum $a=0.3$ are:
\begin{verbatim}
  ActiveThorns = "... ADMBase StaticConformal IDAnalyticBH ..."

  admbase::metric_type = "static conformal"

  admbase::initial_data = "kerr"
  admbase::initial_lapse = "kerr"
  admbase::initial_shift = "kerr"

  idanalyticbh::mass = 1.0
  idanalyticbh::a_kerr = 0.3
\end{verbatim}

\section{Misner}

The earliest suggestion for initial data that might be said to
corresponding to multiple black holes was given by Misner in 1960
\cite{CactusEinstein_IDAnalyticBH_misner:1960}. He provided a prescription for writing a metric
connecting a pair of massive bodies, instaneously at rest, whose
throats are connected by a wormhole. Using the method of images, this
solution was generalised to describe any number of black holes whose
throats connect two identical asymptotically flat spacetimes
\cite{CactusEinstein_IDAnalyticBH_misner:1963}.
\begin{figure}
  \centering
  \includegraphics[height=40mm]{misner}
  \caption{The topology of the Misner spacetime is that of a pair of
  asymptotically flat sheets connected by a number of Einstein-Rosen
  bridges. By construction, an exact isometry exists between the upper
  and lower sheet across the throats. The parameter $\mu_0$ is a
  measure of the distance of a loop in the surface, passing through
  one throat and out the other.}
\end{figure}

Two implementations of the Misner data are available. The first of
these, ``\texttt{misner\_bh}'', is due to Joan Masso, Ed Seidel and
Karen Camarda, and implements the original two-throat solution. The
more general solution was implemented by Steve Brandt and Carsten
Gundlach, and is available as ``\texttt{multiple\_misner\_bh}''.

\subsection{Two-throat Misner data}

The \texttt{misner\_bh} initial data generates a metric of the form
\begin{equation}
  ds^2 = -dt^2 + \psi^4 (dx^2 + dy^2 + dz^2),
\end{equation}
where the conformal factor $\psi$ is given by
\begin{equation}
  \psi = \sum^N_{n=-N}
  \frac{1}{\sinh(\mu_0 n)}
  \frac{1}{\sqrt{x^2 + y^2 + (z + \coth(\mu_0 n))^2}}.
\end{equation}
The extrinsic curvature for the Misner data is zero.

The parameter $\mu_0$ is a measure of the ratio of mass to separation
of the throats, and is set using the parameter
\texttt{idanalyticbh::mu}. For values less than $\mu\simeq 1.8$, the
throats will have a single event horizon.

The summation limit $N$ can be set using the parameter
\texttt{idanalyticbh::nmax}. Ideally, it should tend to infinity, but
in practice the default value of $N=30$ works well enough for the
applications that have been tested. The \texttt{misner\_nbh} parameter
is only used for the \texttt{multiple\_misner\_bh} multi-throat data,
and will be ignored for the \texttt{misner\_bh} initial data, which
assumes two throats.

For the given metric, the ADM mass of the system is determined via
\begin{equation}
  m = 4 \sum^N_{n=1} \frac{1}{\sinh(\mu_0 n)}.
\end{equation}
This quantity is determined automatically and written to standard
output.

If the conformal form of the metric is used (via the
\texttt{admbase::metric\_type} parameter), then derivatives of the
conformal factor are computed analytically from the derivatives of the
above expression for $\psi$.

To make use of the two black hole initial data, a variation of the
following set of parameters can be used:
\begin{verbatim}
  ActiveThorns = "... ADMBase StaticConformal IDAnalyticBH ..."

  admbase::metric_type = "static conformal"

  admbase::initial_data = "misner_bh"
  idanalyticbh::mu = 2.2
\end{verbatim}


\section{Multiple-throat Misner data}

The generalisation of the above form of data to multiple black holes
is available as the \texttt{multiple\_misner\_bh} initial data set. The
conformal factor is determined by recursively applying a Misner
isometry condition to each of the black holes relative to the others.

The black holes are arranged at equal-spaced angles on a circle around
the origin in the $xy$-plane. The radius of the circle is $\coth\mu_0$,
where $\mu_0$ is given by the \texttt{idanalyticbh::mu} parameter, and
the first black hole lies on the $x$-axis (as in Figure
\ref{fig:multi_misner}).
\begin{figure}
  \centering
  \label{fig:multi_misner}
  \includegraphics[height=40mm]{multi_misner}
  \caption{Configuration for three Misner throats using the
  \texttt{multiple\_misner\_bh} initial data.}
\end{figure}

The number of throats is given by the parameter
\texttt{idanalyticbh::misner\_nbh}, which defaults to 1 and has a
hard-coded upper limit of 10. The number of terms used in the Misner
expansion is controlled by the parameter
\texttt{idanalyticbh::nmax}, which has a default value of 30.

For this version of the Misner data, derivatives of the conformal
factor $\psi$ are determined numerically by finite differencing,
using values of $\psi$ calculated at small distances from the point at
which the derivative is to be evaluated. The size of the numerical
stencil is hardcoded at $dx=10^-6$.

As an example, a parameter file implementing 3 Misner black holes on a
circle of radius $\cosh 4$ would use the following parameters:
\begin{verbatim}
  ActiveThorns = "... ADMBase StaticConformal IDAnalyticBH ..."

  admbase::metric_type = "static conformal"

  admbase::initial_data = "multiple_misner_bh"

  idanalyticbh::misner_nbh = 3
  idanalyticbh::mu = 4
\end{verbatim}


\section{Brill-Lindquist}

The Brill-Lindquist initial data is an alternate form of multi-throat
data which differs from the Misner data mainly in its choice of
spacetime topology. Whereas the Misner data presumes that the throats
connect a pair of asymptotically flat spacetimes which are identical
to each other, the Brill-Lindquist data connects each throat to a
separate asymptotically flat region \cite{CactusEinstein_IDAnalyticBH_brill-lindquist:1963}.
\begin{figure}
  \centering
  \includegraphics[height=40mm]{brill_lindquist}
  \caption{Two Brill-Lindquist throats connecting separate
    asymptotically flat regions.}
\end{figure}
The form of the conformal factor is:
\begin{equation}
  \psi = 1 + \sum_{i=1}^N \frac{m_i}{2r_i},
\end{equation}
where the $m_i$ and $r_i$ are the masses and positions of the $i$
particles.

The parameter specifying the number of black holes is
\texttt{idanalyticbh::bl\_nbh}. A maximum of four black holes can be
specified. The mass and $(x,y,z)$ position of the first black hole is
given by \texttt{bl\_M\_1}, \texttt{bl\_x0\_1}, \texttt{bl\_y0\_1},
\texttt{bl\_z0\_1}, with corresponding parameters for the second to
fourth black holes. Note that the default values for each of the
position coordinates are $0.0$, so that only the coordinates off
of the axes must be specified.

If the conformal metric is used, then derivatives of the conformal
factor are calculated from the analytic derivatives of the above
expression for the conformal factor.

To initialise a run with a pair of Brill-Lindquist black holes with
masses $1$ and $2$ and located at $\pm 1$ on the $y$-axis, a set of
parameters such as the following could be used:
\begin{verbatim}
  ActiveThorns = "... ADMBase StaticConformal IDAnalyticBH ..."

  admbase::metric_type = "static conformal"

  admbase::initial_data = "bl_bh"

  idanalyticbh::bl_nbh = 2

  idanalyticbh::bl_M_1 = 1.0
  idanalyticbh::bl_y0_1 = 1.0

  idanalyticbh::bl_M_2 = 2.0
  idanalyticbh::bl_y0_2 = -1.0
\end{verbatim}

\begin{thebibliography}{9}
  \bibitem{CactusEinstein_IDAnalyticBH_mtw-isotropic}
    See, for instance, p. 840 of:
    Misner, C. W., Thorne, K. S., and Wheeler, J. A. (1973)
    \emph{Gravitation}, W. H. Freeman, San Francisco.
  \bibitem{CactusEinstein_IDAnalyticBH_brandt-seidel:1996}
    Brandt, Steven R. and Seidel, Edward (1996)
    \emph{Evolution of distorted rotating black holes. III. Initial
    data},
    Phys. Rev., \textbf{D54}, 1403--1416.
  \bibitem{CactusEinstein_IDAnalyticBH_misner:1960}
    Misner, Charles W. (1960)
    \emph{Wormhole Initial Conditions},
    Phys. Rev., \textbf{118}, 1110--1111.
  \bibitem{CactusEinstein_IDAnalyticBH_misner:1963}
    Misner, Charles W. (1963)
    \emph{The Method of Images in Geometrostatics},
    Ann. Phys., \textbf{24}, 102--117.
  \bibitem{CactusEinstein_IDAnalyticBH_brill-lindquist:1963}
    Brill, Dieter R., and Lindquist, Richard W. (1963)
    \emph{Interaction Energy in Geometrostatics}
    Phys. Rev., \textbf{131}, 471--476.
\end{thebibliography}

% Do not delete next line
% END CACTUS THORNGUIDE

\end{document}

```



Reference answer:
src_code:  /*@@
   @file      BrillLindquist.F
   @date
   @author
   @desc
              Set up initial data for Brill Lindquist black holes
   @enddesc
   @version   $Header$
 @@*/

#include <math.h>
#include <string.h>

#include "cctk.h"
#include "cctk_Arguments.h"
#include "cctk_Parameters.h"

#include "IDAnalyticBH.h"

static const char *rcsid = "$Header$";
CCTK_FILEVERSION(CactusEinstein_IDAnalyticBH_BrillLindquist_c)

#define MAX_HOLES 4

 /*@@
   @routine    BrillLindquist
   @date
   @author
   @desc
               Set up initial data for Brill Lindquist black holes
   @enddesc
   @history
   @hdate Fri Apr 26 10:04:05 2002 @hauthor Tom Goodale
   @hdesc Changed to use new StaticConformal stuff
   @endhistory 
@@*/
void BrillLindquist(CCTK_ARGUMENTS)
{
  DECLARE_CCTK_ARGUMENTS
  DECLARE_CCTK_PARAMETERS

  int n;
  CCTK_REAL hole_mass[MAX_HOLES], hole_x0[MAX_HOLES];
  CCTK_REAL hole_y0[MAX_HOLES], hole_z0[MAX_HOLES];
  CCTK_REAL tmp1, tmp2, tmp3;
  CCTK_REAL xval, yval, zval;
  int i, npoints;
  int make_conformal_derivs = 0;

  CCTK_VInfo(CCTK_THORNSTRING,
             "setting up Brill-Lindquist initial data for %d black holes",
             (int)bl_nbh);

  /* Check if we should create and store conformal factor stuff */
  if(CCTK_EQUALS(metric_type, "static conformal"))
  {
    if      (CCTK_EQUALS(conformal_storage,"factor"))
    {
      *conformal_state = 1;
      make_conformal_derivs = 0;
    }
    else if (CCTK_EQUALS(conformal_storage,"factor+derivs"))
    {
      *conformal_state = 2;
      make_conformal_derivs = 1;
    }
    else if (CCTK_EQUALS(conformal_storage,"factor+derivs+2nd derivs"))
    {
      *conformal_state = 3;
      make_conformal_derivs = 1;
    }
    else
    {
      CCTK_VWarn(0, __LINE__, __FILE__, CCTK_THORNSTRING,
"BrillLindquist(): impossible value for conformal_storage=\"%s\"!",
                 conformal_storage);                            /*NOTREACHED*/
    }
  }


  npoints = cctk_lsh[0] * cctk_lsh[1] * cctk_lsh[2];

  /*     Put parameters into arrays for following calculations
   *     -----------------------------------------------------
   */
  hole_x0[0]   = -bl_x0_1;
  hole_y0[0]   = -bl_y0_1;
  hole_z0[0]   = -bl_z0_1;
  hole_mass[0] = bl_M_1;

  hole_x0[1]   = -bl_x0_2;
  hole_y0[1]   = -bl_y0_2;
  hole_z0[1]   = -bl_z0_2;
  hole_mass[1] = bl_M_2;

  hole_x0[2]   = -bl_x0_3;
  hole_y0[2]   = -bl_y0_3;
  hole_z0[2]   = -bl_z0_3;
  hole_mass[2] = bl_M_3;

  hole_x0[3]   = -bl_x0_4;
  hole_y0[3]   = -bl_y0_4;
  hole_z0[3]   = -bl_z0_4;
  hole_mass[3] = bl_M_4;

  
  if (make_conformal_derivs == 1)
  {
    IDAnalyticBH_zero_CCTK_REAL_array(npoints, psix);
    IDAnalyticBH_zero_CCTK_REAL_array(npoints, psiy);
    IDAnalyticBH_zero_CCTK_REAL_array(npoints, psiz);

    if(*conformal_state > 2)
    {
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psixx);
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psixy);
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psixz);
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psiyy);
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psiyz);
      IDAnalyticBH_zero_CCTK_REAL_array(npoints, psizz);
    }
  }

  for (i = 0; i < npoints; i++)
  {
    /*     Initialize to zero and then use +=
     *     ----------------------------------
     */

    psi[i] = 1.0;

    xval = x[i];
    yval = y[i];
    zval = z[i];

    for(n = 0;  n < bl_nbh; n++)
    {

      /*     Maple Output
       *     ------------
       */

      tmp1 = sqrt(SQR(xval+hole_x0[n])
                 +SQR(yval+hole_y0[n])
                 +SQR(zval+hole_z0[n])
                 +SQR(epsilon));

      psi[i] += hole_mass[n]/tmp1*0.5;

      if (make_conformal_derivs == 1)
      {
        tmp2 = 1 / (tmp1 * tmp1 * tmp1);
        tmp3 = 4 * (3.0 / 8.0) * hole_mass[n] * tmp2 / (tmp1 * tmp1);
        tmp2 *= -0.5 * hole_mass[n];

        psix[i] +=  tmp2 * (xval+hole_x0[n]);
        psiy[i] +=  tmp2 * (yval+hole_y0[n]);
        psiz[i] +=  tmp2 * (zval+hole_z0[n]);

        if(*conformal_state > 2)
        {
          psixx[i] += tmp3 * SQR(xval+hole_x0[n]) + tmp2;
          
          psixy[i] += tmp3 * (xval+hole_x0[n]) * (yval+hole_y0[n]);
          psixz[i] += tmp3 * (xval+hole_x0[n]) * (zval+hole_z0[n]);
          
          psiyy[i] += tmp3 * SQR(yval+hole_y0[n]) + tmp2;
          psiyz[i] += tmp3 * (yval+hole_y0[n]) * (zval+hole_z0[n]);
          
          psizz[i] += tmp3 * SQR(zval+hole_z0[n]) + tmp2;
        }
      }
    }
  }

  /*     Cactus conventions
   *     ------------------
   */

  if (make_conformal_derivs == 1)
  {
    for (i = 0; i < npoints; i++)
    {
      tmp1 = 1 / psi[i];

      psix[i]  *= tmp1;
      psiy[i]  *= tmp1;
      psiz[i]  *= tmp1;

      if(*conformal_state > 2)
      {
        psixx[i] *= tmp1;
        psixy[i] *= tmp1;
        psixz[i] *= tmp1;
        psiyy[i] *= tmp1;
        psiyz[i] *= tmp1;
        psizz[i] *= tmp1;
      }
    }
  }

  /*     Metric depends on conformal state
   *     ---------------------------------
   */

  if (CCTK_EQUALS(metric_type, "static conformal"))
  {
    for (i = 0; i < npoints; i++)
    {
      gxx[i] = 1.0;
      gyy[i] = 1.0;
      gzz[i] = 1.0;
      gxy[i] = 0.0;
      gxz[i] = 0.0;
      gyz[i] = 0.0;
    }
  }
  else
  {
    for (i = 0; i < npoints; i++)
    {
      tmp1 = psi[i] * psi[i];
      gxx[i] = tmp1 * tmp1;
      gyy[i] = gxx[i];
      gzz[i] = gxx[i];
      gxy[i] = 0.0;
      gxz[i] = 0.0;
      gyz[i] = 0.0;
    }
  }

  /*     Time-symmetric data
   *     -------------------
   */
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kxx);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kxy);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kxz);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kyy);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kyz);
  IDAnalyticBH_zero_CCTK_REAL_array(npoints, kzz);
}