def tosegmentxml(file, segs):
    """
    Write the glue.segments.segmentlist object segs to file object file in xml
    format with appropriate tables.
  """

    # generate empty document
    xmldoc = ligolw.Document()
    xmldoc.appendChild(ligolw.LIGO_LW())
    xmldoc.childNodes[-1].appendChild(lsctables.New(lsctables.ProcessTable))
    xmldoc.childNodes[-1].appendChild(
        lsctables.New(lsctables.ProcessParamsTable))

    # append process to table
    process = ligolw_process.append_process(xmldoc,\
                                    program='pylal.dq.dqSegmentUtils',\
                                    version=__version__,\
                                    cvs_repository='lscsoft',\
                                    cvs_entry_time=__date__)

    gpssegs = segments.segmentlist()
    for seg in segs:
        gpssegs.append(
            segments.segment(LIGOTimeGPS(seg[0]), LIGOTimeGPS(seg[1])))

    # append segs and seg definer
    segments_tables = ligolw_segments.LigolwSegments(xmldoc)
    segments_tables.add(ligolw_segments.LigolwSegmentList(active=gpssegs))
    # finalise
    segments_tables.coalesce()
    segments_tables.optimize()
    segments_tables.finalize(process)
    ligolw_process.set_process_end_time(process)

    # write file
    with utils.SignalsTrap():
        utils.write_fileobj(xmldoc, file, gz=False)
            epoch = sngl_inspiral.end - (len(snr) - 1) / sample_rate
            snr = np.concatenate((snr[:0:-1].conj(), snr))
            snr *= sngl_inspiral.snr * np.exp(1j * sngl_inspiral.coa_phase)
            snr_series = lal.CreateCOMPLEX16TimeSeries('snr', epoch, 0,
                                                       dt, lal.StrainUnit,
                                                       len(snr))
            snr_series.data.data[:] = snr
            elem = lal.series.build_COMPLEX16TimeSeries(snr_series)
            elem.appendChild(
                ligolw_param.Param.from_pyvalue(u'event_id',
                                                sngl_inspiral.event_id))
            out_xmldoc.childNodes[0].appendChild(elem)

        # Add CoincMap entry.
        coinc_map = lsctables.CoincMap()
        coinc_map.coinc_event_id = coinc.coinc_event_id
        coinc_map.table_name = sngl_inspiral_table.tableName
        coinc_map.event_id = sngl_inspiral.event_id
        coinc_map_table.append(coinc_map)

# Record process end time.
progress.update(-1, 'writing ' + opts.output.name)
ligolw_process.set_process_end_time(process)

# Write output file.
with ligolw_utils.SignalsTrap():
    ligolw_utils.write_fileobj(out_xmldoc,
                               opts.output,
                               gz=(os.path.splitext(
                                   opts.output.name)[-1] == ".gz"))
