#!/usr/bin/python

__author__ = "Stephen Fairhurst <sfairhur@gravity.phys.uwm.edu>"
__prog__="plotthinca"

import sys
import os
from optparse import *
import re
import exceptions
import glob
from types import *

from glue import lal
from glue import segments
from glue import segmentsUtils
from glue.ligolw import ligolw
from glue.ligolw import table
from glue.ligolw import lsctables
from glue.ligolw import utils
import glue.iterutils
from pylal import CoincInspiralUtils
from pylal import SnglInspiralUtils
from pylal import InspiralUtils
from pylal import git_version
from numpy import histogram

from glue.ligolw import ligolw, table as tab, lsctables, utils

from pylal import SnglInspiralUtils, SimInspiralUtils
from pylal.tools import XLALEThincaParameterForInjection as ethinca_param


##############################################################################
usage = """
usage: %prog [options] 

Thinca Triggers Plotting Function

Generate a set of summary plots from a list of thinca files.  First, specify
which ifos the triggers come from using:
--g1-triggers, --h1-triggers, --h2-triggers, --l1-triggers.


The plots which can be generated are:

1)  A plot of the snr of coincident triggers (for each ifo) vs time.
    Generated by specifying: --snr-time

2)  Plots of snr vs snr for various ifo combinations.
    Generated by specifying: --snr-snr
    Note: specifying --snr-chi causes these to be plotted for snr/chi

These plots can be made with time slide triggers by specifying: 
  --slide-snr

3)  Plots of effective distance vs effective distance for various ifo comb.
    Generated by specifying: --dist-dist

These plots can be made with time slide triggers by specifying: 
  --slide-dist
  
4)  For each combination of two or more ifos, plot the number of triggers per 
    time slide.  Generated by specifying: --plot-slides

5)  For each combination of two or more ifos, make a histogram of the number 
    of triggers per time slide.  Generated by specifying: --hist-slides

The zero lag number of triggers can be added to the above by specifying:
  --add-zero-lag

If the zero lag data only contains playground, the number of triggers in the
  background can be scaled by a factor or 600/6370 by specifying
  --zero-lag-playground

6)  For each combination of two or more ifos, make a cumulative histogram of
    the number of events as a function of the combined snr.
    Generated by specifying: --snr-dist
"""


def parse_command_line():
  """
  Parser function dedicated
  """
  parser = OptionParser(usage=usage, version=git_version.verbose_msg)

  # options related to input and output
  parser.add_option("-g","--glob",action="store",type="string",\
      default=None, metavar=" GLOB",help="GLOB of thinca files to read" )
  parser.add_option("-I", "--cache-file", \
      help="read thinca filenames from cache input file")  
  parser.add_option("-o","--off-source-glob",action="store",type="string",\
      default=None, metavar=" GLOB", \
      help="GLOB off-source thinca files to read" )
  parser.add_option("-V","--veto-file",action="store",type="string",\
      default=None,metavar=" FNAME",\
      help="discard triggers in segments from these comma-seperated files"\
                    " (segwizard format).")
  parser.add_option("-O","--enable-output",action="store_true",\
      default="false",  metavar="OUTPUT",\
      help="enable the generation of the html and cache documents")
  parser.add_option("-u","--user-tag",action="store",type="string",\
      default=None, metavar=" USERTAG",\
      help="The user tag used in the name of the figures" )
  parser.add_option("","--ifo-tag",action="store",type="string",\
      default=None, metavar=" IFOTAG",\
      help="The ifo tag used in the name of the figures (e.g. SECOND_H1H2L1)")      
  parser.add_option("","--gps-start-time",action="store", type="int", \
      metavar="GPSSTARTTIME",\
      help="gps start time used in the figure and output file names")
  parser.add_option("","--gps-end-time",action="store", type= "int", \
      metavar="GPSENDTIME",\
      help="gps end time used in the figure and output file names")
  parser.add_option("-P","--output-path",action="store",\
      type="string",default="",  metavar="PATH",\
      help="path where the figures would be stored")
  parser.add_option("","--ifo-times",action="store",type="string",\
      default=None,metavar="IFOS",\
      help="sets ifo times for which plots will be made (e.g. H1H2L1)" )
  parser.add_option("-s","--show-plot",action="store_true",default=False,\
      help="display the figures on the terminal" )
  parser.add_option("-v","--verbose",action="store_true",\
      default=False,help="print information" )

  # options needed for exttrig analysis
  parser.add_option("-E","--ext-trig",action="store_true",default=False,\
      help="external trigger search, so should have off-source triggers" )
  parser.add_option("","--offsource-veto-file",action="store",type="string",\
      default=None,metavar=" FNAME",\
      help="discard off-source triggers in segments from FNAME (segwizard format)")
  parser.add_option("","--padding-time",action="store",type="int",\
      metavar=" PADDING_TIME",default=-1, \
      help="setting the padding time (for exttrig)" )
  parser.add_option("","--segment-file",action="store",type="string",\
      metavar=" SEGMENT_FILE",default=None, \
      help="setting the segment file (for exttrig)" )

  # options used in sieving the cache file, in case it is given    
  parser.add_option("","--coinc-pattern",
    help="sieve the cache for zero-lag coincidence files with this pattern")
  parser.add_option("","--slide-pattern",
    help="sieve the cache for time-slide files with this pattern")
  parser.add_option("","--match", action="store_true", default=False,
      help="Apply sieve patterns exactly (no leading and trailing *s)")

  #options that set parameters specific to the plots 

  parser.add_option("-L","--cluster-window",action="store",type="int",\
      default=0,\
      metavar=" SEC", help="length of time over which to cluster triggers" )
  parser.add_option("-x","--min-snr",action="store",type="float",\
      default=0, metavar=" MIN_SNR",help="minimum value of snr on plot" )
  parser.add_option("-X","--max-snr",action="store",type="float",\
      default=0, metavar=" MAX_SNR",help="maximum value of snr on plot" )
  parser.add_option("-A","--g1-triggers",action="store_true",default=False,\
      help="input files contain triggers from G1" )
  parser.add_option("-B","--h1-triggers",action="store_true",default=False,\
      help="input files contain triggers from H1" )
  parser.add_option("-C","--h2-triggers",action="store_true",default=False,\
      help="input files contain triggers from H2" )
  parser.add_option("-D","--l1-triggers",action="store_true",default=False,\
      help="input files contain triggers from L1" )
  parser.add_option("-U","--v1-triggers",action="store_true",default=False,\
      help="input files contain triggers from V1" )
  parser.add_option("","--remove-h1h2",action="store_true",default=False,\
      help="remove H1H2 triggers (e.g. when H1 and H2 are slid together)" )
  parser.add_option("-a","--snr-time",action="store_true",default=False,\
      help="plot the snr vs time of coinc triggers" )
  parser.add_option("-b","--snr-snr",action="store_true",default=False,\
      help="make snr vs snr plots of coinc triggers" )
  parser.add_option("","--snr-cube",action="store_true",default=False,\
      help="make snr vs snr vs snr plots of coinc triggers, if possible" )
  parser.add_option("","--parcoord-var",action="store",type="string",default=None,\
      metavar="VAR_LIST",help="make parallel coordinates plot of variables described by the "\
           "VAR_LIST string. This string is of the form \"xxx yyy zzz\" "\
	   "where xxx, yyy, zzz are variables declared in the sngl_inspiral tables. "\
	   "There will be as many single variables (for example snr) as there are "\
	   "selected interferometers and the interferometer name will be appended "\
	   "to the variable name in the par. coord. plot.")
  parser.add_option("-S","--statistic",action="store",default='snr',\
      type="string",\
      help="choice of statistic used in making plots, valid arguments are: "
            "snr (DEFAULT), snr_over_chi, effective_snr, new_snr, " 
            "bitten_l, bitten_lsq") 
  parser.add_option("","--eff-snr-denom-fac",action="store",default=250.0,\
      type="float", help="Effective snr denom fac: default = 250")
  parser.add_option("","--chisq-index",action="store",default=6.,\
      type="float", help="New snr chisq index: default = 6")
  parser.add_option("-F","--bittenl_a",action="store",type="float",\
      default=None,\
      metavar=" BITTENL_A", help="parameter a for bitten-l statistic" ) 	 
  parser.add_option("-G","--bittenl_b",action="store",type="float",\
      default=None,\
      metavar=" BITTENL_B", help="parameter b for bitten-l statistic" )
  parser.add_option("-d","--slide-snr",action="store_true",default=False,\
    help="make snr vs snr plots of time slide triggers" )
  parser.add_option("-e","--dist-dist",action="store_true",default=False,\
      help="make dist vs dist plots of coinc triggers" )
  parser.add_option("-l","--slide-dist",action="store_true",default=False,\
      help="make dist vs dist plots of time slide triggers" )
  parser.add_option("-i","--hist-slides",action="store_true",default=False,\
      help="plot histograms of number of triggers in time slides")
  parser.add_option("-j","--plot-slides",action="store_true",default=False,\
      help="plot of number of triggers vs slide number")
  parser.add_option("-z","--add-zero-lag",action="store_true",default=False,\
      help="add the zero lag info to slide plots")
  parser.add_option("-Z","--zero-lag-playground",action="store_true",\
      default=False,\
      help="scale number of bkg triggers in slide plots by 600/6370")
  parser.add_option("-k","--snr-dist",action="store_true",default=False,\
      help="plot distribution of combined statistic" )
  parser.add_option("-K","--snr-hist",action="store_true",default=False,\
      help="plot histogram of combined statistic" )
  parser.add_option("-m","--mass-dependent",action="store_true",default=False,\
      help="sort triggers by mass range" )
  parser.add_option("-M","--mass-range",action="store",type="string",\
      metavar=" MASS_RANGE",default=None,help="sort triggers by mass range" )
  parser.add_option("-n","--nbins",action="store",type="int",default=20,\
      metavar=" NBINS", help="number of bins for the histogram plots" )
  parser.add_option("-N","--num-slides",action="store",type="int",default=0,\
      metavar=" NUM_SLIDES",help="number of time slides performed" )
  parser.add_option("-t","--plot-type",action="store",type="string",\
      default="linear",metavar=" PLOT_TYPE", \
      help="make either linear or log plots" )

  (options,args) = parser.parse_args()


  return options, sys.argv[1:]


