From 83d7387e99da63c842954759054a773bf49efc8f Mon Sep 17 00:00:00 2001
From: Jameson Graef Rollins <jrollins@finestructure.net>
Date: Fri, 15 Dec 2017 17:12:01 -0800
Subject: [PATCH] overhaul tests to use new gwinc_matlab functions

This directly compares the output of gwinc() and gwinc_matlab().  Any
noises that differ by more than 1% are noted and the return value of the
test is non-zero.  Differences can be plotted with the -p option.
---
 gwinc/test/__main__.py | 210 ++++++++++++++---------------------------
 1 file changed, 73 insertions(+), 137 deletions(-)

diff --git a/gwinc/test/__main__.py b/gwinc/test/__main__.py
index 117a210..8efaa40 100644
--- a/gwinc/test/__main__.py
+++ b/gwinc/test/__main__.py
@@ -1,165 +1,101 @@
 """Beginnings of a test suite for pygwinc
 
-todo:
-
- * unittest
- * write test to compare matgwinc default IFOs and pygwinc default
-
 """
 import os
+import sys
 import signal
-import scipy
-import scipy.io
+import pickle
 import numpy as np
 import matplotlib.pyplot as plt
+import argparse
 
 import logging
-logging.basicConfig(level=logging.INFO)
-
-from .. import load_ifo, noise_calc
-from ..plot import plot_noise
-from ..ifo import Struct
-# NOTE: gwinc needs to be imported before matlab for some very strange
-# reason:
-#
-# Traceback (most recent call last):
-#   File "./ifo2txt.py", line 9, in <module>
-#     import gwinc
-#   File "/home/jrollins/ligo/src/pygwinc/gwinc/__init__.py", line 9, in <module>
-#     from . import plot
-#   File "/home/jrollins/ligo/src/pygwinc/gwinc/plot.py", line 2, in <module>
-#     import matplotlib.pyplot as plt
-#   File "/usr/lib/python2.7/dist-packages/matplotlib/pyplot.py", line 29, in <module>
-#     import matplotlib.colorbar
-#   File "/usr/lib/python2.7/dist-packages/matplotlib/colorbar.py", line 32, in <module>
-#     import matplotlib.artist as martist
-#   File "/usr/lib/python2.7/dist-packages/matplotlib/artist.py", line 15, in <module>
-#     from .transforms import (Bbox, IdentityTransform, TransformedBbox,
-#   File "/usr/lib/python2.7/dist-packages/matplotlib/transforms.py", line 39, in <module>
-#     from matplotlib._path import (affine_transform, count_bboxes_overlapping_bbox,
-# ImportError: /opt/matlab/R2016b/extern/engines/python/dist/matlab/engine/glnxa64/../../../../../../../sys/os/glnxa64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by /usr/lib/python2.7/dist-packages/matplotlib/_path.x86_64-linux-gnu.so)
-#
-# no idea what that's about
-import matlab.engine
-
-
-GWINC_MAT_SCRIPT = '''
-source = SourceModel();
-source.BlackHole.Mass1 = 20; % [MSol]
-source.BlackHole.Mass2 = 20; % [MSol]
-
-flo = 5;
-fhi = 6000;
-npoints = 3000;
-f = logspace(log10(flo), log10(fhi), npoints);
-
-ifo = {ifoname}();
-ifo = precompIFO(ifo);
-[score, noises, ifo] = gwinc(f, [], ifo, source, 0);
-'''
-
-def run_gwinc(eng, ifoname, matpath):
-    # This doesn't work I think because the ifo.Suspension.Stages is not a
-    # "scalar structure":
-    # ifo = eng.IFOModel()
-    # ifo = eng.precompIFO(ifo)
-    script = GWINC_MAT_SCRIPT.format(ifoname=ifoname)
-    print()
-    print(script)
-    print()
-    eng.eval(script, nargout=0)
-    eng.save(matpath, 'score', 'noises', 'ifo', nargout=0)
+logging.basicConfig(level=logging.WARNING)
 
+from .. import load_ifo, gwinc
 
-def main():
 
-    ifoname = 'IFOModel'
-    gwincpath = os.path.join(os.path.dirname(__file__),
-                             'gwinc')
-    logging.info('gwincpath: {}'.format(gwincpath))
-    matpath = os.path.join(os.path.dirname(__file__),
-                           'gwinc.mat')
-    logging.info('matpath: {}'.format(matpath))
+IFO = 'aLIGO'
+FLO = 5
+FHI = 6000
+NPOINTS = 3000
 
-    if not os.path.exists(matpath):
-        logging.info("starting matlab engine...")
-        eng = matlab.engine.start_matlab()
-        eng.addpath(gwincpath)
-        logging.info("running gwinc matlab script...")
 
-        run_gwinc(eng, ifoname, matpath)
-        eng.exit()
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--plot', '-p', action='store_true', help='plot differences')
+    args = parser.parse_args()
+
+    ifo = load_ifo(IFO)
 
