def parse_command_line():

    parser = OptionParser(usage=usage)

    #
    # waveform options
    #
    parser.add_option(
        "--approximant",
        choices=waveforms.keys(),
        metavar='|'.join(waveforms.keys()),
        default=None,
        help="Required. Specify the approximant to use for waveform generation."
    )
    parser.add_option(
        "--use-metric",
        action="store_true",
        default=False,
        help=
        "Use analytic approximation to the numerical match calculation (if available)."
    )
    parser.add_option(
        "--duration-min",
        type=float,
        help="Set minimum allowed duration of the template waveform in seconds."
    )
    parser.add_option(
        "--duration-max",
        type=float,
        help="Set maximum allowed duration of the template waveform in seconds."
    )

    #
    # mass parameter options
    #
    parser.add_option(
        "--mass1-min",
        help="Required. Set minimum mass of the first component.",
        type="float",
        metavar="MASS")
    parser.add_option(
        "--mass1-max",
        help="Required. Set maximum mass of the first component.",
        type="float",
        metavar="MASS")
    parser.add_option(
        "--mass2-min",
        help=
        "Set minimum mass of the second component. If not specified, the mass limits provided on the first component will be assumed for the second component.",
        type="float",
        metavar="MASS")
    parser.add_option(
        "--mass2-max",
        help=
        "Set maximum mass of the second component. If not specified, the mass limits provided on the first component will be assumed for the second component.",
        type="float",
        metavar="MASS")
    parser.add_option("--mtotal-min",
                      help="Set minimum total mass of the system.",
                      type="float",
                      metavar="MASS")
    parser.add_option("--mtotal-max",
                      help="Set maximum total mass of the system.",
                      type="float",
                      metavar="MASS")
    parser.add_option(
        "--mratio-min",
        dest="qmin",
        help=
        "Set minimum allowed mass ratio of the system (convention is that q=m1/m2).",
        metavar="RATIO",
        type="float",
        default=1.0)
    parser.add_option(
        "--mratio-max",
        dest="qmax",
        help=
        "Set maximum allowed mass ratio of the system (convention is that q=m1/m2).",
        metavar="RATIO",
        type="float")

    #
    # spin parameter options
    #
    parser.add_option(
        "--spin1-min",
        help=
        "Set minimum allowed value for the spin of the first component. If spins are aligned, this parameter is interpreted as the projection of the spin vector along the orbital angualr momentum and can be positive or negative. If the spins are not aligned, this parameter is interpreted as the magnitude of the spin vector and must be positive.",
        type="float",
        default=None,
        metavar="SPIN")
    parser.add_option(
        "--spin1-max",
        help="Set maximum allowed value for the spin of the first component.",
        type="float",
        default=None,
        metavar="SPIN")
    parser.add_option(
        "--spin2-min",
        help=
        "Set minimum allowed value for the spin of the second component. If not specified, the spin2 limits will equal the spin1 limits.",
        type="float",
        default=None,
        metavar="SPIN")
    parser.add_option(
        "--spin2-max",
        help="Set maximum allowed value for the spin of the second component.",
        type="float",
        default=None,
        metavar="SPIN")
    parser.add_option(
        "--aligned-spin",
        action="store_true",
        default=False,
        help=
        "Only generate templates whose spins are parallel to the orbital angular momentum."
    )
    parser.add_option(
        "--ns-bh-boundary-mass",
        type=float,
        metavar="MASS",
        help=
        "Use spin bounds based on whether the object is a black hole or a neutron star. Objects with mass smaller (larger) than the given value are considered NSs (BHs) and use spin bounds given by --ns-spin-{min,max} (--bh-spin-{min,max}) rather than --spin{1,2}-{min,max}."
    )
    parser.add_option(
        "--bh-spin-min",
        type=float,
        metavar="SPIN",
        help="Minimum spin for black holes when using --ns-bh-boundary-mass.")
    parser.add_option(
        "--bh-spin-max",
        type=float,
        metavar="SPIN",
        help="Maximum spin for black holes when using --ns-bh-boundary-mass.")
    parser.add_option(
        "--ns-spin-min",
        type=float,
        metavar="SPIN",
        help="Minimum spin for neutron stars when using --ns-bh-boundary-mass."
    )
    parser.add_option(
        "--ns-spin-max",
        type=float,
        metavar="SPIN",
        help="Maximum spin for neutron stars when using --ns-bh-boundary-mass."
    )

    #
    # initial condition options
    #
    parser.add_option(
        "--seed",
        help=
        "Set the seed for the random number generator used by SBank for waveform parameter (masss, spins, ...) generation.",
        metavar="INT",
        default=1729,
        type="int")
    parser.add_option(
        "--bank-seed",
        metavar="FILE[:APPROX]",
        help=
        "Add templates from FILE to the initial bank. If APPROX is also specified, the templates from this seed bank will be computed with this approximant instead of the one specified by --approximant. Can be specified multiple times. Only the additional templates will be outputted.",
        action="append",
        default=[])
    parser.add_option(
        "--trial-waveforms",
        metavar="FILE",
        help=
        "If supplied, instead of choosing points randomly, choose trial points from the sngl_inspiral table within the supplied XML file. Generation will terminate if the end of the file is reached unless any other termination condition is met first."
    )

    #
    # noise model options
    #
    parser.add_option(
        "--noise-model",
        choices=noise_models.keys(),
        metavar='|'.join(noise_models.keys()),
        default="aLIGOZeroDetHighPower",
        help=
        "Choose a noise model for the PSD from a set of available analytical model."
    )
    parser.add_option(
        "--reference-psd",
        help=
        "Read PSD from an xml file instead of using analytical noise model. The PSD is assumed to be infinite beyond the maximum frequency contained in the file. This effectively sets the upper frequency cutoff to that frequency, unless a smaller frequency is given via --fhigh-max.",
        metavar="FILE")
    parser.add_option(
        "--instrument",
        metavar="IFO",
        help=
        "Specify the instrument from input PSD file for which to generate a template bank."
    )

    #
    # match calculation options
    #
    parser.add_option(
        "--flow",
        type="float",
        help=
        "Required. Set the low-frequency cutoff to use for the match caluclation."
    )
    parser.add_option(
        "--optimize-flow",
        type=float,
        metavar="FRACTION",
        help=
        "Increase the low-frequency cutoff by a variable amount so as to make each waveform as short as possible, but recovering at least FRACTION of the range as calculated using the original cutoff. The resulting frequency is stored according to the --flow-column option."
    )
    parser.add_option(
        "--match-min",
        help=
        "Set minimum match of the bank. Note that since this is a stochastic process, the requested minimal match may not be strictly guaranteed but should be fulfilled on a statistical basis. Default: 0.95.",
        type="float",
        default=0.95)
    parser.add_option(
        "--convergence-threshold",
        metavar="N",
        help=
        "Set the criterion for convergence of the stochastic bank. The code terminates when there are N rejected proposals for each accepted proposal, averaged over the last ten acceptances. Default 1000.",
        type="int",
        default=1000)
    parser.add_option(
        "--max-new-templates",
        metavar="N",
        help=
        "Use this option to force the code to exit after accepting a specified number N of new templates. Note that the code may exit with fewer than N templates if the convergence criterion is met first.",
        type="int",
        default=float('inf'))
    parser.add_option(
        "--cache-waveforms",
        default=False,
        action="store_true",
        help=
        "A given waveform in the template bank will be used many times throughout the bank generation process. You can save a considerable amount of CPU by caching the waveform from the first time it is generated; however, do so only if you are sure that storing the waveforms in memory will not overload the system memory."
    )
    parser.add_option(
        "--coarse-match-df",
        type="float",
        default=None,
        help=
        "If given, use this value of df to quickly test if the mismatch is less than 4 times the minimal mismatch. This can quickly reject points at high values of df, that will not have high overlaps at smaller df values. This can be used to speed up the sbank process."
    )
    parser.add_option(
        "--iterative-match-df-max",
        type="float",
        default=None,
        help=
        "If this option is given it will enable sbank using larger df values than 1 / data length when computing overlaps. Sbank will then compute a match at this value, and at half this value, if the two values agree to 0.1% the value obtained will be taken as the actual value. If the values disagree the match will be computed again using a df another factor of 2 smaller until convergence or a df of 1/ data_length, is reached."
    )
    parser.add_option(
        "--fhigh-max",
        type="float",
        default=None,
        help=
        "If given, generate waveforms and compute matches only to this frequency. The number will be rounded up to the nearest power of 2."
    )
    parser.add_option(
        "--neighborhood-size",
        metavar="N",
        default=0.25,
        type="float",
        help=
        "Specify the window size in seconds to define \"nearby\" templates used to compute the match against each proposed template. The neighborhood is chosen symmetric about the proposed template; \"nearby\" is defined using the option --neighborhood-type. The default value of 0.25 is *not a guarantee of performance*. Choosing the neighborhood too small will lead to larger banks (but also higher bank coverage)."
    )
    parser.add_option(
        "--neighborhood-param",
        default="tau0",
        choices=["tau0", "dur"],
        help="Choose how the neighborhood is sorted for match calculations.")
    parser.add_option(
        "--checkpoint",
        default=0,
        metavar="N",
        help=
        "Periodically save the bank to disk every N templates (set to 0 to disable).",
        type="int",
        action="store")

    #
    # output options
    #
    parser.add_option(
        "--output-filename",
        default=None,
        help=
        "Required. Name for output template bank. May not clash with seed bank."
    )
    parser.add_option(
        "--verbose",
        default=False,
        action="store_true",
        help="Be verbose and write diagnostic information out to file.")
    parser.add_option(
        "--flow-column",
        type=str,
        metavar="NAME",
        help=
        "If given, store the low-frequency cutoff for each template in column NAME of the single-inspiral table."
    )

    parser.add_option(
        "--mchirp-boundaries-file",
        metavar="FILE",
        help="Deprecated. File containing chirp mass bin boundaries")
    parser.add_option(
        "--mchirp-boundaries-index",
        metavar="INDEX",
        type="int",
        help=
        "Deprecated. Integer index into --mchirp-boundaries-file line number such that boundaries[INDEX] is taken as --mchirp-min and boundaries[INDEX + 1] is taken as --mchirp-max"
    )
    parser.add_option(
        "--mchirp-min",
        help=
        "Deprecated. Set minimum chirp-mass of the system (in solar masses)",
        type="float")
    parser.add_option(
        "--mchirp-max",
        help=
        "Deprecated. Set maximum chirp-mass of the system (in solar masses)",
        type="float")

    opts, args = parser.parse_args()

    #
    # check for required arguments
    #
    for opt in ("flow", "match_min", "mass1_min", "mass1_max",
                "output_filename"):
        if getattr(opts, opt) is None:
            parser.error("--%s is required" % opt.replace("_", "-"))

    #
    # check for argument consistency
    #
    for seed in opts.bank_seed:
        if seed == opts.output_filename:
            raise ValueError(
                "Bank seed %s would be overwritten by output file. Choose a different output name."
                % seed)

    if opts.qmin < 1:
        parser.error("Mass ratio is assumed to be >= 1.")

    numeric_spin_opt_presence = [getattr(opts, x + '_' + y) is not None \
                                 for x in ['spin1', 'spin2'] for y in ['min', 'max']]
    all_numeric_spin_opts = False not in numeric_spin_opt_presence
    any_numeric_spin_opts = True in numeric_spin_opt_presence

    nsbh_spin_opt_presence = [getattr(opts, x + '_' + y) is not None \
                              for x in ['bh_spin', 'ns_spin'] for y in ['min', 'max']]
    all_nsbh_spin_opts = False not in nsbh_spin_opt_presence
    any_nsbh_spin_opts = True in nsbh_spin_opt_presence

    if any_numeric_spin_opts and any_nsbh_spin_opts:
        parser.error("conflicting specification of spin bounds")
    if any_nsbh_spin_opts and opts.ns_bh_boundary_mass is None:
        parser.error("NSBH spin bounds require --ns-bh-boundary-mass")
    if opts.ns_bh_boundary_mass is not None and not any_nsbh_spin_opts:
        parser.error(
            "--ns-bh-boundary-mass requires NSBH spin bounds (--bh-spin-* etc)"
        )

    if all_numeric_spin_opts:
        if not -1 <= opts.spin1_min <= opts.spin1_max <= 1:
            parser.error("unphysical spin1 bounds: [%.2f, %.2f]" %
                         (opts.spin1_min, opts.spin1_max))
        if not -1 <= opts.spin2_min <= opts.spin2_max <= 1:
            parser.error("unphysical spin2 bounds: [%.2f, %.2f]" %
                         (opts.spin2_min, opts.spin2_max))
    elif all_nsbh_spin_opts:
        if not -1 <= opts.bh_spin_min <= opts.bh_spin_max <= 1:
            parser.error("unphysical BH spin bounds: [%.2f, %.2f]" %
                         (opts.bh_spin_min, opts.bh_spin_max))
        if not -1 <= opts.ns_spin_min <= opts.ns_spin_max <= 1:
            parser.error("unphysical NS spin bounds: [%.2f, %.2f]" %
                         (opts.ns_spin_min, opts.ns_spin_max))
    else:
        # default spin bounds
        if opts.spin1_min is None:
            opts.spin1_min = -1
        if opts.spin1_max is None:
            opts.spin1_max = 1
        if opts.spin2_min is None:
            opts.spin2_min = opts.spin1_min
        if opts.spin2_max is None:
            opts.spin2_max = opts.spin1_max

    if opts.approximant in [
            "TaylorF2RedSpin", "IMRPhenomB", "IMRPhenomC", "IMRPhenomD",
            "SEOBNRv1", "SEOBNRv2", "SEOBNRv2_ROM_DoubleSpin",
            "SEOBNRv2_ROM_DoubleSpin_HI"
    ] and not opts.aligned_spin:
        parser.error("--aligned-spin is required for the %s approximant" %
                     opts.approximant)

    if opts.approximant in ["IMRPhenomPv2", "SEOBNRv3"] and opts.aligned_spin:
        opts.approximant = {
            "IMRPhenomPv2": "IMRPhenomD",
            "SEOBNRv3": "SEOBNRv2"
        }[opts.approximant]

    if (opts.mchirp_boundaries_file
            is not None) ^ (opts.mchirp_boundaries_index is not None):
        parser.error(
            "must supply both --mchirp-boundaries-file and --mchirp-boundaries-index or neither"
        )

    if opts.mchirp_boundaries_file:
        boundaries = [
            float(line) for line in open(opts.mchirp_boundaries_file)
        ]
        if opts.mchirp_boundaries_index > len(boundaries):
            raise ValueError(
                "mchirp boundaries file not long enough for requested index")

        if opts.mchirp_boundaries_index > 0:
            boundary_mchirp_min = float(
                boundaries[opts.mchirp_boundaries_index - 1])
            if opts.mchirp_min is None or opts.mchirp_min < boundary_mchirp_min:
                opts.mchirp_min = boundary_mchirp_min
        if opts.mchirp_boundaries_index + 1 < len(boundaries):
            boundary_mchirp_max = float(
                boundaries[opts.mchirp_boundaries_index])
            if opts.mchirp_max is None or opts.mchirp_max > boundary_mchirp_max:
                opts.mchirp_max = boundary_mchirp_max

    if opts.optimize_flow is not None:
        if opts.optimize_flow >= 1 or opts.optimize_flow <= 0:
            parser.error(
                '--optimize-flow takes a value between 0 and 1, excluded')
        if opts.flow_column is None and \
                opts.output_filename.endswith(('.xml', '.xml.gz')):
            parser.error(
                '--flow-column is required when using --optimize-flow')

    if opts.checkpoint and not opts.output_filename.endswith(
        ('.xml', '.xml.gz')):
        err_msg = "Checkpointing currently only supported for XML format."
        raise ValueError(err_msg)

    return opts, args
        --mass1-min 1 --mass1-max 10 \\
        --mass2-min 1 --mass2-max 10 \\
        --mtotal-max 10 \\
        --ns-bh-boundary-mass 2 \\
        --bh-spin-min -0.98 --bh-spin-max 0.98 \\
        --ns-spin-min -0.4 --ns-spin-max 0.4 \\
        --match-min 0.97 --flow 30 --noise-model aLIGOZeroDetHighPower \\
        --output-filename combined-TaylorF2RedSpin-aLIGOZeroDetHighPower.xml.gz --verbose