# ============================================================================
# -- get command line arguments
opts, args = parse_command_line()



if opts.plot_type not in ['log', 'linear']:
  print >>sys.stderr, "plot-type must be either \"log\" or \"linear\""
  sys.exit(1)

if not opts.glob and not opts.cache_file:
  print >>sys.stderr, \
      "Must specify a --glob of files or --cache-input file to read"
  print >>sys.stderr, "Enter 'plotthinca --help' for usage"
  sys.exit(1)

if not opts.off_source_glob and opts.ext_trig:
  print >>sys.stderr, "Must specify a off source GLOB of files to read"
  print >>sys.stderr, "Enter 'plotthinca --help' for usage"
  sys.exit(1)

# if doing any slide plots:
if opts.hist_slides or opts.slide_snr:
  if not opts.num_slides:
    print >>sys.stderr, "--num-slides must be specified if"
    print >>sys.stderr, "--hist-slides or --slide-snr are"
    sys.exit(1)

# check that statistic is OK:
if (opts.statistic != 'snr') and (opts.statistic != 'snr_over_chi') \
    and (opts.statistic != 'effective_snr')\
    and (opts.statistic != 'new_snr')\
    and (opts.statistic != 'bitten_lsq')\
    and (opts.statistic != 'bitten_l'):
  print >>sys.stderr, "--statistic must be one of"
  print >>sys.stderr, "(snr|snr_over_chi|effective_snr|new_snr|bitten_l)"
  sys.exit(1)

# check if bittenl-parameters has been specifiedv (or bitten_lsq), set snglStat 	 
snglStat=opts.statistic 	 
if 'bitten_l' in opts.statistic:
   snglStat='snr' 	 
   if (not opts.bittenl_a or not opts.bittenl_b): 	 
     print >>sys.stderr, "--bittenl_a and --bittenl_b must be specified" 	 
     sys.exit(1)
#####################################################################
# -- some initialisation
opts = InspiralUtils.initialise(opts, __prog__, git_version.verbose_msg)
# -- set the proper color code
colors = InspiralUtils.colors
figure_number = 0  # used for the figure label (showplot)
fnameList = []   # use for the cache file
tagList= []   # use for the cache file


# Change to Agg back-end if show() will not be called 
# thus avoiding display problem
if not opts.show_plot:
  import matplotlib
  matplotlib.use('Agg')
from pylab import *
from pylal import viz
from numpy import histogram
rc('text', usetex=True)

####################################################################

statistic = CoincInspiralUtils.coincStatistic( opts.statistic, opts.bittenl_a,\
     opts.bittenl_b, opts.eff_snr_denom_fac, opts.chisq_index)
    
plotsymbols = ['r+','bx','ko','g1']
hanfordsym = ['b+','bx','r1']
addsym = ['k+','kx','k1']

xlow = 20
xhigh = 0

#######################################################
# determine IFOs and IFO combos in play

ifo_list = [ifo for ifo in ("G1", "H1", "H2", "L1", "V1") \
            if getattr(opts, "%s_triggers" % ifo.lower())]
ifo_non_h = [ifo for ifo in ifo_list if ifo[0] != "H"]

ifo_coincs = []
for num_ifos in range(2, len(ifo_list) + 1):
  ifo_coincs.extend(list(glue.iterutils.choices(ifo_list, num_ifos)))

#######################################################
# turn the mass range from a string to a list of floats

if opts.mass_range is not None:
  mass_range = [float(s) for s in opts.mass_range.split(",") if s != ""]

###################################
# glob the list of files to read in

slidefiles = []
coincfiles = []
offsourcefiles = []

if opts.glob is not None or opts.cache_file is not None:
  if opts.glob is not None:
    allfiles = []
    for gl in opts.glob.split(" "):
      allfiles.extend(glob.glob(gl))
    if len(allfiles) < 1:
      print >>sys.stderr, "The glob for " + opts.glob + " returned no files" 
      sys.exit(1)

    for file in allfiles:
      if 'SLIDE' in file:
        slidefiles.append(file)
      else:
        coincfiles.append(file)

  elif opts.cache_file is not None:
    allfilesCache = lal.Cache.fromfile(open(opts.cache_file))    

    if opts.coinc_pattern is not None:
        sieved_cache = allfilesCache.sieve(description=opts.coinc_pattern,
                                           exact_match=opts.match)
        if opts.ifo_times is not None:
          sieved_cache = sieved_cache.sieve(ifos=opts.ifo_times,
                                            exact_match=True)
        found, missed = sieved_cache.checkfilesexist()
        coincfiles = found.pfnlist()
    if opts.slide_pattern is not None:
        sieved_cache = allfilesCache.sieve(description=opts.slide_pattern,
                                           exact_match=opts.match)
        if opts.ifo_times is not None:
          sieved_cache = sieved_cache.sieve(ifos=opts.ifo_times,
                                            exact_match=True)
        found, missed = sieved_cache.checkfilesexist()
        slidefiles = found.pfnlist()
  
  # check if the file lists are not empty
  if not (coincfiles or slidefiles):
    print >>sys.stdout, "No files match your description."
    sys.exit(0) 
         
# Off Source Files
if opts.off_source_glob:  
  allfiles = []
  for gl in opts.off_source_glob.split(" "):
    allfiles.extend(glob.glob(gl))

  if len(allfiles) == 0:
    print >>sys.stderr, "The glob for " + opts.off_source_glob + \
        " returned no files"
    sys.exit(1)

  for file in allfiles: 
    offsourcefiles.append(file)


# there are no time-slides in the exttrig analysis
if opts.ext_trig:
  slidefiles=[]

  # In the ext-trig search the coincfiles correspond to zero-lag data
  if not opts.add_zero_lag:
    coincfiles = []

