From ab4def20d48fc9e07ff518e096b2ea3243a69bcc Mon Sep 17 00:00:00 2001 From: Gregory Ashton <gregory.ashton@ligo.org> Date: Thu, 6 Feb 2020 09:08:31 +1100 Subject: [PATCH 1/8] Improvements to the review test and associated machinery 1) Add a injection-dict option to specify the injection from the ini file 2) Add directory structure to the review tests, naming by version and git hasg 3) Add additional options to the review test: sampler kwargs, reference_frequency, n-parallel, detectors, and marginalizations. --- .gitlab-ci.yml | 20 +++ bilby_pipe/__init__.py | 1 + bilby_pipe/bilbyargparser.py | 2 +- bilby_pipe/data_generation.py | 5 + bilby_pipe/input.py | 20 +++ bilby_pipe/main.py | 17 ++- bilby_pipe/overview.py | 2 +- bilby_pipe/parser.py | 9 +- bilby_pipe/review.py | 263 ++++++++++++++++++++++++---------- bilby_pipe/utils.py | 4 +- tests/slurm_output_test.py | 1 + 11 files changed, 264 insertions(+), 80 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 748e3e22..7a2d0c36 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -105,6 +105,26 @@ injection-regression-test: - bilby_pipe test_gaussian_noise_simulation_and_injection.ini --local --bilby-test-mode - bilby_pipe test_gaussian_noise_simulation.ini --local --bilby-test-mode +# Test that the review tests build properly +review-test-build: + stage: test + image: bilbydev/bilby_pipe-test-suite-python37 + script: + - source activate python37 + - mkdir -p .pip37 + - pip --cache-dir=.pip37 install --upgrade bilby + - pip --cache-dir=.pip37 install . + - mkdir TEST_REVIEW_FILES + - cd TEST_REVIEW_FILES + + - bilby_pipe_review --prior 4s --bbh --marginalization distance phase --nact 5 --directory TEST + - if [[ ! -f TEST/review_fiducial_bbh_4s_dynesty_distance-phase_nact5.ini ]] ; then exit 1; else echo "Webpage exists"; fi + + - bilby_pipe_review --prior 4s --pp-test --marginalization distance phase --sampler cPnEst --nact 5 --directory TEST + - if [[ ! -f TEST/review_pp_test_4s_cpnest_distance-phase_nact5.ini ]] ; then exit 1; else echo "Webpage exists"; fi + - cd .. + - rm TEST_REVIEW_FILES -r + example-ini-file-pesummary: stage: test image: bilbydev/bilby_pipe-test-suite-python37 diff --git a/bilby_pipe/__init__.py b/bilby_pipe/__init__.py index 5d1c5dd5..38a48241 100644 --- a/bilby_pipe/__init__.py +++ b/bilby_pipe/__init__.py @@ -11,3 +11,4 @@ estimation on computing clusters. from . import bilbyargparser, main, parser, utils __version__ = utils.get_version_information().split(":", 1)[0] +__long_version__ = utils.get_version_information() diff --git a/bilby_pipe/bilbyargparser.py b/bilby_pipe/bilbyargparser.py index e02dc5f1..ddede4ac 100644 --- a/bilby_pipe/bilbyargparser.py +++ b/bilby_pipe/bilbyargparser.py @@ -158,7 +158,7 @@ class BilbyArgParser(configargparse.ArgParser): file=ff, ) if isinstance(comment, str): - print(comment + "\n", file=ff) + print("#" + comment + "\n", file=ff) for group in self._action_groups[2:]: print("#" * 80, file=ff) print("## {}".format(group.title), file=ff) diff --git a/bilby_pipe/data_generation.py b/bilby_pipe/data_generation.py index 4866b9aa..b691bca7 100644 --- a/bilby_pipe/data_generation.py +++ b/bilby_pipe/data_generation.py @@ -189,11 +189,16 @@ class DataGenerationInput(Input): self.injection = args.injection self.injection_numbers = args.injection_numbers self.injection_file = args.injection_file + self.injection_dict = args.injection_dict self.gaussian_noise = args.gaussian_noise # The following are all mutually exclusive methods to set the data if self.gaussian_noise: if args.injection_file is not None: + logger.debug("Using provided injection file") + self._set_interferometers_from_injection_in_gaussian_noise() + elif args.injection_dict is not None: + logger.debug("Using provided injection dict") self._set_interferometers_from_injection_in_gaussian_noise() elif args.injection is False: self._set_interferometers_from_gaussian_noise() diff --git a/bilby_pipe/input.py b/bilby_pipe/input.py index 2129c7ad..360b7856 100644 --- a/bilby_pipe/input.py +++ b/bilby_pipe/input.py @@ -468,6 +468,26 @@ class Input(object): "Injection file {} not found".format(injection_file) ) + @property + def injection_dict(self): + return self._injection_dict + + @injection_dict.setter + def injection_dict(self, injection_dict): + if injection_dict is None: + self._injection_dict = None + return + elif isinstance(injection_dict, str): + self._injection_dict = convert_string_to_dict(injection_dict) + elif isinstance(injection_dict, dict): + pass + else: + raise ValueError("injection-dict can not be coerced to a dict") + + self.injection_df = pd.DataFrame(self._injection_dict, index=[0]) + self.total_number_of_injections = 1 + self.injection = True + @staticmethod def read_injection_file(injection_file): if "json" in injection_file: diff --git a/bilby_pipe/main.py b/bilby_pipe/main.py index 93d34bdd..f7051491 100644 --- a/bilby_pipe/main.py +++ b/bilby_pipe/main.py @@ -4,6 +4,7 @@ bilby_pipe is a command line tools for taking user input (as command line arguments or an ini file) and creating DAG files for submitting bilby parameter estimation jobs. """ +import json import os import shutil import subprocess @@ -93,6 +94,7 @@ class MainInput(Input): self.injection = args.injection self.injection_numbers = args.injection_numbers self.injection_file = args.injection_file + self.injection_dict = args.injection_dict self.injection_waveform_approximant = args.injection_waveform_approximant self.generation_seed = args.generation_seed if self.injection: @@ -202,11 +204,22 @@ class MainInput(Input): logger.warning(" ".join(msg)) def check_injection(self): - """ If injections are requested, create an injection file """ + """ Check injection behaviour + + If injections are requested, either use the injection-dict, + injection-file, or create an injection-file + + """ default_injection_file_name = "{}/{}_injection_file.dat".format( self.data_directory, self.label ) - if self.injection_file is not None: + if self.injection_dict is not None: + logger.info( + "Using injection dict from ini file {}".format( + json.dumps(self.injection_dict, indent=2) + ) + ) + elif self.injection_file is not None: logger.info("Using injection file {}".format(self.injection_file)) elif os.path.isfile(default_injection_file_name): # This is done to avoid overwriting the injection file diff --git a/bilby_pipe/overview.py b/bilby_pipe/overview.py index c5695fa1..98f16960 100644 --- a/bilby_pipe/overview.py +++ b/bilby_pipe/overview.py @@ -35,7 +35,7 @@ def create_overview( if inputs.injection_waveform_approximant is None: inputs.injection_waveform_approximant = inputs.waveform_approximant - if inputs.injection: + if inputs.injection_file: injection_file = abspath(inputs.injection_file) else: injection_file = None diff --git a/bilby_pipe/parser.py b/bilby_pipe/parser.py index 69c0e26f..c6ec610e 100644 --- a/bilby_pipe/parser.py +++ b/bilby_pipe/parser.py @@ -340,7 +340,14 @@ def create_parser(top_level=True): default=False, help="Create data from an injection file", ) - injection_parser.add( + injection_parser_input = injection_parser.add_mutually_exclusive_group() + injection_parser_input.add( + "--injection-dict", + type=nonestr, + default=None, + help="A single injection dictionary given in the ini file", + ) + injection_parser_input.add( "--injection-file", type=nonestr, default=None, diff --git a/bilby_pipe/review.py b/bilby_pipe/review.py index ff48cc85..71241ace 100644 --- a/bilby_pipe/review.py +++ b/bilby_pipe/review.py @@ -1,6 +1,13 @@ -""" Tools for running review accessed through bilby_pipe_review """ +""" 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 --submit option to also submit the job + +""" import argparse import json +import os +import sys import time import bilby @@ -11,6 +18,7 @@ from .utils import ( DURATION_LOOKUPS, MAXIMUM_FREQUENCY_LOOKUPS, check_directory_exists_and_if_not_mkdir, + logger, run_command_line, ) @@ -92,41 +100,109 @@ fiducial_bns_injections = { } -def get_date_string(): - return time.strftime("%y%m%d_%H%M") +SAMPLER_KEYS = ["nlive", "nact", "maxmcmc"] -def get_default_setup(args, review_name): - if args.duration is None: - args.duration = DURATION_LOOKUPS[args.prior] +def get_default_top_level_dir(): + bilby_version_number = bilby.__version__.split(":")[0] + bilby_git_hash = bilby.__version__.split(" ")[2] + bilby_state = ["CLEAN", "UNCLEAN"]["UNCLEAN" in bilby.__version__] + + bilby_pipe_version_number = bilby_pipe.__long_version__.split(":")[0] + bilby_pipe_git_hash = bilby_pipe.__long_version__.split(" ")[2] + bilby_pipe_state = ["CLEAN", "UNCLEAN"]["UNCLEAN" in bilby_pipe.__long_version__] + + return "bilby{}-{}-{}_bilby_pipe{}-{}-{}".format( + bilby_version_number, + bilby_git_hash, + bilby_state, + bilby_pipe_version_number, + bilby_pipe_git_hash, + bilby_pipe_state, + ) + + +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 + - base_label = "{}_{}".format(review_name, args.prior) +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" - label_with_date = "{}_{}".format(base_label, get_date_string()) + for attr in SAMPLER_KEYS: + if getattr(args, attr, None) is not None: + base_label += "_{}{}".format(attr, getattr(args, attr)) + + return base_label + + +def get_date_string(): + return time.strftime("%y%m%d %H:%M") + + +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[:]) + ) + ), + ) + + +def get_sampler_kwargs(args): + sampler_kwargs = { + attr: getattr(args, attr) + for attr in SAMPLER_KEYS + if getattr(args, attr, None) is not None + } + sampler_kwargs["n_check_point"] = 10000 + return sampler_kwargs + + +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 = "outdir_{}".format(base_label) - check_directory_exists_and_if_not_mkdir(rundir) - filename = "review_{}.ini".format(label_with_date) + filename = "{}/review_{}.ini".format(top_level_dir, base_label) base_dict = dict( - label=label_with_date, + label=base_label, accounting="ligo.dev.o3.cbc.pe.lalinference", - detectors="[H1, L1]", + 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="{n_check_point: 10000}", + sampler_kwargs=get_sampler_kwargs(args), create_plots=None, + n_parallel=args.n_parallel, sampling_frequency=4 * MAXIMUM_FREQUENCY_LOOKUPS[args.prior], maximum_frequency=MAXIMUM_FREQUENCY_LOOKUPS[args.prior], - time_marginalization=True, - distance_marginalization=True, - phase_marginalization=True, + time_marginalization="time" in args.marginalization, + distance_marginalization="distance" in args.marginalization, + phase_marginalization="phase" in args.marginalization, ) if args.roq: @@ -164,28 +240,10 @@ def fiducial_bbh(args): config_dict["injection"] = True config_dict["n-injection"] = 1 config_dict["generation-seed"] = 1010 - config_dict["n-parallel"] = 4 - if args.prior == "128s": - config_dict["sampler_kwargs"] = "{walks=800, n_check_point=10000}" + config_dict["injection-dict"] = str(fiducial_bbh_injections[args.prior]) - injection_filename = "{}/injection_file.json".format(rundir) - with open(injection_filename, "w") as file: - json.dump( - dict(injections=fiducial_bbh_injections[args.prior]), - file, - indent=2, - cls=bilby.core.result.BilbyJsonEncoder, - ) - config_dict["injection-file"] = injection_filename - - _parser = parser.create_parser() - _parser.write_to_file( - filename=filename, - args=config_dict, - overwrite=True, - include_description=False, - exclude_default=True, - ) + ini_parser = parser.create_parser() + write_ini_file(ini_parser, filename, config_dict) return filename @@ -209,13 +267,10 @@ def fiducial_bns(args): config_dict["injection"] = True config_dict["n-injection"] = 1 config_dict["generation-seed"] = 1010 - config_dict["n-parallel"] = 4 config_dict["frequency_domain_source_model"] = "lal_binary_neutron_star" config_dict["waveform-approximant"] = "IMRPhenomPv2_NRTidal" - config_dict["sampler_kwargs"] = "{walks=500, n_check_point=10000}" - injection_filename = "{}/injection_file.json".format(rundir) with open(injection_filename, "w") as file: json.dump( @@ -226,14 +281,8 @@ def fiducial_bns(args): ) config_dict["injection-file"] = injection_filename - _parser = parser.create_parser() - _parser.write_to_file( - filename=filename, - args=config_dict, - overwrite=True, - include_description=False, - exclude_default=True, - ) + ini_parser = parser.create_parser() + write_ini_file(ini_parser, filename, config_dict) return filename @@ -257,38 +306,39 @@ def pp_test(args): config_dict["injection"] = True config_dict["gaussian-noise"] = True config_dict["n-simulation"] = 100 - config_dict["reference_frequency"] = 100 - if args.prior in ["128s", "128s_tidal"]: - config_dict[ - "sampler_kwargs" - ] = "{check_point_plot=False, walks=800, n_check_point=10000}" - else: - config_dict["sampler_kwargs"] = "{check_point_plot=False, n_check_point=10000}" + config_dict["sampler_kwargs"]["check_point_plot"] = False config_dict["postprocessing-executable"] = "bilby_pipe_pp_test" config_dict["postprocessing-arguments"] = "{}/result --outdir {}".format( rundir, rundir ) - _parser = parser.create_parser() - _parser.write_to_file( - filename=filename, - args=config_dict, - overwrite=True, - include_description=False, - exclude_default=True, - ) + ini_parser = parser.create_parser() + write_ini_file(ini_parser, filename, config_dict) return filename -def main(): - parser = argparse.ArgumentParser(prog="bilby_pipe review script", usage="") - parser.add_argument("--submit", action="store_true", help="Submit the job") - parser.add_argument("--bbh", action="store_true", help="Fiducial BBH test") - parser.add_argument("--bns", action="store_true", help="Fiducial BNS test") +def get_args(): + parser = argparse.ArgumentParser(prog="bilby_pipe review script", usage=__doc__) + + 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") + 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("--pp-test", action="store_true", help="PP test test") parser.add_argument( "--zero-noise", action="store_true", help="Run BBH and BNS test with zero noise" ) @@ -311,18 +361,83 @@ def main(): default="dynesty", help="Sampler to use, default is dynesty", ) - args = parser.parse_args() + 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( + "--n-parallel", + type=int, + default=4, + help=("The number of parallel-processes to use, default 4"), + ) + parser.add_argument( + "--reference-frequency", + type=int, + default=100, + help=("The reference frequency to run tests at, default 100"), + ) + + return parser.parse_args() + + +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.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) else: raise Exception("No review test requested, see --help") - arguments = ["bilby_pipe", filename] - if args.submit: - arguments.append("--submit") - run_command_line(arguments) + if args.submit or args.build: + dirname = os.path.dirname(filename) + logger.info("Building and submitting the ini file {}".format(filename)) + arguments = ["bilby_pipe", filename] + if args.submit: + arguments.append("--submit") + run_command_line(arguments, directory=dirname) + else: + logger.info("Built ini file {}".format(filename)) diff --git a/bilby_pipe/utils.py b/bilby_pipe/utils.py index 94f02174..8dfd8435 100644 --- a/bilby_pipe/utils.py +++ b/bilby_pipe/utils.py @@ -212,7 +212,9 @@ def get_command_line_arguments(): return sys.argv[1:] -def run_command_line(arguments): +def run_command_line(arguments, directory=None): + if directory: + os.chdir(directory) print("\nRunning command $ {}\n".format(" ".join(arguments))) subprocess.call(arguments) diff --git a/tests/slurm_output_test.py b/tests/slurm_output_test.py index 767c40fc..c8ffd997 100644 --- a/tests/slurm_output_test.py +++ b/tests/slurm_output_test.py @@ -22,6 +22,7 @@ class TestSlurm(unittest.TestCase): n_parallel=1, injection=False, injection_file=None, + injection_dict=None, injection_waveform_approximant=None, n_injection=None, injection_numbers=None, -- GitLab From 5bf1d5deeafcbe26d40a38f8a35bde81f8cce25d Mon Sep 17 00:00:00 2001 From: Gregory Ashton <gregory.ashton@ligo.org> Date: Thu, 6 Feb 2020 21:49:44 +1100 Subject: [PATCH 2/8] Add chdir --- bilby_pipe/utils.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bilby_pipe/utils.py b/bilby_pipe/utils.py index 8dfd8435..89d58f66 100644 --- a/bilby_pipe/utils.py +++ b/bilby_pipe/utils.py @@ -214,9 +214,14 @@ def get_command_line_arguments(): def run_command_line(arguments, directory=None): if directory: + pwd = os.path.abspath(".") os.chdir(directory) + else: + pwd = None print("\nRunning command $ {}\n".format(" ".join(arguments))) subprocess.call(arguments) + if pwd: + os.chdir(pwd) def parse_args(input_args, parser, allow_unknown=True): -- GitLab From d17a0b57f31851c3bfa7637b232afdf4091d73e5 Mon Sep 17 00:00:00 2001 From: Gregory Ashton <gregory.ashton@ligo.org> Date: Thu, 6 Feb 2020 14:41:38 -0800 Subject: [PATCH 3/8] Update PP tests to work with merged runs --- bilby_pipe/pp_test.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/bilby_pipe/pp_test.py b/bilby_pipe/pp_test.py index 925efebd..9a822490 100755 --- a/bilby_pipe/pp_test.py +++ b/bilby_pipe/pp_test.py @@ -39,6 +39,12 @@ def create_parser(): parser.add_argument( "-n", type=int, help="Number of samples to truncate to", default=None ) + parser.add_argument( + "--filter", + type=str, + help="A string to match and filtering results", + default=None, + ) return parser @@ -51,7 +57,16 @@ def get_results_filenames(args): if len(results_files) == 0: raise FileNotFoundError("No results found in path {}".format(args.directory)) + if args.filter is not None: + logger.info("Filtering results to only '{}' results".format(args.filter)) + results_files = [rf for rf in results_files if args.filter in rf] + + if any("merge" in item for item in results_files): + logger.info("Filtering results to only 'merge' results") + results_files = [rf for rf in results_files if "merge" in rf] + if args.n is not None: + logger.info("Truncating to first {} results".format(args.n)) results_files = results_files[: args.n] return results_files -- GitLab From c91f0ea1f1f781e02e7191da07934654f891ef20 Mon Sep 17 00:00:00 2001 From: Gregory Ashton <gregory.ashton@ligo.org> Date: Thu, 6 Feb 2020 15:07:53 -0800 Subject: [PATCH 4/8] Fix test --- tests/pp_test.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/pp_test.py b/tests/pp_test.py index b3dc65c6..181c80d5 100644 --- a/tests/pp_test.py +++ b/tests/pp_test.py @@ -15,7 +15,12 @@ class TestPP(unittest.TestCase): def setUp(self): self.outdir = "test_outdir" self.args = SimpleNamespace( - directory=self.outdir, outdir=None, label=None, n=None, print=False + directory=self.outdir, + outdir=None, + label=None, + n=None, + print=False, + filter=None, ) os.mkdir(self.outdir) -- GitLab From 72173108765b8886f5eca13226adcef18246e029 Mon Sep 17 00:00:00 2001 From: Gregory Ashton <gregory.ashton@ligo.org> Date: Sun, 9 Feb 2020 23:01:12 -0800 Subject: [PATCH 5/8] Fix and expose generation seed --- bilby_pipe/review.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bilby_pipe/review.py b/bilby_pipe/review.py index 71241ace..1b8300fc 100644 --- a/bilby_pipe/review.py +++ b/bilby_pipe/review.py @@ -203,6 +203,7 @@ def get_default_setup(args, review_name): 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: @@ -239,7 +240,6 @@ def fiducial_bbh(args): config_dict["gaussian-noise"] = True config_dict["injection"] = True config_dict["n-injection"] = 1 - config_dict["generation-seed"] = 1010 config_dict["injection-dict"] = str(fiducial_bbh_injections[args.prior]) ini_parser = parser.create_parser() @@ -266,7 +266,6 @@ def fiducial_bns(args): config_dict["gaussian-noise"] = True config_dict["injection"] = True config_dict["n-injection"] = 1 - config_dict["generation-seed"] = 1010 config_dict["frequency_domain_source_model"] = "lal_binary_neutron_star" config_dict["waveform-approximant"] = "IMRPhenomPv2_NRTidal" @@ -406,6 +405,12 @@ def get_args(): default=100, help=("The reference frequency to run tests at, default 100"), ) + parser.add_argument( + "--generation-seed", + type=int, + default=1010, + help=("The seed used for generation: reproducible injections, default 1010"), + ) return parser.parse_args() -- GitLab From 82325e09c7c31cc5de4d16795e6d6c555c9ba602 Mon Sep 17 00:00:00 2001 From: Gregory Ashton <gregory.ashton@ligo.org> Date: Sun, 9 Feb 2020 23:11:47 -0800 Subject: [PATCH 6/8] Make prior file required --- bilby_pipe/review.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bilby_pipe/review.py b/bilby_pipe/review.py index 1b8300fc..9a7786c4 100644 --- a/bilby_pipe/review.py +++ b/bilby_pipe/review.py @@ -346,6 +346,7 @@ def get_args(): type=str, help="The default prior to use", choices=sorted(bilby_pipe.input.Input.get_default_prior_files()), + required=True, ) parser.add_argument( "--duration", -- GitLab From 1a49367105c390975aee139be0354f016ff876f5 Mon Sep 17 00:00:00 2001 From: Gregory Ashton <gregory.ashton@ligo.org> Date: Mon, 10 Feb 2020 22:00:55 -0800 Subject: [PATCH 7/8] Add walks to sampler keys --- bilby_pipe/review.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bilby_pipe/review.py b/bilby_pipe/review.py index 9a7786c4..0854567d 100644 --- a/bilby_pipe/review.py +++ b/bilby_pipe/review.py @@ -100,7 +100,7 @@ fiducial_bns_injections = { } -SAMPLER_KEYS = ["nlive", "nact", "maxmcmc"] +SAMPLER_KEYS = ["nlive", "walks", "nact", "maxmcmc"] def get_default_top_level_dir(): @@ -394,6 +394,12 @@ def get_args(): 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, -- GitLab From eda52f1f1eca43cca892187392b2c637bad98584 Mon Sep 17 00:00:00 2001 From: Gregory Ashton <gregory.ashton@ligo.org> Date: Wed, 12 Feb 2020 10:51:03 +1100 Subject: [PATCH 8/8] Fix bug and improve testing around injections --- bilby_pipe/input.py | 18 ++- bilby_pipe/utils.py | 5 + tests/input_test.py | 150 +++++++++++++++++- .../lalinference_test_injection_standard.dat | 2 + .../lalinference_test_injection_standard.json | 52 ++++++ 5 files changed, 222 insertions(+), 5 deletions(-) create mode 100644 tests/lalinference_test_injection_standard.dat create mode 100644 tests/lalinference_test_injection_standard.json diff --git a/bilby_pipe/input.py b/bilby_pipe/input.py index bf2dd644..f0be7cd4 100644 --- a/bilby_pipe/input.py +++ b/bilby_pipe/input.py @@ -15,7 +15,13 @@ import pandas as pd import bilby from . import utils -from .utils import BilbyPipeError, convert_string_to_dict, get_geocent_prior, logger +from .utils import ( + BilbyPipeError, + BilbyPipeInternalError, + convert_string_to_dict, + get_geocent_prior, + logger, +) class Input(object): @@ -407,7 +413,10 @@ class Input(object): @property def injection_numbers(self): - return self._injection_numbers + if hasattr(self, "_injection_numbers"): + return self._injection_numbers + else: + raise BilbyPipeInternalError("Injection numbers requested, but not yet set") @injection_numbers.setter def injection_numbers(self, injection_numbers): @@ -480,9 +489,9 @@ class Input(object): elif isinstance(injection_dict, str): self._injection_dict = convert_string_to_dict(injection_dict) elif isinstance(injection_dict, dict): - pass + self._injection_dict = injection_dict else: - raise ValueError("injection-dict can not be coerced to a dict") + raise BilbyPipeError("injection-dict can not be coerced to a dict") self.injection_df = pd.DataFrame(self._injection_dict, index=[0]) self.total_number_of_injections = 1 @@ -505,6 +514,7 @@ class Input(object): try: injection_df = pd.DataFrame(injection_df) except ValueError: + # If injection_df is a dictionary of single elements, set the index-array in pandas injection_df = pd.DataFrame(injection_df, index=[0]) return injection_df diff --git a/bilby_pipe/utils.py b/bilby_pipe/utils.py index 89d58f66..4674c0d5 100644 --- a/bilby_pipe/utils.py +++ b/bilby_pipe/utils.py @@ -29,6 +29,11 @@ class BilbyPipeError(Exception): super().__init__(message) +class BilbyPipeInternalError(Exception): + def __init__(self, message): + super().__init__(message) + + class ArgumentsString(object): """ A convenience object to aid in the creation of argument strings """ diff --git a/tests/input_test.py b/tests/input_test.py index ea0c6379..9a85192e 100644 --- a/tests/input_test.py +++ b/tests/input_test.py @@ -1,14 +1,20 @@ import os import unittest +import pandas as pd + import bilby import bilby_pipe -from bilby_pipe.utils import BilbyPipeError +from bilby_pipe.utils import BilbyPipeError, BilbyPipeInternalError class TestInput(unittest.TestCase): def setUp(self): self.test_gps_file = "tests/gps_file.txt" + self.test_injection_file_json = ( + "tests/lalinference_test_injection_standard.json" + ) + self.test_injection_file_dat = "tests/lalinference_test_injection_standard.dat" def tearDown(self): pass @@ -270,6 +276,148 @@ class TestInput(unittest.TestCase): inputs.frequency_domain_source_model = "unknown" inputs.bilby_roq_frequency_domain_source_model + def test_injection_numbers_unset(self): + inputs = bilby_pipe.main.Input() + with self.assertRaises(BilbyPipeInternalError): + inputs.injection_numbers + + def test_injection_numbers_None(self): + inputs = bilby_pipe.main.Input() + inputs.injection_numbers = None + self.assertEqual(inputs.injection_numbers, None) + + def test_injection_numbers_list(self): + inputs = bilby_pipe.main.Input() + inputs.injection_numbers = [1, 2, 3] + self.assertEqual(inputs.injection_numbers, [1, 2, 3]) + + def test_injection_numbers_None_list(self): + inputs = bilby_pipe.main.Input() + inputs.injection_numbers = [None] + self.assertEqual(inputs.injection_numbers, None) + + def test_injection_numbers_None_str_list(self): + inputs = bilby_pipe.main.Input() + inputs.injection_numbers = ["None"] + self.assertEqual(inputs.injection_numbers, None) + + def test_injection_numbers_invalid_str(self): + inputs = bilby_pipe.main.Input() + with self.assertRaises(BilbyPipeError): + inputs.injection_numbers = ["a"] + + def test_injection_df_nonpandas(self): + inputs = bilby_pipe.main.Input() + with self.assertRaises(BilbyPipeError): + inputs.injection_df = dict(a=1) + + def test_injection_df(self): + inputs = bilby_pipe.main.Input() + df = pd.DataFrame(dict(a=[1, 2, 3])) + inputs.injection_numbers = None + inputs.injection_df = df + self.assertTrue(all(inputs.injection_df == df)) + + def test_injection_df_injection_numbers(self): + inputs = bilby_pipe.main.Input() + df = pd.DataFrame(dict(a=[1, 2, 3])) + df_trunc = pd.DataFrame(dict(a=[1, 2])) + inputs.injection_numbers = [0, 1] + inputs.injection_df = df + self.assertTrue(all(inputs.injection_df == df_trunc)) + + def test_injection_df_injection_numbers_fail(self): + inputs = bilby_pipe.main.Input() + df = pd.DataFrame(dict(a=[1, 2, 3])) + inputs.injection_numbers = [0, 1, 10] + with self.assertRaises(BilbyPipeError): + inputs.injection_df = df + + def test_injection_numbers_invalid_float(self): + inputs = bilby_pipe.main.Input() + with self.assertRaises(BilbyPipeError): + inputs.injection_numbers = [1.5] + + def test_injection_file_set_none(self): + inputs = bilby_pipe.main.Input() + inputs.injection_file = None + self.assertTrue(inputs.injection_file is None) + + def test_injection_file_set_no_file(self): + inputs = bilby_pipe.main.Input() + with self.assertRaises(FileNotFoundError): + inputs.injection_file = "this/is/not/a/file" + + def test_injection_file_json_set(self): + inputs = bilby_pipe.main.Input() + inputs.injection_numbers = None + inputs.injection_file = self.test_injection_file_json + self.assertTrue(len(inputs.injection_df) == 1) + self.assertTrue(inputs.injection_df["mass_1"].values[0] == 30) + self.assertTrue(inputs.injection_file == self.test_injection_file_json) + + def test_injection_file_dat_set(self): + inputs = bilby_pipe.main.Input() + inputs.injection_numbers = None + inputs.injection_file = self.test_injection_file_dat + self.assertTrue(len(inputs.injection_df) == 1) + self.assertTrue(inputs.injection_df["mass_1"].values[0] == 30) + self.assertTrue(inputs.injection_file == self.test_injection_file_dat) + + def test_injection_file_json_dat_equiv(self): + inputs_dat = bilby_pipe.main.Input() + inputs_dat.injection_numbers = None + inputs_dat.injection_file = self.test_injection_file_dat + + inputs_json = bilby_pipe.main.Input() + inputs_json.injection_numbers = None + inputs_json.injection_file = self.test_injection_file_json + + self.assertTrue(all(inputs_dat.injection_df == inputs_json.injection_df)) + + def test_injection_file_set_with_numbers(self): + inputs = bilby_pipe.main.Input() + inputs.injection_numbers = [0] + inputs.injection_file = self.test_injection_file_json + self.assertTrue(len(inputs.injection_df) == 1) + self.assertTrue(inputs.injection_df["mass_1"].values[0] == 30) + + def test_injection_file_set_with_invalid_numbers(self): + inputs = bilby_pipe.main.Input() + inputs.injection_numbers = [1] + with self.assertRaises(BilbyPipeError): + inputs.injection_file = self.test_injection_file_json + + def test_injection_dict_set_None(self): + inputs = bilby_pipe.main.Input() + inputs.injection_dict = None + self.assertEqual(inputs.injection_dict, None) + + def test_injection_dict_set_dict(self): + inputs = bilby_pipe.main.Input() + dict_test = dict(a=1, b=2) + inputs.injection_numbers = None + inputs.injection_dict = dict_test + self.assertEqual(dict_test, inputs.injection_dict) + + def test_injection_dict_set_str(self): + inputs = bilby_pipe.main.Input() + dict_str = "{a=1, b=2}" + dict_test = dict(a=1, b=2) + inputs.injection_numbers = None + inputs.injection_dict = dict_str + self.assertEqual(dict_test, inputs.injection_dict) + + def test_injection_dict_set_fail(self): + inputs = bilby_pipe.main.Input() + with self.assertRaises(BilbyPipeError): + inputs.injection_dict = "fail" + + def test_injection_dict_set_fail_int(self): + inputs = bilby_pipe.main.Input() + with self.assertRaises(BilbyPipeError): + inputs.injection_dict = 1 + if __name__ == "__main__": unittest.main() diff --git a/tests/lalinference_test_injection_standard.dat b/tests/lalinference_test_injection_standard.dat new file mode 100644 index 00000000..4cf07750 --- /dev/null +++ b/tests/lalinference_test_injection_standard.dat @@ -0,0 +1,2 @@ +mass_1 mass_2 luminosity_distance psi phase geocent_time ra dec theta_jn a_1 a_2 tilt_1 tilt_2 phi_12 phi_jl +30.0 30.0 1196.094970703125 4.598893165588379 0.15484610199928284 441417609.0 0.33732008934020996 -0.19791379570960999 2.1890859603881836 0.0 0.0 1.5707963267948966 1.5707963267948966 0.0 0.0 diff --git a/tests/lalinference_test_injection_standard.json b/tests/lalinference_test_injection_standard.json new file mode 100644 index 00000000..5cab8197 --- /dev/null +++ b/tests/lalinference_test_injection_standard.json @@ -0,0 +1,52 @@ +{ + "injections": { + "__dataframe__": true, + "content": { + "mass_1": [ + 30.0 + ], + "mass_2": [ + 30.0 + ], + "luminosity_distance": [ + 1196.094970703125 + ], + "psi": [ + 4.598893165588379 + ], + "phase": [ + 0.15484610199928284 + ], + "geocent_time": [ + 441417609.0 + ], + "ra": [ + 0.33732008934020996 + ], + "dec": [ + -0.19791379570960999 + ], + "theta_jn": [ + 2.1890859603881836 + ], + "a_1": [ + 0.0 + ], + "a_2": [ + 0.0 + ], + "tilt_1": [ + 1.5707963267948966 + ], + "tilt_2": [ + 1.5707963267948966 + ], + "phi_12": [ + 0.0 + ], + "phi_jl": [ + 0.0 + ] + } + } +} \ No newline at end of file -- GitLab