For large parameter spaces with many templates, it is recommended that
you split the space into smaller sub-regions and ligolw_add the
resulting banks. One can also seed the template placement process with
a pre-generated bank, produced for instance by lalapps_tmpltbank, and
SBank will fill in whichever gaps remain. See also lalapps_cbc_sbank_pipe.
""" % '\n\t'.join(sorted(waveforms.keys()))


#
# callback function for periodic checkpointing
#
def checkpoint_save(xmldoc, fout, process):

    print >> sys.stderr, "\t[Checkpointing ...]"

    # save rng state
    rng_state = np.random.get_state()
    np.savez(fout + "_checkpoint.rng.npz",
             state1=rng_state[1],
             state2=np.array(rng_state[2]),
             state3=np.array(rng_state[3]),
        --mass2-min 1 --mass2-max 10 \\
        --mtotal-max 10 \\
        --ns-bh-boundary-mass 2 \\
        --bh-spin-min -0.98 --bh-spin-max 0.98 \\
        --ns-spin-min -0.4 --ns-spin-max 0.4 \\
        --match-min 0.97 --flow 30 --noise-model aLIGOZeroDetHighPower \\
        --instrument H1 --gps-start-time 961545543 --gps-end-time 962150343 \\
        --user-tag combined-TaylorF2RedSpin-aLIGOZeroDetHighPower --verbose


For large parameter spaces with many templates, it is recommended that
you split the space into smaller sub-regions and ligolw_add the
resulting banks. One can also seed the template placement process with
a pre-generated bank, produced for instance by lalapps_tmpltbank, and
SBank will fill in whichever gaps remain. See also lalapps_cbc_sbank_pipe.
""" % '\n\t'.join(sorted(waveforms.keys()))