########################
# read in coinc triggers
inspTriggers = None
coincTriggers = CoincInspiralUtils.coincInspiralTable()

if coincfiles:
  inspTriggers = SnglInspiralUtils.\
           ReadSnglInspiralFromFiles(coincfiles, verbose=opts.verbose)
  
  # perform the veto
  if opts.veto_file:
    for veto_file in opts.veto_file.split(','):
      seglist = segmentsUtils.fromsegwizard(open(veto_file, "r"))
      inspTriggers = inspTriggers.veto(seglist)
  
  # construct the coincs
  coincTriggers = CoincInspiralUtils.coincInspiralTable(inspTriggers,statistic)

  # remove H1H2 triggers
  if opts.remove_h1h2:
    coincTriggers = coincTriggers.removecoinctype(['H1', 'H2'])

  # cluster them
  if opts.cluster_window:
    coincTriggers = coincTriggers.cluster(opts.cluster_window)

  if not opts.max_snr:
    if inspTriggers:
      xhigh = max( inspTriggers.get_column(snglStat) )
    else:
      xhigh = 50.0
  else:
    xhigh = opts.max_snr + 1

  if not opts.min_snr:
    if inspTriggers:
      xlow = min( inspTriggers.get_column(snglStat) )
    else:
      xlow = 1.0
  else:
    xlow = opts.min_snr

if opts.mass_dependent:
  coincTriggersByMass = []
  for i in range(len(mass_range)-1):
    if len(coincTriggers) > 0:
      coincTriggersByMass.append( \
        coincTriggers.getChirpMass(mass_range[i],mass_range[i+1]) )
    else:
      coincTriggersByMass.append( coincTriggers )


########################
# read in off-source triggers
offsourceTriggers = None

# need to specify this variable here,
# since it might be filled with slides OR offsource triggers
slideTriggers = []  

if offsourcefiles:
  # need: segmentfile as argument, need padding as argument
  #seglist = segmentsUtils.fromsegwizard(open('segments.txt'))
  seglist = segmentsUtils.fromsegwizard(open(opts.segment_file))
  offSourcePadding = opts.padding_time
  offSourceSegLength = opts.gps_end_time - opts.gps_start_time
  offSourceSegLeft = (opts.gps_start_time -offSourcePadding - seglist[0][0])\
      // offSourceSegLength
  offSourceSegRight = (seglist[0][1] - opts.gps_end_time - offSourcePadding)\
      // offSourceSegLength

  # read the off-source triggers
  inspTriggers = SnglInspiralUtils.\
           ReadSnglInspiralFromFiles(offsourcefiles, verbose=opts.verbose)

  # perform the veto
  if opts.offsource_veto_file:
    seglist = segmentsUtils.fromsegwizard(open(opts.offsource_veto_file, "r"))
    inspTriggers = inspTriggers.veto(seglist)
  elif opts.veto_file:
    for veto_file in opts.veto_file.split(','):
      seglist = segmentsUtils.fromsegwizard(open(veto_file, "r"))
      inspTriggers = inspTriggers.veto(seglist)

  # now create
  for segnr in range(-offSourceSegLeft, offSourceSegRight):
    # skip the onsource segment
    if segnr==0:
      continue

    # calculate the start- and end-time of the actual segment
    t0 = opts.gps_start_time+segnr*offSourceSegLength
    t1 = t0+offSourceSegLength

    # prepare a new segment
    this_seg = {}
    this_seg["slide_num"]=segnr
    this_seg["sngl_trigs"] = [trig for trig in inspTriggers \
        if trig.end_time>t0 and trig.end_time<=t1]
    this_seg["coinc_trigs"] = CoincInspiralUtils.\
        coincInspiralTable(this_seg["sngl_trigs"],statistic)

    # remove H1H2 triggers
    if opts.remove_h1h2:
      this_seg["coinc_trigs"] = this_seg["coinc_trigs"].\
          removecoinctype(['H1', 'H2'])

    # cluster triggers if required
    if opts.cluster_window:
      this_seg["coinc_trigs"] = \
        this_seg["coinc_trigs"].cluster(opts.cluster_window)


    # append this segment to the whole collection
    slideTriggers.append(this_seg)
  
  if inspTriggers:
    slide_high = max( inspTriggers.get_column(snglStat) )
    slide_low = min( inspTriggers.get_column(snglStat) )
  else:
    slide_high = 50.0
    slide_low = 1.0


########################
# read in slide triggers
if slidefiles:
  inspSlide = SnglInspiralUtils.\
           ReadSnglInspiralFromFiles(slidefiles, verbose=opts.verbose)
  
  # if there are no triggers, skip remaining slide stuff
#  slidefiles = []
#  inspSlide = []
  
  # perform the veto
  if opts.veto_file:      
    for veto_file in opts.veto_file.split(','):    
      seglist = segmentsUtils.fromsegwizard(open(veto_file, "r"))
      inspSlide = inspSlide.veto(seglist)
      
  slide_num = range(1 , opts.num_slides + 1)
  slide_num.extend(range(-opts.num_slides, 0))

  for slide in slide_num:
    this_slide = {}
    this_slide["slide_num"] = slide
    try:
      this_slide["sngl_trigs"] = inspSlide.getslide(slide)
    except:
      this_slide["sngl_trigs"] = []

    # make coincs
    this_slide["coinc_trigs"] = \
      CoincInspiralUtils.coincInspiralTable(this_slide["sngl_trigs"],statistic)

    # remove H1H2 triggers
    if opts.remove_h1h2:
      this_slide["coinc_trigs"] = \
          this_slide["coinc_trigs"].removecoinctype(['H1', 'H2'])

    # cluster triggers
    if opts.cluster_window:
      this_slide["coinc_trigs"] = \
        this_slide["coinc_trigs"].cluster(opts.cluster_window)  
    
    # add slide to list
    slideTriggers.append(this_slide)
    if opts.verbose:
      print (str(slide) + ' ' + str(len(this_slide["sngl_trigs"])))
  
  if inspSlide:
    slide_high = max( inspSlide.get_column(snglStat) )
    slide_low = min( inspSlide.get_column(snglStat) )
  else:
    slide_high = 50.0
    slide_low = 1.0

if opts.mass_dependent:
  slideTriggersByMass = []
  for i in range(len(mass_range)-1):
    slideTriggersByMass.append( [] )
    for j in range(len(slideTriggers)):
      tmp_this_slide = {}
      tmp_this_slide["slide_num"] = slideTriggers[j]["slide_num"]
      if slideTriggers[j]["coinc_trigs"]:
        tmp_this_slide["coinc_trigs"] = slideTriggers[j]["coinc_trigs"].\
            getChirpMass(mass_range[i],mass_range[i+1])
      else:
        tmp_this_slide["coinc_trigs"] = slideTriggers[j]["coinc_trigs"]
      slideTriggersByMass[i].append( tmp_this_slide )

################################
# plot of h1 vs h2 stat