def main(args=None):
    p = parser()
    opts = p.parse_args(args)

    # LIGO-LW XML imports.
    from glue.ligolw import ligolw
    from glue.ligolw.param import Param
    from glue.ligolw.utils import process as ligolw_process
    from glue.ligolw.utils.search_summary import append_search_summary
    from glue.ligolw import utils as ligolw_utils
    from glue.ligolw.lsctables import (
        New, CoincDefTable, CoincID, CoincInspiralTable, CoincMapTable,
        CoincTable, ProcessParamsTable, ProcessTable, SimInspiralTable,
        SnglInspiralTable, TimeSlideTable)

    # glue, LAL and pylal imports.
    from ligo import segments
    import glue.lal
    import lal.series
    import lalsimulation
    from lalinspiral.inspinjfind import InspiralSCExactCoincDef
    from lalinspiral.thinca import InspiralCoincDef
    from tqdm import tqdm

    # FIXME: disable progress bar monitor thread.
    #
    # I was getting error messages that look like this:
    #
    # Traceback (most recent call last):
    #   File "/tqdm/_tqdm.py", line 885, in __del__
    #     self.close()
    #   File "/tqdm/_tqdm.py", line 1090, in close
    #     self._decr_instances(self)
    #   File "/tqdm/_tqdm.py", line 454, in _decr_instances
    #     cls.monitor.exit()
    #   File "/tqdm/_monitor.py", line 52, in exit
    #     self.join()
    #   File "/usr/lib64/python3.6/threading.py", line 1053, in join
    #     raise RuntimeError("cannot join current thread")
    # RuntimeError: cannot join current thread
    #
    # I don't know what causes this... maybe a race condition in tqdm's cleanup
    # code. Anyway, this should disable the tqdm monitor thread entirely.
    tqdm.monitor_interval = 0

    # BAYESTAR imports.
    from ..io.events.ligolw import ContentHandler
    from ..bayestar import filter

    # Read PSDs.
    xmldoc, _ = ligolw_utils.load_fileobj(
        opts.reference_psd, contenthandler=lal.series.PSDContentHandler)
    psds = lal.series.read_psd_xmldoc(xmldoc, root_name=None)
    psds = {
        key: filter.InterpolatedPSD(filter.abscissa(psd), psd.data.data)
        for key, psd in psds.items() if psd is not None}
    psds = [psds[ifo] for ifo in opts.detector]

    # Extract simulation table from injection file.
    inj_xmldoc, _ = ligolw_utils.load_fileobj(
        opts.input, contenthandler=ContentHandler)
    orig_sim_inspiral_table = SimInspiralTable.get_table(inj_xmldoc)

    # Prune injections that are outside distance limits.
    orig_sim_inspiral_table[:] = [
        row for row in orig_sim_inspiral_table
        if opts.min_distance <= row.distance <= opts.max_distance]

    # Open output file.
    xmldoc = ligolw.Document()
    xmlroot = xmldoc.appendChild(ligolw.LIGO_LW())

    # Create tables. Process and ProcessParams tables are copied from the
    # injection file.
    coinc_def_table = xmlroot.appendChild(New(CoincDefTable))
    coinc_inspiral_table = xmlroot.appendChild(New(CoincInspiralTable))
    coinc_map_table = xmlroot.appendChild(New(CoincMapTable))
    coinc_table = xmlroot.appendChild(New(CoincTable))
    xmlroot.appendChild(ProcessParamsTable.get_table(inj_xmldoc))
    xmlroot.appendChild(ProcessTable.get_table(inj_xmldoc))
    sim_inspiral_table = xmlroot.appendChild(New(SimInspiralTable))
    sngl_inspiral_table = xmlroot.appendChild(New(SnglInspiralTable))
    time_slide_table = xmlroot.appendChild(New(TimeSlideTable))

    # Write process metadata to output file.
    process = register_to_xmldoc(
        xmldoc, p, opts, ifos=opts.detector, comment="Simulated coincidences")

    # Add search summary to output file.
    all_time = segments.segment(
        [glue.lal.LIGOTimeGPS(0), glue.lal.LIGOTimeGPS(2e9)])
    append_search_summary(xmldoc, process, inseg=all_time, outseg=all_time)

    # Create a time slide entry.  Needed for coinc_event rows.
    time_slide_id = time_slide_table.get_time_slide_id(
        {ifo: 0 for ifo in opts.detector}, create_new=process)

    # Populate CoincDef table.
    inspiral_coinc_def = copy.copy(InspiralCoincDef)
    inspiral_coinc_def.coinc_def_id = coinc_def_table.get_next_id()
    coinc_def_table.append(inspiral_coinc_def)
    found_coinc_def = copy.copy(InspiralSCExactCoincDef)
    found_coinc_def.coinc_def_id = coinc_def_table.get_next_id()
    coinc_def_table.append(found_coinc_def)

    # Precompute values that are common to all simulations.
    detectors = [lalsimulation.DetectorPrefixToLALDetector(ifo)
                 for ifo in opts.detector]
    responses = [det.response for det in detectors]
    locations = [det.location for det in detectors]

    if opts.jobs == 1:
        pool_map = map
    else:
        from .. import omp
        from multiprocessing import Pool
        omp.num_threads = 1  # disable OpenMP parallelism
        pool_map = Pool(opts.jobs).imap

    func = functools.partial(simulate, psds=psds,
                             responses=responses, locations=locations,
                             measurement_error=opts.measurement_error,
                             f_low=opts.f_low, waveform=opts.waveform)

    # Make sure that each thread gets a different random number state.
    # We start by drawing a random integer s in the main thread, and
    # then the i'th subprocess will seed itself with the integer i + s.
    #
    # The seed must be an unsigned 32-bit integer, so if there are n
    # threads, then s must be drawn from the interval [0, 2**32 - n).
    #
    # Note that *we* are thread 0, so there are a total of
    # n=1+len(sim_inspiral_table) threads.
    seed = np.random.randint(0, 2 ** 32 - len(sim_inspiral_table) - 1)
    np.random.seed(seed)

    count_coincs = 0

    with tqdm(total=len(orig_sim_inspiral_table)) as progress:
        for sim_inspiral, simulation in zip(
                orig_sim_inspiral_table,
                pool_map(func, zip(np.arange(len(orig_sim_inspiral_table))
                                   + seed + 1,
                                   orig_sim_inspiral_table))):
            progress.update()

            sngl_inspirals = []
            used_snr_series = []
            net_snr = 0.0
            count_triggers = 0

            # Loop over individual detectors and create SnglInspiral entries.
            for ifo, (horizon, abs_snr, arg_snr, toa, series) \
                    in zip(opts.detector, simulation):

                if np.random.uniform() > opts.duty_cycle:
                    continue
                elif abs_snr >= opts.snr_threshold:
                    # If SNR < threshold, then the injection is not found.
                    # Skip it.
                    count_triggers += 1
                    net_snr += np.square(abs_snr)
                elif not opts.keep_subthreshold:
                    continue

                # Create SnglInspiral entry.
                used_snr_series.append(series)
                sngl_inspirals.append(
                    sngl_inspiral_table.RowType(**dict(
                        dict.fromkeys(sngl_inspiral_table.validcolumns, None),
                        process_id=process.process_id,
                        ifo=ifo,
                        mass1=sim_inspiral.mass1,
                        mass2=sim_inspiral.mass2,
                        spin1x=sim_inspiral.spin1x,
                        spin1y=sim_inspiral.spin1y,
                        spin1z=sim_inspiral.spin1z,
                        spin2x=sim_inspiral.spin2x,
                        spin2y=sim_inspiral.spin2y,
                        spin2z=sim_inspiral.spin2z,
                        end=toa,
                        snr=abs_snr,
                        coa_phase=arg_snr,
                        eff_distance=horizon / abs_snr)))

            net_snr = np.sqrt(net_snr)

            # If too few triggers were found, then skip this event.
            if count_triggers < opts.min_triggers:
                continue

            # If network SNR < threshold, then the injection is not found.
            # Skip it.
            if net_snr < opts.net_snr_threshold:
                continue

            # Add Coinc table entry.
            coinc = coinc_table.appendRow(
                coinc_event_id=coinc_table.get_next_id(),
                process_id=process.process_id,
                coinc_def_id=inspiral_coinc_def.coinc_def_id,
                time_slide_id=time_slide_id,
                insts=opts.detector,
                nevents=len(opts.detector),
                likelihood=None)

            # Add CoincInspiral table entry.
            coinc_inspiral_table.appendRow(
                coinc_event_id=coinc.coinc_event_id,
                instruments=[
                    sngl_inspiral.ifo for sngl_inspiral in sngl_inspirals],
                end=lal.LIGOTimeGPS(1e-9 * np.mean([
                    sngl_inspiral.end.ns()
                    for sngl_inspiral in sngl_inspirals
                    if sngl_inspiral.end is not None])),
                mass=sim_inspiral.mass1 + sim_inspiral.mass2,
                mchirp=sim_inspiral.mchirp,
                combined_far=0.0,  # Not provided
                false_alarm_rate=0.0,  # Not provided
                minimum_duration=None,  # Not provided
                snr=net_snr)

            # Record all sngl_inspiral records and associate them with coincs.
            for sngl_inspiral, series in zip(sngl_inspirals, used_snr_series):
                # Give this sngl_inspiral record an id and add it to the table.
                sngl_inspiral.event_id = sngl_inspiral_table.get_next_id()
                sngl_inspiral_table.append(sngl_inspiral)

                if opts.enable_snr_series:
                    elem = lal.series.build_COMPLEX8TimeSeries(series)
                    elem.appendChild(
                        Param.from_pyvalue(u'event_id',
                                           sngl_inspiral.event_id))
                    xmlroot.appendChild(elem)

                # Add CoincMap entry.
                coinc_map_table.appendRow(
                    coinc_event_id=coinc.coinc_event_id,
                    table_name=sngl_inspiral_table.tableName,
                    event_id=sngl_inspiral.event_id)

            # Record injection
            if not opts.preserve_ids:
                sim_inspiral.simulation_id = sim_inspiral_table.get_next_id()
            sim_inspiral_table.append(sim_inspiral)

            count_coincs += 1
            progress.set_postfix(saved=count_coincs)

    # Record coincidence associating injections with events.
    for i, sim_inspiral in enumerate(sim_inspiral_table):
        coinc = coinc_table.appendRow(
            coinc_event_id=coinc_table.get_next_id(),
            process_id=process.process_id,
            coinc_def_id=found_coinc_def.coinc_def_id,
            time_slide_id=time_slide_id,
            instruments=None,
            nevents=None,
            likelihood=None)
        coinc_map_table.appendRow(
            coinc_event_id=coinc.coinc_event_id,
            table_name=sim_inspiral_table.tableName,
            event_id=sim_inspiral.simulation_id)
        coinc_map_table.appendRow(
            coinc_event_id=coinc.coinc_event_id,
            table_name=coinc_table.tableName,
            event_id=CoincID(i))

    # Record process end time.
    ligolw_process.set_process_end_time(process)

    # Write output file.
    with ligolw_utils.SignalsTrap():
        ligolw_utils.write_fileobj(
            xmldoc, opts.output,
            gz=(os.path.splitext(opts.output.name)[-1] == ".gz"))