"""Tools for running the bilby review
Select from the options below to create an ini file which can be submitted using
bilby_pipe. Alternatively, use the :code:`--submit` option to also submit the job.
.. note::
These were the primary review tests performed until LIGO-Virgo-KAGRA's third
observing run.
More recent review tests used `this data <https://git.ligo.org/colm.talbot/pe_review>`_.
"""
import argparse
import os
import sys
import time
import bilby
import bilby_pipe
from . import parser
from .utils import (
DURATION_LOOKUPS,
MAXIMUM_FREQUENCY_LOOKUPS,
check_directory_exists_and_if_not_mkdir,
logger,
run_command_line,
)
[docs]
fiducial_bbh_injections = {
"128s": dict(
chirp_mass=2.1,
mass_ratio=0.9,
a_1=0.04,
a_2=0.01,
tilt_1=1.0264673717225983,
tilt_2=2.1701305583885513,
phi_12=5.0962562029664955,
phi_jl=2.518241237045709,
luminosity_distance=100.0,
dec=0.2205292600865073,
ra=3.952677097361719,
theta_jn=0.25,
psi=2.6973435044499543,
phase=3.686990398567503,
geocent_time=-0.01,
),
"4s": dict(
chirp_mass=17.051544979894693,
mass_ratio=0.3183945489993522,
a_1=0.29526500202350264,
a_2=0.23262056301313416,
tilt_1=1.0264673717225983,
tilt_2=2.1701305583885513,
phi_12=5.0962562029664955,
phi_jl=2.518241237045709,
luminosity_distance=497.2983560174788,
dec=0.2205292600865073,
ra=3.952677097361719,
theta_jn=1.8795187965094322,
psi=2.6973435044499543,
phase=3.686990398567503,
geocent_time=0.040833669551002205,
),
"high_mass": dict(
chirp_mass=45.051544979894693,
mass_ratio=0.9183945489993522,
a_1=0.29526500202350264,
a_2=0.23262056301313416,
tilt_1=1.0264673717225983,
tilt_2=2.1701305583885513,
phi_12=5.0962562029664955,
phi_jl=2.518241237045709,
luminosity_distance=497.2983560174788,
dec=0.2205292600865073,
ra=3.952677097361719,
theta_jn=1.8795187965094322,
psi=2.6973435044499543,
phase=3.686990398567503,
geocent_time=0.040833669551002205,
),
}
[docs]
fiducial_bns_injections = {
"128s_tidal_lowspin": dict(
chirp_mass=1.486,
mass_ratio=0.9,
a_1=0.04,
a_2=0.01,
tilt_1=1.0264673717225983,
tilt_2=2.1701305583885513,
phi_12=5.0962562029664955,
phi_jl=2.518241237045709,
luminosity_distance=100.0,
dec=0.2205292600865073,
ra=3.952677097361719,
theta_jn=0.25,
psi=2.6973435044499543,
phase=3.686990398567503,
geocent_time=-0.01,
lambda_1=1500,
lambda_2=750,
)
}
[docs]
SAMPLER_KEYS = ["nlive", "walks", "nact", "maxmcmc"]
[docs]
def get_default_top_level_dir():
def _parse_version(module):
version_tuple = module._version.version_tuple
if len(version_tuple) == 3:
version = "{}.{}.{}".format(*version_tuple)
git_hash = "release"
else:
version = "{}.{}.{}".format(*version_tuple[:3])
git_hash = version_tuple[4].split(".")[0]
return f"{module.__name__}-{version}-{git_hash}"
return f"{_parse_version(bilby)}_{_parse_version(bilby_pipe)}"
[docs]
def get_top_level_dir(args):
if args.directory:
dirname = args.directory
else:
dirname = get_default_top_level_dir()
check_directory_exists_and_if_not_mkdir(dirname)
return dirname
[docs]
def get_base_label(args, review_name):
base_label = "{}_{}_{}_{}".format(
review_name, args.prior, args.sampler, "-".join(args.marginalization)
)
if args.roq:
base_label += "_ROQ"
if args.zero_noise:
base_label += "_zero-noise"
for attr in SAMPLER_KEYS:
if getattr(args, attr, None) is not None:
base_label += f"_{attr}{getattr(args, attr)}"
return base_label
[docs]
def get_date_string():
return time.strftime("%y%m%d %H:%M")
[docs]
def write_ini_file(parser, filename, config_dict):
parser.write_to_file(
filename=filename,
args=config_dict,
overwrite=True,
include_description=False,
exclude_default=False,
comment=(
"Ini file written {}, command line args: {}".format(
get_date_string(), " ".join(sys.argv[:])
)
),
)
[docs]
def get_sampler_kwargs(args):
sampler_kwargs = {
attr: getattr(args, attr)
for attr in SAMPLER_KEYS
if getattr(args, attr, None) is not None
}
return sampler_kwargs
[docs]
def get_default_setup(args, review_name):
if args.duration is None:
args.duration = DURATION_LOOKUPS[args.prior]
top_level_dir = get_top_level_dir(args)
base_label = get_base_label(args, review_name)
rundir = f"outdir_{base_label}"
filename = f"{top_level_dir}/review_{base_label}.ini"
base_dict = dict(
label=base_label,
accounting="ligo.dev.o4.cbc.pe.bilby",
detectors=str(args.detectors),
outdir=rundir,
deltaT=0.2,
reference_frequency=args.reference_frequency,
prior_file=args.prior,
duration=args.duration,
sampler=args.sampler,
sampler_kwargs=get_sampler_kwargs(args),
n_parallel=args.n_parallel,
sampling_frequency=4 * MAXIMUM_FREQUENCY_LOOKUPS[args.prior],
maximum_frequency=MAXIMUM_FREQUENCY_LOOKUPS[args.prior],
time_marginalization="time" in args.marginalization,
distance_marginalization="distance" in args.marginalization,
phase_marginalization="phase" in args.marginalization,
generation_seed=args.generation_seed,
)
if args.roq:
base_dict["likelihood-type"] = "ROQGravitationalWaveTransient"
if args.roq_folder is None:
base_dict["roq-folder"] = f"/home/cbc/ROQ_data/IMRPhenomPv2/{args.prior}"
else:
base_dict["roq-folder"] = args.roq_folder
if args.zero_noise:
base_dict["zero-noise"] = True
return base_dict, rundir, filename
[docs]
def fiducial_bbh(args):
"""Review test: fiducial binary black hole in Gaussian noise
Parameters
----------
args: Namespace
The command line arguments namespace object
Returns
-------
filename: str
A filename of the ini file generated
"""
config_dict, rundir, filename = get_default_setup(args, "fiducial_bbh")
config_dict["plot_trace"] = True
config_dict["plot_data"] = True
config_dict["create_summary"] = True
config_dict["gaussian-noise"] = True
config_dict["injection"] = True
config_dict["n-injection"] = 1
config_dict["injection-dict"] = str(fiducial_bbh_injections[args.prior])
ini_parser = parser.create_parser()
write_ini_file(ini_parser, filename, config_dict)
return filename
[docs]
def fiducial_bns(args):
"""Review test: fiducial binary neutron star in Gaussian noise
Parameters
----------
args: Namespace
The command line arguments namespace object
Returns
-------
filename: str
A filename of the ini file generated
"""
config_dict, rundir, filename = get_default_setup(args, "fiducial_bns")
config_dict["plot_trace"] = True
config_dict["plot_data"] = True
config_dict["create_summary"] = True
config_dict["gaussian-noise"] = True
config_dict["injection"] = True
config_dict["n-injection"] = 1
config_dict["injection-dict"] = str(fiducial_bns_injections[args.prior])
config_dict["frequency_domain_source_model"] = "lal_binary_neutron_star"
config_dict["waveform-approximant"] = "IMRPhenomPv2_NRTidal"
ini_parser = parser.create_parser()
write_ini_file(ini_parser, filename, config_dict)
return filename
[docs]
def pp_test(args):
"""Review test: pp-test
Parameters
----------
args: Namespace
The command line arguments namespace object
Returns
-------
filename: str
A filename of the ini file generated
"""
config_dict, rundir, filename = get_default_setup(args, "pp_test")
config_dict["injection"] = True
config_dict["gaussian-noise"] = True
config_dict["n-simulation"] = 100
config_dict["sampler_kwargs"]["check_point_plot"] = False
config_dict["postprocessing-executable"] = "bilby_pipe_pp_test"
config_dict["postprocessing-arguments"] = f"{rundir}/result --outdir {rundir}"
ini_parser = parser.create_parser()
write_ini_file(ini_parser, filename, config_dict)
return filename
[docs]
def create_parser():
parser = argparse.ArgumentParser(
prog="bilby_pipe_review",
description=__doc__,
formatter_class=argparse.RawTextHelpFormatter,
)
parser.add_argument(
"--submit", action="store_true", help="Build and submit the job"
)
parser.add_argument("--build", action="store_true", help="Build the job")
parser.add_argument(
"--directory",
type=str,
default=None,
help="Set the top-level directory to use, defaults to a standardise naming scheme",
)
main_job_parser = parser.add_mutually_exclusive_group(required=True)
main_job_parser.add_argument("--bbh", action="store_true", help="Fiducial BBH test")
main_job_parser.add_argument("--bns", action="store_true", help="Fiducial BNS test")
main_job_parser.add_argument("--pp-test", action="store_true", help="PP test test")
main_job_parser.add_argument(
"--create-dir", action="store_true", help="Create test directory and exit"
)
parser.add_argument("--roq", action="store_true", help="Use ROQ likelihood")
parser.add_argument(
"--roq-folder", type=str, help="The ROQ folder to use, defaults to IMRPhenomPv2"
)
parser.add_argument(
"--zero-noise", action="store_true", help="Run BBH and BNS test with zero noise"
)
parser.add_argument(
"--prior",
type=str,
help="The default prior to use",
choices=sorted(bilby_pipe.input.Input.get_default_prior_files()),
default=None,
)
parser.add_argument(
"--duration",
type=int,
default=None,
help="Signal duration",
choices=[4, 8, 16, 32, 64, 128],
)
parser.add_argument(
"--sampler",
type=str,
default="dynesty",
help="Sampler to use, default is dynesty",
)
parser.add_argument(
"--marginalization",
nargs="+",
default=["time", "distance", "phase"],
help=(
"A space-separated list of {time, distance, phase} marginalization"
"choices, default `--marginalization time distance phase`"
),
)
parser.add_argument(
"--detectors",
nargs="+",
default=["H1", "L1"],
help=("The detectors to use for simulating data, default [H1, L1]"),
)
parser.add_argument(
"--nlive",
type=int,
default=None,
help=("The number of live points to use, default None (use bilby defaults)"),
)
parser.add_argument(
"--nact",
type=int,
default=None,
help=("The nact (for dynesty only) to use, default None (use bilby defaults)"),
)
parser.add_argument(
"--maxmcmc",
type=int,
default=None,
help=("The maxmcmc to use, default None (use bilby defaults)"),
)
parser.add_argument(
"--walks",
type=int,
default=None,
help=("The walks (minimum mcmc) to use, default None (use bilby defaults)"),
)
parser.add_argument(
"--n-parallel",
type=int,
default=4,
help=("The number of parallel-processes to use, default 4"),
)
parser.add_argument(
"--reference-frequency",
type=int,
default=20,
help=("The reference frequency to run tests at, default 20"),
)
parser.add_argument(
"--generation-seed",
type=int,
default=1010,
help=("The seed used for generation: reproducible injections, default 1010"),
)
return parser
[docs]
def get_args():
return create_parser().parse_args()
[docs]
def main():
args = get_args()
# Standardise inputs
args.marginalization = [xx.lower() for xx in args.marginalization]
args.marginalization = sorted(args.marginalization)
args.sampler = args.sampler.lower()
if args.prior is None and args.create_dir is False:
raise Exception("You need to specify a default prior for the test")
if args.bbh:
logger.info("Review test: fiducial BBH")
filename = fiducial_bbh(args)
elif args.bns:
logger.info("Review test: fiducial BNS")
filename = fiducial_bns(args)
elif args.pp_test:
logger.info("Review test: PP-test")
filename = pp_test(args)
elif args.create_dir:
get_top_level_dir(args)
sys.exit()
else:
raise Exception("No review test requested, see --help")
if args.submit or args.build:
dirname = os.path.dirname(filename)
logger.info(f"Building and submitting the ini file {filename}")
arguments = ["bilby_pipe", filename]
if args.submit:
arguments.append("--submit")
run_command_line(arguments, directory=dirname)
else:
logger.info(f"Built ini file {filename}")