if opts.snr_snr or opts.slide_snr:
  if 'H1' in ifo_list and 'H2' in ifo_list:
    # create a text for the alt and title of html document
    text=" Plot of H1 vs H2 " + snglStat.replace("_"," ") + " statistic"
    figure(figure_number)
    figure_number += 1
    hold(True)
    if opts.slide_snr:
      for slide in slideTriggers:
        if len(slide["coinc_trigs"]) > 0:
          viz.plotcoincval(slide["coinc_trigs"], snglStat, 'H1', 'H2', \
          'k+',opts.plot_type)
    if opts.ext_trig and len(offsourceTriggers) > 0:
      viz.plotcoincval(offsourceTriggers, snglStat, 'H1', 'H2', \
          'bo',opts.plot_type)
      # The color in 'bo' only applies to markerfacecolor, which plotcoincval
      # removes.  Must set the markeredgecolor on the last dataset in the
      # current axes.
      gca().get_lines()[-1].set_markeredgecolor('b')
    if opts.snr_snr and len(coincTriggers) > 0:
      viz.plotcoincval(coincTriggers, snglStat, 'H1', 'H2', \
          'r+',opts.plot_type)
      # The color in 'r+' only applies to markerfacecolor, which plotcoincval
      # removes.  Must set the markeredgecolor on the last dataset in the
      # current axes.
      gca().get_lines()[-1].set_markeredgecolor('r')


    xlabel(snglStat.replace("_"," ") + ' H1', size='x-large')
    ylabel(snglStat.replace("_"," ") + ' H2', size='x-large')
    title("H1 vs H2 coincident event " + snglStat.replace("_"," ") + \
        " statistic", size='x-large')
    grid(True)
    if opts.min_snr:
      xlim(xmin=opts.min_snr)
      ylim(ymin=opts.min_snr)
    if opts.max_snr:
      xlim(xmax=opts.max_snr)
      ylim(ymax=opts.max_snr)
    else:
      viz.square_axis()
    if opts.enable_output is True:
      fname = InspiralUtils.set_figure_name(opts, "H1_vs_H2_"+snglStat)
      fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
      fnameList.append(fname)
      tagList.append(text)
    if not opts.show_plot:
      close()



if (opts.snr_snr or opts.slide_snr) and opts.mass_dependent:
  for i in range(len(mass_range)-1):
    if 'H1' in ifo_list and 'H2' in ifo_list:
      figure(figure_number)
      figure_number += 1
      hold(True)
      if opts.slide_snr:
        for slide in slideTriggersByMass[i]:
          if len(slide["coinc_trigs"]) >0:
            viz.plotcoincval(slide["coinc_trigs"], snglStat, 'H1', 'H2', \
            'k+',opts.plot_type)
      if opts.snr_snr and len(coincTriggersByMass[i]) > 0:
        viz.plotcoincval(coincTriggersByMass[i], snglStat, 'H1', 'H2', \
            'r+',opts.plot_type)
        # The color in 'r+' only applies to markerfacecolor, which plotcoincval
        # removes.  Must set the markeredgecolor on the last dataset in the
        # current axes.
        gca().get_lines()[-1].set_markeredgecolor('r')

      xlabel(snglStat.replace("_"," ") + ' H1', size='x-large')
      ylabel(snglStat.replace("_"," ") + ' H2', size='x-large')
      title("H1 vs H2 coincident event " + snglStat.replace("_"," ") + \
          " for " + str(mass_range[i]) + str(mass_range[i+1]) +  \
          " mass range", size='x-large')
      grid(True)
      if opts.min_snr:
        xlim(xmin=opts.min_snr)
        ylim(ymin=opts.min_snr)
      if opts.max_snr:
        xlim(xmax=opts.max_snr)
        ylim(ymax=opts.max_snr)
      else:
        viz.square_axis()
      if opts.enable_output is True:
        text = "Plot of H1 vs H2 " + snglStat.replace("_"," ") + " for " + \
            str(mass_range[i]) + str(mass_range[i+1]) +  " mass range"
        name = "H1_vs_H2_"+snglStat+"_"+str(mass_range[i]) + "_" + \
            str(mass_range[i+1]) 
        fname = InspiralUtils.set_figure_name(opts, name)
        fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
        fnameList.append(fname)
        tagList.append(text)
      if not opts.show_plot:
        close()

 

################################
# plot of hanford vs other snr

if opts.snr_snr or opts.slide_snr:
  if 'H1' in ifo_list or 'H2' in ifo_list:
    for ifo in ifo_non_h:
      figure(figure_number)
      figure_number += 1
      hold(True)
      if opts.slide_snr:
        for slide in slideTriggers:
          if ('H1' in ifo_list) and (len(slide["coinc_trigs"]) > 0):
            viz.plotcoincval(slide["coinc_trigs"], snglStat, 'H1', ifo, \
              'kx', opts.plot_type)
          if ('H2' in ifo_list) and (len(slide["coinc_trigs"]) > 0):
            viz.plotcoincval(slide["coinc_trigs"], snglStat, 'H2', ifo, \
              'k+', opts.plot_type)
          if ('H1' in ifo_list) and ('H2' in ifo_list) and \
              (len(slide["coinc_trigs"]) > 0):
            viz.plotcoinchanford(slide["coinc_trigs"], snglStat, ifo, \
              'sqrtsqr', 'k1', opts.plot_type)
      
      if opts.snr_snr:
        sym = 0
        legend_text = []
        if ('H1' in ifo_list) and (len(coincTriggers) > 0):
          viz.plotcoincval(coincTriggers, snglStat, 'H1', ifo, \
              hanfordsym[sym], opts.plot_type)
          legend_text.append('H1' + ifo)
          for add_ifo in ifo_non_h:
            if add_ifo != ifo:
              viz.plotcoincval(coincTriggers, snglStat, 'H1', ifo, \
                  addsym[sym], opts.plot_type, add_ifo)
              legend_text.append('H1' + ifo + '(also in ' + add_ifo + ')')
              
        if not opts.slide_snr: sym += 1
        if ('H2' in ifo_list) and (len(coincTriggers) > 0):
          viz.plotcoincval(coincTriggers, snglStat, 'H2', ifo, \
              hanfordsym[sym], opts.plot_type)
          legend_text.append('H2' + ifo)
          for add_ifo in ifo_non_h:
            if add_ifo != ifo:
              viz.plotcoincval(coincTriggers, snglStat, 'H2', ifo, \
                  addsym[sym], opts.plot_type, add_ifo)
              legend_text.append('H2' + ifo + '(also in ' + add_ifo + ')')
              
        if not opts.slide_snr: sym += 1
        if ('H1' in ifo_list) and ('H2' in ifo_list) and (len(coincTriggers) > 0):       
          viz.plotcoinchanford(coincTriggers, snglStat, ifo, 'sqrtsqr', \
              hanfordsym[sym], opts.plot_type)
          legend_text.append('H1H2' + ifo)
          for add_ifo in ifo_non_h:
            if add_ifo != ifo:
              viz.plotcoinchanford(coincTriggers, snglStat, ifo, \
                  'sqrtsqr', addsym[sym],opts.plot_type, add_ifo)
              legend_text.append('H1H2' + ifo + '(also in ' + add_ifo + ')')


      if not opts.slide_snr and len(legend_text)>0:
        legend(legend_text,2)
      xlabel(snglStat.replace("_"," ") + ' Hanford', size='x-large')
      ylabel(snglStat.replace("_"," ") + ' '+ ifo, size='x-large')
      title("Hanford vs " + ifo + " " + snglStat.replace("_"," ") + \
          " statistic", size='x-large')
      grid(True)
      if opts.min_snr:
        xlim(xmin=opts.min_snr)
        ylim(ymin=opts.min_snr)
      if opts.max_snr:
        xlim(xmax=opts.max_snr)
        ylim(ymax=opts.max_snr)
      else:
        viz.square_axis()
      if opts.enable_output is True:
        name =  "H_vs_" + ifo +"_" +snglStat 
        text = "Plot of Hanford vs " + ifo + " " + snglStat.replace("_"," ") \
            + " statistic" 
        fname = InspiralUtils.set_figure_name(opts, name)
        fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
        fnameList.append(fname)
        tagList.append(text)
      if not opts.show_plot:
        close()
 