+    freq = np.logspace(np.log10(FLO), np.log10(FHI), NPOINTS)
+
+    matdata = os.path.join(os.path.dirname(__file__), 'matlab.pkl')
+    if os.path.exists(matdata):
+        logging.info("using existing {}...".format(matdata))
+        with open(matdata, 'r') as f:
+            mdata = pickle.load(f)
     else:
-        logging.info("Using existing {}...".format(matpath))
-
-    logging.info("loading {}...".format(matpath))
-    mat_dict = scipy.io.loadmat(matpath,
-                                squeeze_me=True,
-                                struct_as_record=False)
-
-    ifo = Struct.from_matstruct(mat_dict['ifo'])
-    matnoises = Struct.from_matstruct(mat_dict['noises']).to_dict()
-    f = matnoises['Freq']
-
-    # FIXME: pygwinc expects some newer attributes, for newer
-    # suspensionthermal calc that requires stage dilution, among other
-    # things.  Should make sure it's fully backwards compatible.
-    ifo.Seismic.Omicron = 1
-    ifo.Suspension.FiberType = 0
-    ifo.Suspension.Stage[0].Dilution = np.NaN
-    ifo.Suspension.Stage[1].Dilution = 106
-    ifo.Suspension.Stage[2].Dilution = 80
-    ifo.Suspension.Stage[3].Dilution = 87
-
-    logging.info("calculating noise...")
-    noises = noise_calc(ifo, f)
-
-    # plt.figure()
-    # plot_noise(noises)
-    # plt.title('pygwinc with matgwinc ifo')
-    # plt.show(block=False)
-
-    # mapping from pygwinc names to matgwinc names
-    MAPPING = {
-        'Quantum Vacuum': 'Quantum',
-        'Seismic': 'Seismic',
-        'Newtonian Gravity': 'Newtonian',
-        'Suspension Thermal': 'SuspThermal',
-        'Coating Brownian': 'MirrorThermal',
-        # 'Coating Thermo-Optic': 
-        # 'ITM Thermo-Refractive':
-        # 'ITM Carrier Density':
-        'Substrate Brownian': 'SubBrown',
-        'Substrate Thermo-Elastic': 'SubTE',
-        'Excess Gas': 'ResGas',
-        }
+        logging.info("calculating matlab noise...")
+        gwincpath = os.path.join(os.path.dirname(__file__), 'gwinc')
+        from ..gwinc_matlab import gwinc_matlab
+        score, noises, ifo = gwinc_matlab(freq, ifo, gwincpath=gwincpath)
+        mdata = dict(score=score, noises=noises, ifo=ifo)
+        with open(matdata, 'w') as f:
+            pickle.dump(mdata, f)
+
+
+    score, noises, ifo = gwinc(freq, ifo)
+
+    mnoises = mdata['noises']
 
     diffs = {}
     for name, noise in noises.iteritems():
+        if name == 'Freq':
+            continue
         try:
-            matnoise = matnoises[MAPPING[name]]
+            mnoise = mnoises[name]
         except KeyError:
             continue
-        logging.info("plot compare: {}".format(name))
-        # FIXME: for CoatTO
-        if isinstance(matnoise, dict):
-            logging.info("  skip")
+        # logging.info("compare: {}".format(name))
+
+        diff = np.sqrt(mnoise) - np.sqrt(noise)
+        frac = abs(diff / np.sqrt(noise))
+
+        if max(frac) < 0.01:
             continue
-        diff = matnoise - noise
-        logging.info("  max: {}, min: {}".format(max(diff), min(diff)))
-        diffs[name] = diff
 
-    fig, ax = plt.subplots(len(diffs), sharex=True)
-    for i, name in enumerate(diffs):
-        diff = diffs[name]
-        ax[i].semilogx(f, diff, label=name)
-        ax[i].legend(loc='lower right')
-    ax[0].set_title('difference: (mat_gwinc - pygwinc)')
-    plt.xlabel('frequency [Hz]')
-    plt.show()
+        logging.warning("Excessive difference: {}".format(name))
+        logging.warning("  max: {:e}, min: {:e}".format(max(frac), min(frac)))
+
+        diffs[name] = frac
+
+
+    if args.plot:
+        spec = (len(diffs), 2)
+        for i, name in enumerate(diffs):
+            ax = plt.subplot2grid(spec, (i, 0))
+            ax.loglog(freq, np.sqrt(noises[name]), label='pygwinc')
+            ax.loglog(freq, np.sqrt(mnoises[name]), label='matlab')
+            ax.grid()
+            ax.legend(loc='upper right')
+            # ax.set_title(name)
+            ax.set_ylabel(name)
+    
+            ax = plt.subplot2grid(spec, (i, 1))
+            ax.loglog(freq, diffs[name], label=name)
+            ax.grid()
+            # ax.set_title(name)
+    
+        plt.suptitle("noises that differ by more than 1% [(mat-py)/py]")
+        plt.show()
+
+
+    if len(diffs) > 0:
+        return 1
+    return 0
 
 
 if __name__ == '__main__':
     signal.signal(signal.SIGINT, signal.SIG_DFL)
-    main()
+    sys.exit(main())
-- 
GitLab