#!/usr/bin/env python


from math import *
import sys
import re
from string import Template

################################################################################
# Binary black hole configuration
################################################################################

# BHs labeled as '+' and '-' ('p' and 'm') for their initial position
# on the x axis.  The more massive BH is '+'.

final_time = 300

D = 100.0                    # Separation
q = 1.0               # Mass ratio: q = mp/mm >= 1
M = 1.0                     # Total mass
chip = [0, 0, 0]        # Dimensionsless spin of + BH (x0 > 0, more massive)
chim = [0, 0, 0]        # Dimensionsless spin of - BH (x0 < 0, less massive)

Pabs = 0.49
theta = 0.07

Pr = -Pabs * cos(theta)  # Radial linear momentum
Pphi = Pabs * sin(theta) # Azimuthal linear momentum

################################################################################
# Initial data
################################################################################

mp = M * q/(1+q)           # Heavier, larger BH, AH1, SS 0
mm = M * 1/(1+q)           # Lighter, smaller BH, AH2, SS 1

xp     = D * mm
xm     = -D * mp
half_D = D/2.0

center_offset = xp - half_D

Spx = chip[0] * mp**2;
Spy = chip[1] * mp**2;
Spz = chip[2] * mp**2;

Smx = chim[0] * mm**2;
Smy = chim[1] * mm**2;
Smz = chim[2] * mm**2;

Ppx = Pr
Ppy = Pphi
Ppz = 0

Pmx = -Ppx
Pmy = -Ppy
Pmz = -Ppz

# Estimated eventual AH radii
ahrp = mp * 1.0
ahrm = mm * 1.0

# Coordinate size of finest boxes around each BH
rp = ahrp * 1.2
rm = ahrm * 1.2


outer_bound = 768.0
d_r = 12.8

max_rls = 10

rlsp = 10
levelsp = "[512.0, 256.0, 128.0, 64.0, 32.0, 16.0, 8.0, 4.0, 2.0, 1.0]"

rlsm = 10
levelsm = "[512.0, 256.0, 128.0, 64.0, 32.0, 16.0, 8.0, 4.0, 2.0, 1.0]"


local_vars = locals()

col_width = 0
for key,val in sorted(local_vars.items()):
    if isinstance(val, (int, float, complex, str)):
        col_width = max(len(str(key)), col_width)

var_settings = []
for key,val in sorted(local_vars.items()):
    if isinstance(val, (int, float, complex, str)):
        var_settings = var_settings + ["# {0}{1} = {2}".format(key," "*(col_width-len(key)),val)]

var_settings_str = "\n".join(var_settings)