if (opts.snr_snr or opts.slide_snr) and opts.mass_dependent:
  for i in range(len(mass_range)-1):
    if 'H1' in ifo_list or 'H2' in ifo_list:
      for ifo in ifo_non_h:
        figure(figure_number)
        figure_number += 1
        hold(True)
        if opts.slide_snr:
          for slide in slideTriggersByMass[i]:
            if ('H1' in ifo_list) and (len(slide["coinc_trigs"]) > 0):
              viz.plotcoincval(slide["coinc_trigs"], snglStat, 'H1', ifo, \
                'kx', opts.plot_type)
            if ('H2' in ifo_list) and (len(slide["coinc_trigs"]) > 0):
              viz.plotcoincval(slide["coinc_trigs"], snglStat, 'H2', ifo, \
                'k+', opts.plot_type)
            if ('H1' in ifo_list) and ('H2' in ifo_list) and \
                (len(slide["coinc_trigs"]) > 0):
              viz.plotcoinchanford(slide["coinc_trigs"], snglStat, ifo, \
                'sqrtsqr', 'k1', opts.plot_type)

        if opts.snr_snr:
          sym = 0
          legend_text = []
          if ('H1' in ifo_list) and (len(coincTriggersByMass[i]) > 0):
            viz.plotcoincval(coincTriggersByMass[i], snglStat, 'H1', ifo, \
                hanfordsym[sym], opts.plot_type)
            legend_text.append('H1' + ifo)
            for add_ifo in ifo_non_h:
              if add_ifo != ifo:
                viz.plotcoincval(coincTriggersByMass[i], snglStat, 'H1', ifo, \
                    addsym[sym], opts.plot_type, add_ifo)
                legend_text.append('H1' + ifo + '(also in ' + add_ifo + ')')

          if not opts.slide_snr: sym += 1
          if ('H2' in ifo_list) and (len(coincTriggersByMass[i]) > 0):
            viz.plotcoincval(coincTriggersByMass[i], snglStat, 'H2', ifo, \
                hanfordsym[sym], opts.plot_type)
            legend_text.append('H2' + ifo)
            for add_ifo in ifo_non_h:
              if add_ifo != ifo:
                viz.plotcoincval(coincTriggersByMass[i], snglStat, 'H2', ifo, \
                    addsym[sym], opts.plot_type, add_ifo)
                legend_text.append('H2' + ifo + '(also in ' + add_ifo + ')')

          if not opts.slide_snr: sym += 1
          if ('H1' in ifo_list) and ('H2' in ifo_list) and \
              (len(coincTriggersByMass[i]) > 0):
            viz.plotcoinchanford(coincTriggersByMass[i], snglStat, ifo, \
                'sqrtsqr', hanfordsym[sym], opts.plot_type)
            legend_text.append('H1H2' + ifo)
            for add_ifo in ifo_non_h:
              if add_ifo != ifo:
                viz.plotcoinchanford(coincTriggersByMass[i], snglStat, ifo, \
                    'sqrtsqr', addsym[sym],opts.plot_type, add_ifo)
                legend_text.append('H1H2' + ifo + '(also in ' + add_ifo + ')')


        if not opts.slide_snr:
          legend(legend_text,2)
        xlabel(snglStat.replace("_"," ") + ' Hanford', size='x-large')
        ylabel(snglStat.replace("_"," ") + ' '+ ifo, size='x-large')
        title('Hanford vs ' + ifo + snglStat.replace("_"," ") + \
          'mass range ' + str(mass_range[i]) + ' to ' +  str(mass_range[i+1]),\
          size='x-large')
        grid(True)
        if opts.min_snr:
          xlim(xmin=opts.min_snr)
          ylim(ymin=opts.min_snr)
        if opts.max_snr:
          xlim(xmax=opts.max_snr)
          ylim(ymax=opts.max_snr)
        else:
          viz.square_axis()
        if opts.enable_output is True:
          text = "Plot of Hanford vs " + ifo + " " + \
              snglStat.replace("_"," ") + \
              " statistic" + " for " + str(mass_range[i]) + "-" + \
              str(mass_range[i+1]) + " mass range"
          name = "H_vs_" + ifo +"_"+ snglStat + "-"+\
              str(mass_range[i]) + "_" + str(mass_range[i+1]) 
          fname = InspiralUtils.set_figure_name(opts, name)
          fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
          fnameList.append(fname)
          tagList.append(text)
        if not opts.show_plot:
          close()

################################
# plot of non hanford snr

if opts.snr_snr or opts.slide_snr:
  if len(ifo_non_h) >= 2:
    figure(figure_number)
    figure_number += 1
    hold(True)
    if opts.slide_snr:
      for slide in slideTriggers:
        if len(slide["coinc_trigs"]) > 0:
          viz.plotcoincval(slide["coinc_trigs"], snglStat, ifo_non_h[0], 
            ifo_non_h[1], 'kx',opts.plot_type)
    if opts.snr_snr and  len(coincTriggers) > 0:
      viz.plotcoincval(coincTriggers, snglStat, ifo_non_h[0], 
          ifo_non_h[1], 'r+',opts.plot_type)
      # The color in 'r+' only applies to markerfacecolor, which plotcoincval
      # removes.  Must set the markeredgecolor on the last dataset in the
      # current axes.
      gca().get_lines()[-1].set_markeredgecolor('r')

    xlabel(snglStat.replace("_"," ") + ' ' + ifo_non_h[0], size='x-large')
    ylabel(snglStat.replace("_"," ") + ' ' + ifo_non_h[1], size='x-large')
    title(ifo_non_h[0] + ' vs ' + ifo_non_h[1] + " " + \
        snglStat.replace("_"," "), size='x-large')
    grid(True)
    if opts.min_snr:
      xlim(xmin=opts.min_snr)
      ylim(ymin=opts.min_snr)
    if opts.max_snr:
      xlim(xmax=opts.max_snr)
      ylim(ymax=opts.max_snr)
    else:
      viz.square_axis()
    if opts.enable_output is True:
      text = "Plot of " + ifo_non_h[0] + " vs " +ifo_non_h[1] + \
          snglStat.replace("_"," ") + " statistic"
      name = ifo_non_h[0] + "_vs_" + ifo_non_h[1] + "-" + snglStat 
      fname = InspiralUtils.set_figure_name(opts, name)
      fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
      fnameList.append(fname)
      tagList.append(text)
    if not opts.show_plot:
      close()



if (opts.snr_snr or opts.slide_snr) and opts.mass_dependent:
  for i in range(len(mass_range)-1):
    if len(ifo_non_h) >= 2:
      figure(figure_number)
      figure_number += 1
      hold(True)
      if opts.slide_snr:
        for slide in slideTriggersByMass[i]:
          if len(slide["coinc_trigs"]) > 0:
            viz.plotcoincval(slide["coinc_trigs"], snglStat, ifo_non_h[0],
              ifo_non_h[1], 'kx',opts.plot_type)
      if opts.snr_snr and len(coincTriggersByMass[i]) > 0:
        viz.plotcoincval(coincTriggersByMass[i], snglStat, ifo_non_h[0],
            ifo_non_h[1], 'r+',opts.plot_type)
        # The color in 'r+' only applies to markerfacecolor, which plotcoincval
        # removes.  Must set the markeredgecolor on the last dataset in the
        # current axes.
        gca().get_lines()[-1].set_markeredgecolor('r')

      xlabel(snr + ' ' + ifo_non_h[0], size='x-large')
      ylabel(snr + ' ' + ifo_non_h[1], size='x-large')
      title(ifo_non_h[0] + ' vs ' + ifo_non_h[1] + " " + \
          snglStat.replace("_"," ") +'\n' + 'mass range ' + \
          str(mass_range[i]) + ' to ' + str(mass_range[i+1]), size='x-large')
      grid(True)
      if opts.min_snr:
        xlim(xmin=opts.min_snr)
        ylim(ymin=opts.min_snr)
      if opts.max_snr:
        xlim(xmax=opts.max_snr)
        ylim(ymax=opts.max_snr)
      else:
        viz.square_axis()
      if opts.enable_output is True:
        text = "Plot of " + ifo_non_h[0] +" vs " + ifo_non_h[1] +  " " + \
            snglStat.replace("_"," ") + " statistic" + " for " + \
            str(mass_range[i]) + "-" + str(mass_range[i+1]) + " mass range"
        name = ifo_non_h[0] +"_vs_" +ifo_non_h[1] + "-"\
            +snglStat + str(mass_range[i]) + "_" + str(mass_range[i+1])
        fname = InspiralUtils.set_figure_name(opts, name)
        fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
        fnameList.append(fname)
        tagList.append(text)
      if not opts.show_plot:
        close()
  