#
# callback function for periodic checkpointing
#
def checkpoint_save(xmldoc, fout, process):

    print >>sys.stderr, "\t[Checkpointing ...]"

    # save rng state
    rng_state = np.random.get_state()
    np.savez(fout + "_checkpoint.rng.npz",
             state1=rng_state[1],
             state2=np.array(rng_state[2]),
             state3=np.array(rng_state[3]),
def parse_command_line():

    parser = OptionParser(usage = usage)

    #
    # waveform options
    #
    parser.add_option("--approximant", choices=waveforms.keys(), metavar='|'.join(waveforms.keys()), default=None, help="Required. Specify the approximant to use for waveform generation.")
    parser.add_option("--use-metric", action="store_true", default=False, help="Use analytic approximation to the numerical match calculation (if available).")

    #
    # mass parameter options
    #
    parser.add_option("--mass1-min",help="Required. Set minimum mass of the first component.", type="float", metavar="MASS")
    parser.add_option("--mass1-max",help="Required. Set maximum mass of the first component.", type="float", metavar="MASS")
    parser.add_option("--mass2-min",help="Set minimum mass of the second component. If not specified, the mass limits provided on the first component will be assumed for the second component.", type="float", metavar="MASS")
    parser.add_option("--mass2-max",help="Set maximum mass of the second component. If not specified, the mass limits provided on the first component will be assumed for the second component.", type="float", metavar="MASS")
    parser.add_option("--mtotal-min", help="Set minimum total mass of the system.", type="float", metavar="MASS")
    parser.add_option("--mtotal-max", help="Set maximum total mass of the system.",  type="float", metavar="MASS")
    parser.add_option("--mratio-min", dest="qmin", help="Set minimum allowed mass ratio of the system (convention is that q=m1/m2).", metavar="RATIO", type="float", default=1.0)
    parser.add_option("--mratio-max", dest="qmax", help="Set maximum allowed mass ratio of the system (convention is that q=m1/m2).", metavar="RATIO", type="float")

    #
    # spin parameter options
    #
    parser.add_option("--spin1-min", help="Set minimum allowed value for the spin of the first component. If spins are aligned, this parameter is interpreted as the projection of the spin vector along the orbital angualr momentum and can be positive or negative. If the spins are not aligned, this parameter is interpreted as the magnitude of the spin vector and must be positive.", type="float", default = None, metavar="SPIN")
    parser.add_option("--spin1-max", help="Set maximum allowed value for the spin of the first component.", type="float", default = None, metavar="SPIN")
    parser.add_option("--spin2-min", help="Set minimum allowed value for the spin of the second component. If not specified, the spin2 limits will equal the spin1 limits.", type="float", default = None, metavar="SPIN")
    parser.add_option("--spin2-max", help="Set maximum allowed value for the spin of the second component.", type="float", default = None, metavar="SPIN")
    parser.add_option("--aligned-spin", action="store_true", default=False, help="Only generate templates whose spins are parallel to the orbital angular momentum.")
    parser.add_option("--ns-bh-boundary-mass", type=float, metavar="MASS", help="Use spin bounds based on whether the object is a black hole or a neutron star. Objects with mass smaller (larger) than the given value are considered NSs (BHs) and use spin bounds given by --ns-spin-{min,max} (--bh-spin-{min,max}) rather than --spin{1,2}-{min,max}.")
    parser.add_option("--bh-spin-min", type=float, metavar="SPIN", help="Minimum spin for black holes when using --ns-bh-boundary-mass.")
    parser.add_option("--bh-spin-max", type=float, metavar="SPIN", help="Maximum spin for black holes when using --ns-bh-boundary-mass.")
    parser.add_option("--ns-spin-min", type=float, metavar="SPIN", help="Minimum spin for neutron stars when using --ns-bh-boundary-mass.")
    parser.add_option("--ns-spin-max", type=float, metavar="SPIN", help="Maximum spin for neutron stars when using --ns-bh-boundary-mass.")

    #
    # initial condition options
    #
    parser.add_option("--seed", help="Set the seed for the random number generator used by SBank for waveform parameter (masss, spins, ...) generation.", metavar="INT", default=1729, type="int")
    parser.add_option("--bank-seed",help="Initialize the bank with specified template bank. For instance, one might generate a template bank by geomtretic/lattice placement methods and use SBank to \"complete\" the bank. NOTE: Only the additional templates will be outputted and to complete the bank you will need to add the output of this to the original bank", metavar="FILE")

    #
    # noise model options
    #
    parser.add_option("--noise-model", choices=noise_models.keys(), metavar='|'.join(noise_models.keys()), default="aLIGOZeroDetHighPower", help="Choose a noise model for the PSD from a set of available analytical model.")
    parser.add_option("--reference-psd", help="Read PSD from an xml file instead of using analytical noise model. The PSD is assumed to be infinite beyond the maximum frequency contained in the file. This effectively sets the upper frequency cutoff to that frequency, unless a smaller frequency is given via --fhigh-max.", metavar="FILE")

    #
    # match calculation options
    #
    parser.add_option("--flow", type="float", help="Required. Set the low-frequency cutoff to use for the match caluclation.")
    parser.add_option("--match-min",help="Set minimum match of the bank. Note that since this is a stochastic process, the requested minimal match may not be strictly guaranteed but should be fulfilled on a statistical basis. Default: 0.95.", type="float", default=0.95)
    parser.add_option("--convergence-threshold", metavar="N", help="Set the criterion for convergence of the stochastic bank. The code terminates when there are N rejected proposals for each accepted proposal, averaged over the last ten acceptances. Default 1000.", type="int", default=1000)
    parser.add_option("--templates-max", metavar="N", help="Use this option to force the code to exit after generating a specified number N of templates. Note that the code may exit with fewer than N templates if the convergence criterion is met first.", type="int", default=float('inf'))
    parser.add_option("--cache-waveforms", default = False, action="store_true", help="A given waveform in the template bank will be used many times throughout the bank generation process. You can save a considerable amount of CPU by caching the waveform from the first time it is generated; however, do so only if you are sure that storing the waveforms in memory will not overload the system memory.")
    parser.add_option("--coarse-match-df", type="float", default=None, help="If given, use this value of df to quickly test if the mismatch is less than 4 times the minimal mismatch. This can quickly reject points at high values of df, that will not have high overlaps at smaller df values. This can be used to speed up the sbank process.")
    parser.add_option("--iterative-match-df-max", type="float", default=None, help="If this option is given it will enable sbank using larger df values than 1 / data length when computing overlaps. Sbank will then compute a match at this value, and at half this value, if the two values agree to 0.1% the value obtained will be taken as the actual value. If the values disagree the match will be computed again using a df another factor of 2 smaller until convergence or a df of 1/ data_length, is reached.")
    parser.add_option("--fhigh-max", type="float", default=None, help="If given, generate waveforms and compute matches only to this frequency. The number will be rounded up to the nearest power of 2.")
    parser.add_option("--neighborhood-size", metavar="N", default = 0.25, type="float", help="Specify the window size in seconds to define \"nearby\" templates used to compute the match against each proposed template. The neighborhood is chosen symmetric about the proposed template; \"nearby\" is defined using the option --neighborhood-type. The default value of 0.25 is *not a guarantee of performance*. Choosing the neighborhood too small will lead to larger banks (but also higher bank coverage).")
    parser.add_option("--neighborhood-param", default="tau0", choices=["tau0","dur"], help="Choose how the neighborhood is sorted for match calculations.")
    parser.add_option("--checkpoint", default=0, metavar="N", help="Periodically save the bank to disk every N templates (set to 0 to disable).", type="int", action="store")


    #
    # output options
    #
    parser.add_option("--instrument", metavar="IFO", help="Specify the instrument for which to generate a template bank. This option is used for naming of the output file but also for reading in PSDs or template bank seeds from file.")
    parser.add_option("--gps-start-time", type="int", default=0, help="GPS time of start. Used only for naming of output file.", metavar="INT")
    parser.add_option("--gps-end-time", type="int", default=999999999, help="GPS time of end. Used only for naming of output file", metavar="INT")
    parser.add_option("--user-tag", default=None, help="Apply descriptive tag to output filename.")
    parser.add_option("--verbose", default=False,action="store_true", help="Be verbose and write diagnostic information out to file.")

    parser.add_option("--mchirp-boundaries-file", metavar="FILE", help="Deprecated. File containing chirp mass bin boundaries")
    parser.add_option("--mchirp-boundaries-index", metavar="INDEX", type="int", help="Deprecated. Integer index into --mchirp-boundaries-file line number such that boundaries[INDEX] is taken as --mchirp-min and boundaries[INDEX + 1] is taken as --mchirp-max")
    parser.add_option("--mchirp-min", help="Deprecated. Set minimum chirp-mass of the system (in solar masses)", type="float")
    parser.add_option("--mchirp-max", help="Deprecated. Set maximum chirp-mass of the system (in solar masses)", type="float")

    opts, args = parser.parse_args()

    #
    # check for required arguments
    #
    for opt in ("flow", "match_min", "mass1_min", "mass1_max", "instrument"):
        if getattr(opts, opt) is None:
            parser.error("--%s is required" % opt.replace("_", "-"))

    #
    # check for argument consistency
    #
    if opts.qmin < 1:
        parser.error("Mass ratio is assumed to be >= 1.")

    numeric_spin_opt_presence = [getattr(opts, x + '_' + y) is not None \
                                 for x in ['spin1', 'spin2'] for y in ['min', 'max']]
    all_numeric_spin_opts = False not in numeric_spin_opt_presence
    any_numeric_spin_opts = True in numeric_spin_opt_presence

    nsbh_spin_opt_presence = [getattr(opts, x + '_' + y) is not None \
                              for x in ['bh_spin', 'ns_spin'] for y in ['min', 'max']]
    all_nsbh_spin_opts = False not in nsbh_spin_opt_presence
    any_nsbh_spin_opts = True in nsbh_spin_opt_presence

    if any_numeric_spin_opts and any_nsbh_spin_opts:
        parser.error("conflicting specification of spin bounds")
    if any_nsbh_spin_opts and opts.ns_bh_boundary_mass is None:
        parser.error("NSBH spin bounds require --ns-bh-boundary-mass")
    if opts.ns_bh_boundary_mass is not None and not any_nsbh_spin_opts:
        parser.error("--ns-bh-boundary-mass requires NSBH spin bounds (--bh-spin-* etc)")

    if all_numeric_spin_opts:
        if not -1 <= opts.spin1_min <= opts.spin1_max <=1:
            parser.error("unphysical spin1 bounds: [%.2f, %.2f]" % (opts.spin1_min, opts.spin1_max))
        if not -1 <= opts.spin2_min <= opts.spin2_max <=1:
            parser.error("unphysical spin2 bounds: [%.2f, %.2f]" % (opts.spin2_min, opts.spin2_max))
    elif all_nsbh_spin_opts:
        if not -1 <= opts.bh_spin_min <= opts.bh_spin_max <= 1:
            parser.error("unphysical BH spin bounds: [%.2f, %.2f]" % (opts.bh_spin_min, opts.bs_spin_max))
        if not -1 <= opts.ns_spin_min <= opts.ns_spin_max <= 1:
            parser.error("unphysical NS spin bounds: [%.2f, %.2f]" % (opts.ns_spin_min, opts.ns_spin_max))
    else:
        # default spin bounds
        if opts.spin1_min is None:
            opts.spin1_min = -1
        if opts.spin1_max is None:
            opts.spin1_max = 1
        if opts.spin2_min is None:
            opts.spin2_min = opts.spin1_min
        if opts.spin2_max is None:
            opts.spin2_max = opts.spin1_max

    if opts.approximant in ["TaylorF2RedSpin", "IMRPhenomB","SEOBNRv1"] and not opts.aligned_spin:
        parser.error("--aligned-spin is required for the %s approximant" % opts.approximant)

    if (opts.mchirp_boundaries_file is not None) ^ (opts.mchirp_boundaries_index is not None):
        parser.error("must supply both --mchirp-boundaries-file and --mchirp-boundaries-index or neither")

    if opts.mchirp_boundaries_file and (opts.mchirp_min or opts.mchirp_max):
        parser.error("--mchirp-boundaries-file supercedes --mchirp-min and --mchirp-max")

    if opts.mchirp_boundaries_file:
        boundaries = [float(line) for line in open(opts.mchirp_boundaries_file)]
        if opts.mchirp_boundaries_index > len(boundaries):
            raise ValueError("mchirp boundaries file not long enough for requested index")

        if opts.mchirp_boundaries_index > 0:
            opts.mchirp_min = float(boundaries[opts.mchirp_boundaries_index - 1])
        if opts.mchirp_boundaries_index + 1 < len(boundaries):
            opts.mchirp_max = float(boundaries[opts.mchirp_boundaries_index])

    return opts, args
Esempio n. 5
0
def parse_command_line():

    parser = OptionParser(usage = usage)

    #
    # waveform options
    #
    parser.add_option("--approximant", choices=waveforms.keys(), metavar='|'.join(waveforms.keys()), default=None, help="Required. Specify the approximant to use for waveform generation.")
    parser.add_option("--use-metric", action="store_true", default=False, help="Use analytic approximation to the numerical match calculation (if available).")

    #
    # mass parameter options
    #
    parser.add_option("--mass1-min",help="Required. Set minimum mass of the first component.", type="float", metavar="MASS")
    parser.add_option("--mass1-max",help="Required. Set maximum mass of the first component.", type="float", metavar="MASS")
    parser.add_option("--mass2-min",help="Set minimum mass of the second component. If not specified, the mass limits provided on the first component will be assumed for the second component.", type="float", metavar="MASS")
    parser.add_option("--mass2-max",help="Set maximum mass of the second component. If not specified, the mass limits provided on the first component will be assumed for the second component.", type="float", metavar="MASS")
    parser.add_option("--mtotal-min", help="Set minimum total mass of the system.", type="float", metavar="MASS")
    parser.add_option("--mtotal-max", help="Set maximum total mass of the system.",  type="float", metavar="MASS")
    parser.add_option("--mratio-min", dest="qmin", help="Set minimum allowed mass ratio of the system (convention is that q=m1/m2).", metavar="RATIO", type="float", default=1.0)
    parser.add_option("--mratio-max", dest="qmax", help="Set maximum allowed mass ratio of the system (convention is that q=m1/m2).", metavar="RATIO", type="float")

    #
    # spin parameter options
    #
    parser.add_option("--spin1-min", help="Set minimum allowed value for the spin of the first component. If spins are aligned, this parameter is interpreted as the projection of the spin vector along the orbital angualr momentum and can be positive or negative. If the spins are not aligned, this parameter is interpreted as the magnitude of the spin vector and must be positive.", type="float", default = -1.0, metavar="SPIN")
    parser.add_option("--spin1-max", help="Set maximum allowed value for the spin of the first component.", type="float", default = 1.0, metavar="SPIN")
    parser.add_option("--spin2-min", help="Set minimum allowed value for the spin of the second component. If not specified, the spin2 limits will equal the spin1 limits.", type="float", default = None, metavar="SPIN")
    parser.add_option("--spin2-max", help="Set maximum allowed value for the spin of the second component.", type="float", default = None, metavar="SPIN")
    parser.add_option("--aligned-spin", action="store_true", default=False, help="Only generate templates whose spins are parallel to the orbital angular momentum.")

    #
    # initial condition options
    #
    parser.add_option("--seed", help="Set the seed for the random number generator used by SBank for waveform parameter (masss, spins, ...) generation.", metavar="INT", default=1729, type="int")
    parser.add_option("--bank-seed",help="Initialize the bank with specified template bank. For instance, one might generate a template bank by geomtretic/lattice placement methods and use SBank to \"complete\" the bank. NOTE: Only the additional templates will be outputted and to complete the bank you will need to add the output of this to the original bank", metavar="FILE")

    #
    # noise model options
    #
    parser.add_option("--noise-model", choices=noise_models.keys(), metavar='|'.join(noise_models.keys()), default="aLIGOZeroDetHighPower", help="Choose a noise model for the PSD from a set of available analytical model.")
    parser.add_option("--reference-psd", help="Read PSD from an xml file instead of using analytical noise model.", metavar="FILE")

    #
    # match calculation options
    #
    parser.add_option("--flow", type="float", help="Required. Set the low-frequency cutoff to use for the match caluclation.")
    parser.add_option("--match-min",help="Set minimum match of the bank. Note that since this is a stochastic process, the requested minimal match may not be strictly guaranteed but should be fulfilled on a statistical basis. Default: 0.95.", type="float", default=0.95)
    parser.add_option("--convergence-threshold", metavar="N", help="Set the criterion for convergence of the stochastic bank. The code terminates when there are N rejected proposals for each accepted proposal, averaged over the last ten acceptances. Default 1000.", type="int", default=1000)
    parser.add_option("--cache-waveforms", default = False, action="store_true", help="A given waveform in the template bank will be used many times throughout the bank generation process. You can save a considerable amount of CPU by caching the waveform from the first time it is generated; however, do so only if you are sure that storing the waveforms in memory will not overload the system memory.")

    #
    # output options
    #
    parser.add_option("--instrument", metavar="IFO", help="Specify the instrument for which to generate a template bank. This option is used for naming of the output file but also for reading in PSDs or template bank seeds from file.")
    parser.add_option("--gps-start-time", type="int", default=0, help="GPS time of start. Used only for naming of output file.", metavar="INT")
    parser.add_option("--gps-end-time", type="int", default=999999999, help="GPS time of end. Used only for naming of output file", metavar="INT")
    parser.add_option("--user-tag", default=None, help="Apply descriptive tag to output filename.")
    parser.add_option("--verbose", default=False,action="store_true", help="Be verbose and write diagnostic information out to file.")

    parser.add_option("--mchirp-boundaries-file", metavar="FILE", help="Deprecated. File containing chirp mass bin boundaries")
    parser.add_option("--mchirp-boundaries-index", metavar="INDEX", type="int", help="Deprecated. Integer index into --mchirp-boundaries-file line number such that boundaries[INDEX] is taken as --mchirp-min and boundaries[INDEX + 1] is taken as --mchirp-max")
    parser.add_option("--mchirp-min", help="Deprecated. Set minimum chirp-mass of the system (in solar masses)", type="float")
    parser.add_option("--mchirp-max", help="Deprecated. Set maximum chirp-mass of the system (in solar masses)", type="float")

    opts, args = parser.parse_args()

    #
    # check for required arguments
    #
    for opt in ("flow", "match_min", "mass1_min", "mass1_max", "instrument"):
        if getattr(opts, opt) is None:
            parser.error("--%s is required" % opt.replace("_", "-"))

    #
    # check for argument consistency
    #
    if opts.qmin < 1:
        parser.error("Mass ratio is assumed to be >= 1.")

    if not opts.spin2_min:
        opts.spin2_min = opts.spin1_min

    if not opts.spin2_max:
        opts.spin2_max = opts.spin1_max

    if not -1 <= opts.spin1_min <= opts.spin1_max <=1:
        raise ValueError("unphysical spin bounds: [%.2f, %.2f]" % (opts.spin1_min, opts.spin1_max))

    if not -1 <= opts.spin2_min <= opts.spin2_max <=1:
        raise ValueError("unphysical spin bounds: [%.2f, %.2f]" % (opts.spin2_min, opts.spin2_max))

    if opts.approximant in ["TaylorF2RedSpin", "IMRPhenomB","SEOBNRv1"] and not opts.aligned_spin:
        parser.error("--aligned-spin is required for the %s approximant" % opts.approximant)

    if (opts.mchirp_boundaries_file is not None) ^ (opts.mchirp_boundaries_index is not None):
        parser.error("must supply both --mchirp-boundaries-file and --mchirp-boundaries-index or neither")

    if opts.mchirp_boundaries_file and (opts.mchirp_min or opts.mchirp_max):
        parser.error("--mchirp-boundaries-file supercedes --mchirp-min and --mchirp-max")

    if opts.mchirp_boundaries_file:
        boundaries = [float(line) for line in open(opts.mchirp_boundaries_file)]
        if opts.mchirp_boundaries_index > len(boundaries):
            raise ValueError("mchirp boundaries file not long enough for requested index")

        if opts.mchirp_boundaries_index > 0:
            opts.mchirp_min = float(boundaries[opts.mchirp_boundaries_index - 1])
        if opts.mchirp_boundaries_index + 1 < len(boundaries):
            opts.mchirp_max = float(boundaries[opts.mchirp_boundaries_index])

    return opts, args