lines = """
Cactus::cctk_run_title = "BBH"

Cactus::cctk_full_warnings         = yes
Cactus::highlight_warning_messages = no

Cactus::terminate       = "time"
Cactus::cctk_final_time = $final_time


ActiveThorns = "IOUtil"

IO::out_dir = "@SIMULATION_NAME@"



ActiveThorns = "AEILocalInterp"

#ActiveThorns = "BLAS LAPACK"

ActiveThorns = "Fortran"

ActiveThorns = "GSL"

ActiveThorns = "GenericFD"

#ActiveThorns = "HDF5"

ActiveThorns = "LocalInterp"

ActiveThorns = "LoopControl SystemTopology"

ActiveThorns = "Slab"

ActiveThorns = "QuasiLocalMeasures"


ActiveThorns = "SummationByParts"

SummationByParts::order = 4



ActiveThorns = "InitBase"



ActiveThorns = "Carpet CarpetLib CarpetInterp CarpetReduce CarpetSlab"

Carpet::verbose           = no
Carpet::veryverbose       = no
Carpet::schedule_barriers = no
Carpet::storage_verbose   = no
#Carpet::timers_verbose    = no
CarpetLib::output_bboxes  = no

Carpet::domain_from_coordbase = yes
Carpet::max_refinement_levels = $max_rls

driver::ghost_size       = 3
Carpet::use_buffer_zones = yes

Carpet::prolongation_order_space = 5
Carpet::prolongation_order_time  = 2

Carpet::convergence_level = 0

Carpet::init_fill_timelevels = yes
#Carpet::init_3_timelevels = yes

Carpet::poison_new_timelevels = yes
CarpetLib::poison_new_memory  = yes

Carpet::output_timers_every      = 640
CarpetLib::print_timestats_every = 640
CarpetLib::print_memstats_every  = 640


################################################################################
# Isolated Horizons
################################################################################

QuasiLocalMeasures::verbose                = no
QuasiLocalMeasures::veryverbose            = no
QuasiLocalMeasures::interpolator           = "Lagrange polynomial interpolation"
QuasiLocalMeasures::interpolator_options   = "order=4"
QuasiLocalMeasures::spatial_order          = 4
QuasiLocalMeasures::num_surfaces           = 3
QuasiLocalMeasures::surface_index      [0] = 2
QuasiLocalMeasures::surface_index      [1] = 3
QuasiLocalMeasures::surface_index      [2] = 4
#QuasiLocalMeasures::output_vtk_every       = 16


ActiveThorns = "NaNChecker"

NaNChecker::check_every     = 1 # 64
#NaNChecker::verbose         = "all"
#NaNChecker::action_if_found = "just warn"
NaNChecker::action_if_found = "terminate"
NaNChecker::check_vars      = "
        ML_BSSN::ML_log_confac
        ML_BSSN::ML_metric
        ML_BSSN::ML_trace_curv
        ML_BSSN::ML_curv
        ML_BSSN::ML_Gamma
        ML_BSSN::ML_lapse
        ML_BSSN::ML_shift
        ML_BSSN::ML_dtlapse
        ML_BSSN::ML_dtshift
        ADMBase::metric
        ADMBase::curv
        ADMBase::lapse
        ADMBase::shift
        ADMBase::dtlapse
        ADMBase::dtshift
"



#ActiveThorns = "Boundary CartGrid3D CoordBase ReflectionSymmetry RotatingSymmetry180 SymBase"
ActiveThorns = "Boundary CartGrid3D CoordBase ReflectionSymmetry SymBase"

CoordBase::domainsize = "minmax"

CoordBase::xmin = -$outer_bound
CoordBase::ymin = -$outer_bound
CoordBase::zmin = 0.00
CoordBase::xmax = $outer_bound
CoordBase::ymax = $outer_bound
CoordBase::zmax = $outer_bound
CoordBase::dx   = $d_r
CoordBase::dy   = $d_r
CoordBase::dz   = $d_r

CoordBase::boundary_size_x_lower     = 3
CoordBase::boundary_size_y_lower     = 3
CoordBase::boundary_size_z_lower     = 3
CoordBase::boundary_size_x_upper     = 3
CoordBase::boundary_size_y_upper     = 3
CoordBase::boundary_size_z_upper     = 3

#CoordBase::boundary_shiftout_x_lower = 1
CoordBase::boundary_shiftout_z_lower = 1

CartGrid3D::type = "coordbase"

ReflectionSymmetry::reflection_z   = yes
ReflectionSymmetry::avoid_origin_z = no



ActiveThorns = "SphericalSurface"

SphericalSurface::nsurfaces = 5
SphericalSurface::maxntheta = 39
SphericalSurface::maxnphi   = 76

SphericalSurface::ntheta      [0] = 39
SphericalSurface::nphi        [0] = 76
SphericalSurface::nghoststheta[0] = 2
SphericalSurface::nghostsphi  [0] = 2

SphericalSurface::ntheta      [1] = 39
SphericalSurface::nphi        [1] = 76
SphericalSurface::nghoststheta[1] = 2
SphericalSurface::nghostsphi  [1] = 2

SphericalSurface::ntheta      [2] = 39
SphericalSurface::nphi        [2] = 76
SphericalSurface::nghoststheta[2] = 2
SphericalSurface::nghostsphi  [2] = 2

SphericalSurface::ntheta      [3] = 39
SphericalSurface::nphi        [3] = 76
SphericalSurface::nghoststheta[3] = 2
SphericalSurface::nghostsphi  [3] = 2

SphericalSurface::ntheta      [4] = 39
SphericalSurface::nphi        [4] = 76
SphericalSurface::nghoststheta[4] = 2
SphericalSurface::nghostsphi  [4] = 2



ActiveThorns = "CarpetMask"

CarpetMask::verbose = no

CarpetMask::excluded_surface       [0] = 3
CarpetMask::excluded_surface_factor[0] = 1.0

CarpetMask::excluded_surface       [1] = 4
CarpetMask::excluded_surface_factor[1] = 1.0



ActiveThorns = "ADMBase ADMCoupling ADMMacros CoordGauge SpaceMask StaticConformal TmunuBase"
ActiveThorns = "CarpetRegrid2 PunctureTracker CarpetTracker"

CarpetTracker::surface[0] = 0
CarpetTracker::surface[1] = 1
PunctureTracker::track                      [0] = yes
PunctureTracker::initial_x                  [0] = $xp
PunctureTracker::which_surface_to_store_info[0] = 0
PunctureTracker::track                      [1] = yes
PunctureTracker::initial_x                  [1] = $xm
PunctureTracker::which_surface_to_store_info[1] = 1


CarpetRegrid2::regrid_every            = 16
CarpetRegrid2::freeze_unaligned_levels = yes
CarpetRegrid2::symmetry_rotating180    = no
CarpetRegrid2::verbose                 = yes

CarpetRegrid2::num_centres = 2

CarpetRegrid2::num_levels_1         =  $rlsp
CarpetRegrid2::position_x_1         =  $xp
CarpetRegrid2::radius_1             =  $levelsp
#CarpetRegrid2::movement_threshold_1 =   0.16

CarpetRegrid2::num_levels_2         =  $rlsm
CarpetRegrid2::position_x_2         =  $xm
CarpetRegrid2::radius_2             =  $levelsm
#CarpetRegrid2::movement_threshold_2 =   0.16


ActiveThorns = "MoL Time"

MoL::ODE_Method             = "RK4"
MoL::MoL_Intermediate_Steps = 4
MoL::MoL_Num_Scratch_Levels = 1

Carpet::time_refinement_factors = "[1, 1, 2, 4, 8, 16, 32, 64, 128, 256]"

Time::dtfac = 0.25


ADMMacros::spatial_order = 4



ActiveThorns = "TwoPunctures"

ADMBase::metric_type = "physical"

ADMBase::initial_data    = "twopunctures"
ADMBase::initial_lapse   = "twopunctures-averaged"
ADMBase::initial_shift   = "zero"
ADMBase::initial_dtlapse = "zero"
ADMBase::initial_dtshift = "zero"

TwoPunctures::target_M_plus             = $mp
TwoPunctures::target_M_minus            = $mm

# Initial guesses for iterative solve
TwoPunctures::par_m_plus                = $mp
TwoPunctures::par_m_minus               = $mm

TwoPunctures::par_b                     = $half_D
TwoPunctures::center_offset[0]          = $center_offset

TwoPunctures::par_P_plus[0]             = $Ppx
TwoPunctures::par_P_plus[1]             = $Ppy
TwoPunctures::par_P_plus[2]             = $Ppz

TwoPunctures::par_P_minus[0]            = $Pmx
TwoPunctures::par_P_minus[1]            = $Pmy
TwoPunctures::par_P_minus[2]            = $Pmz

TwoPunctures::par_S_plus[0]             = $Spx
TwoPunctures::par_S_plus[1]             = $Spy
TwoPunctures::par_S_plus[2]             = $Spz

TwoPunctures::par_S_minus[0]            = $Smx
TwoPunctures::par_S_minus[1]            = $Smy
TwoPunctures::par_S_minus[2]            = $Smz

TwoPunctures::npoints_A         = 50
TwoPunctures::npoints_B         = 50
TwoPunctures::npoints_phi       = 20


#TODO# TwoPunctures::grid_setup_method = "evaluation"

TwoPunctures::TP_epsilon = 1.0e-6
TwoPunctures::TP_Tiny    = 1.0e-2

TwoPunctures::verbose = yes



ActiveThorns = "ML_BSSN ML_BSSN_Helper NewRad"

ADMBase::evolution_method         = "ML_BSSN"
ADMBase::lapse_evolution_method   = "ML_BSSN"
ADMBase::shift_evolution_method   = "ML_BSSN"
ADMBase::dtlapse_evolution_method = "ML_BSSN"
ADMBase::dtshift_evolution_method = "ML_BSSN"

ML_BSSN::harmonicN           = 1      # 1+log
ML_BSSN::harmonicF           = 2.0    # 1+log
ML_BSSN::ShiftGammaCoeff     = 0.75
ML_BSSN::BetaDriver          = 1.0
ML_BSSN::advectLapse         = 1.0
ML_BSSN::advectShift         = 1.0

ML_BSSN::MinimumLapse        = 1.0e-8

ML_BSSN::initial_boundary_condition = "extrapolate-gammas"
ML_BSSN::rhs_boundary_condition     = "NewRad"
Boundary::radpower                     = 2

ML_BSSN::ML_log_confac_bound = "none"
ML_BSSN::ML_metric_bound     = "none"
ML_BSSN::ML_Gamma_bound      = "none"
ML_BSSN::ML_trace_curv_bound = "none"
ML_BSSN::ML_curv_bound       = "none"
ML_BSSN::ML_lapse_bound      = "none"
ML_BSSN::ML_dtlapse_bound    = "none"
ML_BSSN::ML_shift_bound      = "none"
ML_BSSN::ML_dtshift_bound    = "none"



ActiveThorns = "Dissipation"

Dissipation::order = 5
Dissipation::vars  = "
        ML_BSSN::ML_log_confac
        ML_BSSN::ML_metric
        ML_BSSN::ML_trace_curv
        ML_BSSN::ML_curv
        ML_BSSN::ML_Gamma
        ML_BSSN::ML_lapse
        ML_BSSN::ML_shift
        ML_BSSN::ML_dtlapse
        ML_BSSN::ML_dtshift
"



ActiveThorns = "ML_ADMConstraints"



ActiveThorns = "AHFinderDirect"

AHFinderDirect::find_every = 16

AHFinderDirect::run_at_CCTK_ANALYSIS = yes
AHFinderDirect::run_at_CCTK_POSTSTEP = false

AHFinderDirect::move_origins            = yes

AHFinderDirect::geometry_interpolator_name = "Lagrange polynomial interpolation"
AHFinderDirect::geometry_interpolator_pars = "order=4"
AHFinderDirect::surface_interpolator_name  = "Lagrange polynomial interpolation"
AHFinderDirect::surface_interpolator_pars  = "order=4"

AHFinderDirect::output_h_every = 0

AHFinderDirect::N_horizons = 3

AHFinderDirect::origin_x                             [1] = $xp
AHFinderDirect::initial_guess__coord_sphere__x_center[1] = $xp
AHFinderDirect::initial_guess__coord_sphere__radius  [1] = $rp
AHFinderDirect::which_surface_to_store_info          [1] = 2
AHFinderDirect::set_mask_for_individual_horizon      [1] = no
AHFinderDirect::reset_horizon_after_not_finding      [1] = no
AHFinderDirect::track_origin_from_grid_scalar        [1] = yes
AHFinderDirect::track_origin_source_x                [1] = "PunctureTracker::pt_loc_x[0]"
AHFinderDirect::track_origin_source_y                [1] = "PunctureTracker::pt_loc_y[0]"
AHFinderDirect::track_origin_source_z                [1] = "PunctureTracker::pt_loc_z[0]"
AHFinderDirect::max_allowable_horizon_radius         [1] = 3

AHFinderDirect::origin_x                             [2] = $xm
AHFinderDirect::initial_guess__coord_sphere__x_center[2] = $xm
AHFinderDirect::initial_guess__coord_sphere__radius  [2] = $rm
AHFinderDirect::which_surface_to_store_info          [2] = 3
AHFinderDirect::set_mask_for_individual_horizon      [2] = no
AHFinderDirect::reset_horizon_after_not_finding      [2] = no
AHFinderDirect::track_origin_from_grid_scalar        [2] = yes
AHFinderDirect::track_origin_source_x                [2] = "PunctureTracker::pt_loc_x[1]"
AHFinderDirect::track_origin_source_y                [2] = "PunctureTracker::pt_loc_y[1]"
AHFinderDirect::track_origin_source_z                [2] = "PunctureTracker::pt_loc_z[1]"
AHFinderDirect::max_allowable_horizon_radius         [2] = 3

AHFinderDirect::origin_x                             [3] = 0
AHFinderDirect::find_after_individual_time           [3] = 100.0
AHFinderDirect::initial_guess__coord_sphere__x_center[3] = 0
AHFinderDirect::initial_guess__coord_sphere__radius  [3] = 1.0
AHFinderDirect::which_surface_to_store_info          [3] = 4
AHFinderDirect::reset_horizon_after_not_finding      [3] = no
AHFinderDirect::max_allowable_horizon_radius         [3] = 6


ActiveThorns = "WeylScal4 Multipole"

Multipole::nradii       = 4
Multipole::radius[0]    = 30
Multipole::radius[1]    = 40
Multipole::radius[2]    = 50
Multipole::radius[3]    = 60
Multipole::ntheta       = 60
Multipole::nphi         = 120
Multipole::variables    = "WeylScal4::Psi4r{sw=-2 cmplx='WeylScal4::Psi4i' name='psi4'}"
Multipole::out_every    = 4
Multipole::l_max       = 8


ActiveThorns = "CarpetIOBasic"

IOBasic::outInfo_every      = 1
IOBasic::outInfo_reductions = "norm2"
IOBasic::outInfo_vars       = "
        Carpet::physical_time_per_hour
        ML_BSSN::ML_trace_curv
"



ActiveThorns = "CarpetIOScalar"

IOScalar::one_file_per_group = yes

IOScalar::outScalar_every = 16
IOScalar::outScalar_vars  = "
        CarpetReduce::weight
        ADMBase::metric
        ADMBase::curv
        ADMBase::lapse
        ADMBase::shift
        ADMBase::dtlapse
        ADMBase::dtshift
        ML_ADMConstraints::ML_Ham
        ML_ADMConstraints::ML_mom
        SphericalSurface::sf_radius
"



ActiveThorns = "CarpetIOASCII"

IOASCII::one_file_per_group = yes

IOASCII::output_symmetry_points = no
IOASCII::out3D_ghosts           = no

IOASCII::out0D_every = 16
IOASCII::out0D_vars  = "
        Carpet::timing
        CarpetReduce::weight
        ADMBase::metric
        ADMBase::curv
        ADMBase::lapse
        ADMBase::shift
        ADMBase::dtlapse
        ADMBase::dtshift
        ML_ADMConstraints::ML_Ham
        ML_ADMConstraints::ML_mom
        SphericalSurface::sf_active
        SphericalSurface::sf_valid
        SphericalSurface::sf_info
        SphericalSurface::sf_radius
        SphericalSurface::sf_origin
        SphericalSurface::sf_coordinate_descriptors
        PunctureTracker::pt_loc
        QuasiLocalMeasures::qlm_scalars
"

IOASCII::out1D_every = 16
IOASCII::out1D_vars  = "
        CarpetReduce::weight
        ADMBase::metric
        ADMBase::curv
        ADMBase::lapse
        ADMBase::shift
        ADMBase::dtlapse
        ADMBase::dtshift
        ML_ADMConstraints::ML_Ham
        ML_ADMConstraints::ML_mom
        SphericalSurface::sf_radius
"

IOASCII::out2D_every = 16
IOASCII::out2D_vars  = "
        SphericalSurface::sf_radius
"



#Activethorns = "CarpetIOHDF5"

#IOHDF5::out_every              = 64
#IOHDF5::one_file_per_group     = yes
#IOHDF5::output_symmetry_points = no
#IOHDF5::out3D_ghosts           = no
#IOHDF5::compression_level      = 1
#IOHDF5::use_checksums          = yes
#IOHDF5::out_vars               = "
#        CarpetReduce::weight
#        ADMBase::metric
#        ADMBase::curv
#        ADMBase::lapse
#        ADMBase::shift
#        ML_ADMConstraints::ML_Ham
#        ML_ADMConstraints::ML_mom
#        WeylScal4::Psi4r
#        WeylScal4::Psi4i
#"

#IOHDF5::checkpoint                  = yes
IO::checkpoint_dir                  = "../checkpoints"
IO::checkpoint_ID                   = yes
IO::checkpoint_every_walltime_hours = 6.0
IO::checkpoint_on_terminate         = yes

IO::recover     = "autoprobe"
IO::recover_dir = "../checkpoints"



ActiveThorns = "Formaline"



ActiveThorns = "TimerReport"

TimerReport::out_every                  = 640
TimerReport::out_filename               = "TimerReport"
TimerReport::output_all_timers_together = yes
TimerReport::output_all_timers_readable = yes
TimerReport::n_top_timers               = 20
"""

open(re.sub(r'(.*)\.rpar$', r'\1.par', sys.argv[0]), 'w').write(re.sub(r'\n *',r'\n',Template(Template(lines).substitute(locals())).substitute(locals())))