################################
# plot of h1 vs h2 eff dist
if opts.dist_dist:
  if 'H1' in ifo_list and 'H2' in ifo_list:
    figure(figure_number)
    figure_number += 1
    hold(True)
    if opts.slide_dist:
      for slide in slideTriggers:
        if len(slide["coinc_trigs"]) > 0:
          viz.plotcoincval(slide["coinc_trigs"], 'eff_distance', 'H1', 'H2', \
          'kx', opts.plot_type)
      
    if opts.ext_trig and len(offsourceTriggers) > 0:
      viz.plotcoincval(offsourceTriggers, 'eff_distance', 'H1', 'H2', 'bo', \
          opts.plot_type)
      # The color in 'bo' only applies to markerfacecolor, which plotcoincval
      # removes.  Must set the markeredgecolor on the last dataset in the
      # current axes.
      gca().get_lines()[-1].set_markeredgecolor('b')

    if opts.dist_dist and len(coincTriggers):
      viz.plotcoincval(coincTriggers, 'eff_distance', 'H1', 'H2', 'r+', \
          opts.plot_type)
      # The color in 'r+' only applies to markerfacecolor, which plotcoincval
      # removes.  Must set the markeredgecolor on the last dataset in the
      # current axes.
      gca().get_lines()[-1].set_markeredgecolor('r')

    xlabel('eff distance H1 (Mpc)', size='x-large')
    ylabel('eff distance H2 (Mpc)', size='x-large')
    title('H1 vs H2 coincident event effective distance', size='x-large')
    grid(True)
    if opts.min_snr:
      xlim(xmin=opts.min_snr)
      ylim(ymin=opts.min_snr)
    if opts.max_snr:
      xlim(xmax=opts.max_snr)
      ylim(ymax=opts.max_snr)
    else:
      viz.square_axis()
    if opts.enable_output is True:
      text = "Plot of H1 vs H2 effective distance"
      name =  "H1_vs_H2_dist" 
      fname = InspiralUtils.set_figure_name(opts, name)
      fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
      fnameList.append(fname)
      tagList.append(text)
    if not opts.show_plot:
      close()
  
if opts.dist_dist and opts.mass_dependent:
  for i in range(len(mass_range)-1):
    if 'H1' in ifo_list and 'H2' in ifo_list:
      figure(figure_number)
      figure_number += 1
      hold(True)
      if opts.slide_dist:
        for slide in slideTriggersByMass[i]:
          if len(slide["coinc_trigs"]) > 0:
            viz.plotcoincval(slide["coinc_trigs"], 'eff_distance', 'H1', 'H2', \
            'kx', opts.plot_type)

      if opts.dist_dist and len(coincTriggersByMass[i]) > 0:
        viz.plotcoincval(coincTriggersByMass[i], 'eff_distance', 'H1', 'H2', \
            'r+', opts.plot_type)
        # The color in 'ro' only applies to markerfacecolor, which plotcoincval
        # removes.  Must set the markeredgecolor on the last dataset in the
        # current axes.
        gca().get_lines()[-1].set_markeredgecolor('r')

      xlabel('eff distance H1 (Mpc)', size='x-large')
      ylabel('eff distance H2 (Mpc)', size='x-large')
      title('H1 vs H2 coincident event effective distance \n' + \
        'mass range ' + str(mass_range[i]) + ' to ' + str(mass_range[i+1]), \
        size='x-large')
      grid(True)
      if opts.min_snr:
        xlim(xmin=opts.min_snr)
        ylim(ymin=opts.min_snr)
      if opts.max_snr:
        xlim(xmax=opts.max_snr)
        ylim(ymax=opts.max_snr)
      else:
        viz.square_axis()
      if opts.enable_output is True:
        text = "Plot of H1 vs H2 effective distance for " + \
            str(mass_range[i]) + ' to ' + str(mass_range[i+1]) + " mass range"
        name =  "H1_vs_H2_dist_"+str(mass_range[i])  +"_"+str(mass_range[i+1])
        fname = InspiralUtils.set_figure_name(opts, name)
        fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
        fnameList.append(fname)
        tagList.append(text)
      if not opts.show_plot:
        close()


####################################
# plot of statistic vs time (for each ifo)
if opts.snr_time:
  figure(figure_number)
  figure_number += 1
  hold(True)
  for ifo in ifo_list:
    ifoTrigs = coincTriggers.getsngls(ifo)
    if opts.plot_type == 'linear':
      plot_type = 'linear'
    elif opts.plot_type == 'log':
      plot_type = 'logy'
    if len(ifoTrigs) > 0:
      viz.plot_a_v_b(ifoTrigs, 'end_time', snglStat, plot_type, \
          plot_sym = InspiralUtils.colors[ifo] + InspiralUtils.symbols[ifo], \
          plot_label = ifo)
  legend()
  title( snglStat.replace("_"," ") + " vs end time " + \
      "for each IFO in the zero-lag data" , size='x-large')
  xlabel('end time (days into run)',size='x-large')
  ylabel(snglStat.replace("_"," "),size='x-large')
  grid(True)
  if opts.min_snr:
    ylim(ymin=opts.min_snr)
  if opts.max_snr:
    ylim(ymax=opts.max_snr)
    
  if opts.enable_output is True:
    name = snglStat + "_vs_time"
    text = "Plot of " + snglStat.replace("_"," ") + \
        " vs time for each of the IFOs in the zero-lag data"
    fname = InspiralUtils.set_figure_name(opts, name)
    fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
    fnameList.append(fname)
    tagList.append(text)
  if not opts.show_plot:
    close()

if opts.snr_time and opts.mass_dependent:
  for i in range(len(mass_range)-1):
    figure(figure_number)
    figure_number += 1
    hold(True)
    sym = 0
    for ifo in ifo_list:
      ifoTrigs = coincTriggersByMass[i].getsngls(ifo)
      if opts.plot_type == 'linear':
        plot_type = 'linear'
      elif opts.plot_type == 'log':
        plot_type = 'logy'
      if len(ifoTrigs) > 0:
        viz.plot_a_v_b(ifoTrigs, 'end_time', snglStat, plot_type, \
        plotsymbols[sym])
      legend(ifo_list,2)
      title( snglStat.replace("_"," ") + ' vs end time \n' + \
          'mass range ' + str(mass_range[i]) + ' to ' + \
          str(mass_range[i+1]), size='x-large')
      xlabel('end time (days into run)',size='x-large')
      ylabel(snglStat.replace("_"," "),size='x-large')
      grid(True)
      sym += 1
      if opts.min_snr:
        ylim(ymin=opts.min_snr)
      if opts.max_snr:
        ylim(ymax=opts.max_snr)
  

    if opts.enable_output is True:
      name =  snglStat + "_vs_time-"+ str(mass_range[i]) +   "_" + \
          str(mass_range[i+1]) 
      text = "Plot of " + snglStat.replace("_"," ") + \
          " vs time for each of the IFOs in " \
          + str(mass_range[i]) + ' to ' + str(mass_range[i+1]) + " mass range"
      fname = InspiralUtils.set_figure_name(opts, name)
      fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
      fnameList.append(fname)
      tagList.append(text)
    if not opts.show_plot:
      close()


################################################
# make plot/hist of number of triggers per slide
if opts.hist_slides or opts.plot_slides:
  coincTrigs = CoincInspiralUtils.coincInspiralTable()
  if opts.add_zero_lag:
    coincTrigs = coincTriggers

  if opts.hist_slides and len(ifo_list) > 2:
    figure(figure_number)
    figure_number += 1
    if len(slideTriggers)>0:
      viz.histslides(slideTriggers,coincTrigs,scalebkg=opts.zero_lag_playground)
    if opts.enable_output is True:
      name =  "hist_slide_trigs" 
      text = "Histogram of number of all coincident triggers per time slide"
      fname = InspiralUtils.set_figure_name(opts, name)
      fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
      fnameList.append(fname)
      tagList.append(text)
    if not opts.show_plot:
      close()

  if opts.plot_slides and len(ifo_list) > 2:
    figure(figure_number)
    figure_number += 1
    if len(slideTriggers)>0:
      viz.plotslides(slideTriggers,coincTrigs,scalebkg=opts.zero_lag_playground)
    if opts.enable_output is True:
      name = "plot_slide_trigs" 
      text = "Plot of number of all coincident triggers per time slide"
      fname = InspiralUtils.set_figure_name(opts, name)
      fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
      fnameList.append(fname)
      tagList.append(text)
    if not opts.show_plot:
      close()

  for ifos in ifo_coincs:
    if opts.hist_slides:
      figure(figure_number)
      figure_number += 1
      if len(slideTriggers)>0:
        viz.histslides(slideTriggers,coincTrigs,ifos, \
            scalebkg=opts.zero_lag_playground)
        if opts.enable_output is True:
          ifo_concat = "" 
          for ifo in ifos:
            ifo_concat += ifo
          name = ifo_concat + "_hist_slide_trigs"
          text = "Histogram of " + ifo_concat + " triggers per time slide" 
          fname = InspiralUtils.set_figure_name(opts, name)
          fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
          fnameList.append(fname)
          tagList.append(text)
        if not opts.show_plot:
          close()

    if opts.plot_slides:
      figure(figure_number)
      figure_number += 1
      if len(slideTriggers)>0:
        viz.plotslides(slideTriggers,coincTrigs,ifos, \
            scalebkg=opts.zero_lag_playground)
        if opts.enable_output is True:
          ifo_concat = ""
          for ifo in ifos:
            ifo_concat += ifo
          name = ifo_concat + "_plot_slide_trigs"
          text = "Plot of " + ifo_concat + " triggers per time slide"
          fname = InspiralUtils.set_figure_name(opts, name)
          fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
          fnameList.append(fname)
          tagList.append(text)
        if not opts.show_plot:
          close()

if (opts.hist_slides or opts.plot_slides) and opts.mass_dependent:
  for i in range(len(mass_range)-1):
    coincTrigs = CoincInspiralUtils.coincInspiralTable()
    if opts.add_zero_lag:
      coincTrigs = coincTriggersByMass[i]
    
    if opts.hist_slides:
      figure(figure_number)
      figure_number += 1
      viz.histslides(slideTriggersByMass[i],coincTrigs, \
        scalebkg=opts.zero_lag_playground)
      if opts.enable_output is True:
        name ="hist_slide_trigs" +  str(mass_range[i]) + "_" + \
            str(mass_range[i+1]) 
        text = \
            "Histogram of number of coincident triggers per time slide for "+ \
            str(mass_range[i]) + " to " +  str(mass_range[i+1]) + " mass range"
        fname = InspiralUtils.set_figure_name(opts, name)
        fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
        fnameList.append(fname)
        tagList.append(text)
      if not opts.show_plot:
        close()

      
    if opts.plot_slides:
      figure(figure_number)
      figure_number += 1
      viz.plotslides(slideTriggersByMass[i],coincTrigs, \
        scalebkg=opts.zero_lag_playground)
      if opts.enable_output is True:
        name = "plot_slide_trigs" + str(mass_range[i]) + "_" + \
            str(mass_range[i+1]) 
        text = "Plot of number of coincident triggers per time slide for " +\
            str(mass_range[i]) + " to " + str(mass_range[i+1]) + " mass range"  
        fname = InspiralUtils.set_figure_name(opts, name)
        fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
        fnameList.append(fname)
        tagList.append(text)
      if not opts.show_plot:
        close()

    for ifos in ifo_coincs:
      if opts.hist_slides:
        figure(figure_number)
        figure_number += 1
        viz.histslides(slideTriggersByMass[i],coincTrigs,ifos,\
        scalebkg=opts.zero_lag_playground)
        if opts.enable_output is True:
          ifo_concat = ""          
          for ifo in ifos:
            ifo_concat += ifo 
          name = ifo_concat + "_hist_slide_trigs" + str(mass_range[i]) 
          text = "Histogram of " + ifo_concat + " triggers per time slide" +  \
            str(mass_range[i]) + " to " +  str(mass_range[i+1]) + " mass range"
          fname = InspiralUtils.set_figure_name(opts, name)
          fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
          fnameList.append(fname)
          tagList.append(text)
        if not opts.show_plot:
          close()

      if opts.plot_slides:
        figure(figure_number)
        figure_number += 1
        viz.plotslides(slideTriggersByMass[i],coincTrigs,ifos,\
        scalebkg=opts.zero_lag_playground)
        if opts.enable_output is True:
          ifo_concat = ""
          for ifo in ifos:
            ifo_concat += ifo
          name = ifo_concat + "_plot_slide_trigs" + str(mass_range[i]) + "_" +\
              str(mass_range[i+1]) 
          text = "Plot of " + ifo_concat + " triggers per time slide" + \
              str(mass_range[i]) + " to " +  str(mass_range[i+1]) + \
              " mass range"
          fname = InspiralUtils.set_figure_name(opts, name)
          fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
          fnameList.append(fname)
          tagList.append(text)
        if not opts.show_plot:
          close()


################################################
# make histogram of stat distribution
if opts.snr_hist:
  figure(figure_number)
  figure_number += 1

  viz.histstat(coincTriggers, slideTriggers, None, opts.min_snr, \
      opts.max_snr, opts.nbins, statistic.name)
  grid(True)
  if opts.enable_output is True:
    name = "hist_" + statistic.name 
    text = "Histogram of " + statistic.name + " distribution"
    fname = InspiralUtils.set_figure_name(opts, name)
    fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
    fnameList.append(fname)
    tagList.append(text)
  if not opts.show_plot:
    close()
 
if opts.snr_dist:
  if len(ifo_list) > 2:
    figure(figure_number) 
    figure_number += 1
    viz.cumhiststat(coincTriggers, slideTriggers, None, opts.min_snr, \
        opts.max_snr, opts.nbins, statistic.name, \
        scalebkg=opts.zero_lag_playground)
    if opts.num_slides: 
      ylim(ymin=1.0/(3 * opts.num_slides) )
    else:
      ylim(ymin=1.0)
    grid(True)
    if opts.enable_output is True:  
      name = "cum_hist_" + statistic.name 
      text = "Cumulative histogram of " + statistic.name + " distribution"
      fname = InspiralUtils.set_figure_name(opts, name)
      fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
    if not opts.show_plot:
      close()
      fnameList.append(fname)
      tagList.append(text)

  for ifos in ifo_coincs:
    figure(figure_number) 
    figure_number += 1
    viz.cumhiststat(coincTriggers, slideTriggers, ifos, opts.min_snr, \
        opts.max_snr, opts.nbins, statistic.name, \
        scalebkg=opts.zero_lag_playground)
    if opts.num_slides: 
      ylim(ymin=1.0/(3 * opts.num_slides) )
    else:
      ylim(ymin=1.0)
    grid(True)

    if opts.enable_output is True:
      ifo_concat = "" 
      for ifo in ifos:
        ifo_concat += ifo
      name = ifo_concat + "_cum_hist_" + statistic.name 
      text = "Cumulative histogram of " + statistic.name + \
          " distribution for " + ifo_concat + " triggers"
      fname = InspiralUtils.set_figure_name(opts, name)
      fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
      fnameList.append(fname)
      tagList.append(text)
    if not opts.show_plot:
      close()


if opts.snr_dist and opts.mass_dependent:
  for i in range(len(mass_range)-1):
    figure(figure_number)
    figure_number += 1
    viz.cumhiststat(coincTriggersByMass[i], slideTriggersByMass[i], None, \
      opts.min_snr, opts.max_snr, opts.nbins, statistic.name, \
      scalebkg=opts.zero_lag_playground)
    ylim(ymin=1.0e-01)
    ylim(ymax=20)
    if opts.enable_output is True:
      name = "cum_hist_" + statistic.name + \
        str(mass_range[i]) + "_" + str(mass_range[i+1]) 
      text = "Cumulative histogram of " + statistic.name + " distribution for triggers in " +\
          str(mass_range[i]) + " to " + str(mass_range[i+1]) +" mass range"
      fname = InspiralUtils.set_figure_name(opts, name)
      fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
      fnameList.append(fname)
      tagList.append(text)
    if not opts.show_plot:
      close()
 


    for ifos in ifo_coincs:
      figure(figure_number)
      figure_number += 1
      viz.cumhiststat(coincTriggersByMass[i], slideTriggersByMass[i], ifos, \
        opts.min_snr, opts.max_snr, opts.nbins, statistic.name, \
        scalebkg=opts.zero_lag_playground)
      if opts.enable_output is True:
        ifo_concat = ""
        for ifo in ifos:
          ifo_concat+= ifo
        name = ifo_concat + "_cum_hist_" + statistic.name + str(mass_range[i]) \
            + "_" + str(mass_range[i+1]) 
        text = "Cumulative histogram of " + statistic.name + " distribution of " + \
            ifo_concat + " triggers in " + str(mass_range[i]) + " to " + str(mass_range[i+1]) +" mass range"
        fname = InspiralUtils.set_figure_name(opts, name)
        fname_thumb = InspiralUtils.savefig_pylal( filename=fname )
        fnameList.append(fname)
        tagList.append(text)
      if not opts.show_plot:
        close()

####################################################################
# parallel coordinates plot. Output is a ROOT script
# and tree files
if opts.parcoord_var or opts.snr_cube:
  from ROOT import gROOT,gSystem,gStyle,TCanvas,TFile
  from ROOT import TH3F, TNtupleD
  from ROOT import TParallelCoord, TParallelCoordVar, TParallelCoordRange
  from ROOT import TLegend
  from ROOT import kRed,kGreen,kBlue,kCyan,kYellow,kViolet,kOrange,kMagenta
  import numpy

  gROOT.SetBatch(1)
  gStyle.SetPalette(1)
  #  Rcolors = {'G1':kYellow,'H1':kRed,'H2':kBlue,'L1':kGreen,'V1':kViolet}
  Rcolors = [kGreen+3,kRed+1,kCyan+3,kCyan-3,kBlue+1,kOrange,kMagenta-4,kBlue-7,kMagenta+1,kRed-7,kYellow+2]

  if opts.snr_cube:
    if len(ifo_list) == 3 and len(ifo_non_h) == 1 and len(coincTriggers) > 0:
      can = TCanvas('canSnr3D','snr3D',1024,700)
      cangl = TCanvas('canSnr3Dgl','snr3Dgl',1024,700)
      ifo_coinc = coincTriggers.coinctype(ifo_list)
      snrifo1 = viz.readcol(ifo_coinc.getsngls(ifo_list[0]),'snr',ifo_list[0])
      snrifo2 = viz.readcol(ifo_coinc.getsngls(ifo_list[1]),'snr',ifo_list[1])
      snrifo3 = viz.readcol(ifo_coinc.getsngls(ifo_list[2]),'snr',ifo_list[2])
      snr1range = abs(max(snrifo1)-min(snrifo1))
      snr1max = max(snrifo1)+snr1range/20
      snr1min = min(snrifo1)-snr1range/20
      snr2range = abs(max(snrifo2)-min(snrifo2))
      snr2max = max(snrifo2)+snr2range/20
      snr2min = min(snrifo2)-snr2range/20
      snr3range = abs(max(snrifo3)-min(snrifo3))
      snr3max = max(snrifo3)+snr3range/20
      snr3min = min(snrifo3)-snr3range/20
      hist=TH3F('snr3D','snr('+ifo_list[0]+') vs snr('+ifo_list[1]+') vs snr('+ifo_list[2]+')', \
		30,snr1min,snr1max,30,snr2min,snr2max,30,snr3min,snr3max)
      for isnr in range(len(snrifo1)):
	hist.Fill(snrifo1[isnr],snrifo2[isnr],snrifo3[isnr])

      hist.SetMarkerStyle(20)
      hist.SetMarkerColor(kBlue)

      hist.Draw('glbox');
      cangl.SaveAs('snr3Dgl.png')
      cangl.SaveAs('snr3Dgl.root')

      hist.Draw()
      can.SaveAs('snr3D.png')
      can.SaveAs('snr3D.root')


      f = TFile('snr3Dhist.root',"RECREATE")
      hist.Write()
      f.Close()
      
  #############################################
  # parallel coordinates
  
  if opts.parcoord_var:
    pc_var_list = opts.parcoord_var.split(' ')
    
    if len(coincTriggers) > 0 and len(ifo_list)>0 and len(pc_var_list)>0:
      pc_plot_title = ''
      for ipcvar in range(len(pc_var_list)):
        pc_plot_title = pc_plot_title + pc_var_list[ipcvar]+'/'
      pc_plot_title = pc_plot_title + ' '
      for ipcifo in range(len(ifo_list)):
        pc_plot_title = pc_plot_title + ifo_list[ipcifo]
      canParC = TCanvas('canParCoord',pc_plot_title,1024,700)
      ifo_coinc = coincTriggers.coinctype(ifo_list)
      
      # build the list of data (list of arrays) of the variables
      pc_name=[]
      pc_table=[]
      nt_var=''
      for ipcvar0 in range(len(pc_var_list)):
        for ipcifo in range(len(ifo_list)):
          pc_name.append(pc_var_list[ipcvar0]+'_'+ifo_list[ipcifo])
	  varifo = viz.readcol(ifo_coinc.getsngls(ifo_list[ipcifo]),pc_var_list[ipcvar0],ifo_list[ipcifo])
	  pc_table.append(varifo)
	  
      # build the list of names for the ntuple definition in ROOT
      for ipcvar in range(len(pc_name)-1):
        nt_var = nt_var+pc_name[ipcvar]+':'
      nt_var = nt_var+pc_name[len(pc_name)-1]
      
      # build the ntuple and draw it
      ntup = TNtupleD('parcoord',pc_plot_title,nt_var)
      nt_fill_array = numpy.ndarray([len(pc_name)])
      for ipc in range(len(pc_table[0])):
	for ipcvar in range(len(pc_name)):
	  var_array = pc_table[ipcvar]
	  nt_fill_array[ipcvar] = var_array[ipc]
        ntup.Fill(nt_fill_array)
      
      ntup.Draw(nt_var,'','para')
      
      # build a set of standard selections, one for each variable
      para = canParC.GetListOfPrimitives().FindObject('ParaCoord')
      for ipcvar in range(len(pc_name)):
        varaxis = para.GetVarList().FindObject(pc_name[ipcvar])
        para.AddSelection('select_'+pc_name[ipcvar])
	# 11 colors in the Rcolors array, set the color modulo 11
        para.GetCurrentSelection().SetLineColor(Rcolors[ipcvar%11])
	# Calculate and set the range
	varaxismin = min(pc_table[ipcvar])
	varaxismax = max(pc_table[ipcvar])
	varaxislength = varaxismax-varaxismin
        varaxis.AddRange(\
	   TParallelCoordRange(varaxis,\
	   varaxismax-varaxislength/(2*len(pc_name))*(ipcvar+0.5),\
	   varaxismax-varaxislength/(2*len(pc_name))*(ipcvar+0.75)))
      
      # Save the parallel coordinates plot and accompanying files
      # in the ParCoord directory
      if (not os.path.exists('ParCoord')):
        os.mkdir('ParCoord')
	
      if (not os.path.isdir('ParCoord')):
        print('ParCoord is not a directory, cannot save parallel coordinates plot')
      else:
        gSystem.ChangeDirectory('ParCoord')
        canParC.SaveAs('ParCoord.C')

# ============================================================================
# final step: html, cache file generation
if opts.enable_output is True:
  html_filename = InspiralUtils.write_html_output(opts, args, fnameList, tagList)
  InspiralUtils.write_cache_output(opts, html_filename, fnameList)

# ============================================================================


if opts.show_plot:
  show()
