From 89bccbc2617cecbce139fb5fab70cd7481c0abb5 Mon Sep 17 00:00:00 2001
From: "gregory.ashton" <gregory.ashton@ligo.org>
Date: Tue, 21 Nov 2017 16:34:53 +0100
Subject: [PATCH] Various improvements to Non-earth test

---
 pyfstat/grid_based_searches.py | 102 ++++++++++++++++++++++++++++++---
 1 file changed, 94 insertions(+), 8 deletions(-)

diff --git a/pyfstat/grid_based_searches.py b/pyfstat/grid_based_searches.py
index 61ff8d4..3bf52d7 100644
--- a/pyfstat/grid_based_searches.py
+++ b/pyfstat/grid_based_searches.py
@@ -660,19 +660,31 @@ class EarthTest(GridSearch):
 
         For all other parameters, see `pyfstat.ComputeFStat` for details
         """
+        if os.path.isdir(outdir) is False:
+            os.mkdir(outdir)
         self.nsegs = 1
         self.F0s = [F0]
         self.F1s = [F1]
         self.F2s = [F2]
         self.Alphas = [Alpha]
         self.Deltas = [Delta]
+        self.duration = maxStartTime - minStartTime
         self.deltaRadius = np.atleast_1d(deltaRadius)
         self.phaseOffset = np.atleast_1d(phaseOffset)
+        self.phaseOffset = self.phaseOffset + 1e-12  # Hack to stop cached data being used
         self.deltaPspin = np.atleast_1d(deltaPspin)
         self.set_out_file()
         self.SSBprec = lalpulsar.SSBPREC_RELATIVISTIC
         self.keys = ['deltaRadius', 'phaseOffset', 'deltaPspin']
 
+        self.prior_widths = [
+            np.max(self.deltaRadius)-np.min(self.deltaRadius),
+            np.max(self.phaseOffset)-np.min(self.phaseOffset),
+            np.max(self.deltaPspin)-np.min(self.deltaPspin)]
+
+        if hasattr(self, 'search') is False:
+            self.inititate_search_object()
+
     def get_input_data_array(self):
         logging.info("Generating input data array")
         coord_arrays = [self.deltaRadius, self.phaseOffset, self.deltaPspin]
@@ -682,16 +694,26 @@ class EarthTest(GridSearch):
         self.input_data = np.array(input_data)
         self.coord_arrays = coord_arrays
 
+    def run_special(self):
+        vals = [self.minStartTime, self.maxStartTime, self.F0, self.F1,
+                self.F2, self.Alpha, self.Delta]
+        self.special_data = {'zero': [0, 0, 0]}
+        for key, (dR, dphi, dP) in self.special_data.iteritems():
+            rescaleRadius = (1 + dR / lal.REARTH_SI)
+            rescalePeriod = (1 + dP / lal.DAYSID_SI)
+            lalpulsar.BarycenterModifyEarthRotation(
+                rescaleRadius, dphi, rescalePeriod, self.tref)
+            FS = self.search.get_det_stat(*vals)
+            self.special_data[key] = list([dR, dphi, dP]) + [FS]
+
     def run(self):
+        self.run_special()
         self.get_input_data_array()
         old_data = self.check_old_data_is_okay_to_use()
         if old_data is not False:
             self.data = old_data
             return
 
-        if hasattr(self, 'search') is False:
-            self.inititate_search_object()
-
         data = []
         vals = [self.minStartTime, self.maxStartTime, self.F0, self.F1,
                 self.F2, self.Alpha, self.Delta]
@@ -708,19 +730,83 @@ class EarthTest(GridSearch):
         np.savetxt(self.out_file, data, delimiter=' ')
         self.data = data
 
-    def marginalised_bayes_factor(self, prior_widths):
+    def marginalised_bayes_factor(self, prior_widths=None):
+        if prior_widths is None:
+            prior_widths = self.prior_widths
+
         ndims = self.data.shape[1] - 1
-        params = [np.unique(self.data[:, j]) for j in range(ndims)]
+        params = np.array([np.unique(self.data[:, j]) for j in range(ndims)])
         twoF = self.data[:, -1].reshape(tuple([len(p) for p in params]))
         F = twoF / 2.0
-        max_F = np.max(F)
         for i, x in enumerate(params[::-1]):
             if len(x) > 1:
                 dx = x[1] - x[0]
-                F = logsumexp(F, axis=-1)+np.log(dx)-np.log(prior_widths[i])
+                F = logsumexp(F, axis=-1)+np.log(dx)-np.log(prior_widths[-1-i])
             else:
                 F = np.squeeze(F, axis=-1)
-        return np.atleast_1d(F)[0], max_F
+        marginalised_F = np.atleast_1d(F)[0]
+        F_at_zero = self.special_data['zero'][-1]/2.0
+
+        max_idx = np.argmax(self.data[:, -1])
+        max_F = self.data[max_idx, -1]/2.0
+        max_F_params = self.data[max_idx, :-1]
+        logging.info('F at zero = {:.1f}, marginalised_F = {:.1f},'
+                     ' max_F = {:.1f} ({})'.format(
+                         F_at_zero, marginalised_F, max_F, max_F_params))
+        return F_at_zero - marginalised_F, (F_at_zero - max_F) / F_at_zero
+
+    def plot_corner(self, prior_widths=None, fig=None, axes=None):
+        Bsa, FmaxMismatch = self.marginalised_bayes_factor(prior_widths)
+
+        data = self.data[:, -1].reshape(
+            (len(self.deltaRadius), len(self.phaseOffset),
+             len(self.deltaPspin)))
+        xyz = [self.deltaRadius/lal.REARTH_SI, self.phaseOffset/(np.pi),
+               self.deltaPspin/60.]
+        labels = [r'$\frac{\Delta R}{R_\mathrm{Earth}}$',
+                  r'$\frac{\Delta \phi}{\pi}$',
+                  r'$\Delta P_\mathrm{spin}$ [min]',
+                  r'$2\mathcal{F}$']
+
+        from projection_matrix import projection_matrix
+
+        fig, axes = projection_matrix(data, xyz, projection='log_mean',
+                                      factor=1.6, labels=labels)
+        axes[-1][-1].axvline((lal.DAYJUL_SI - lal.DAYSID_SI)/60.0, color='C3')
+        plt.suptitle(
+            'T={:.1f} days, $f$={:.2f} Hz, $\log\mathcal{{B}}_{{S/A}}$={:.1f},'
+            r' $\frac{{\mathcal{{F}}_0-\mathcal{{F}}_\mathrm{{max}}}}'
+            r'{{\mathcal{{F}}_0}}={:.1e}$'
+            .format(self.duration/86400, self.F0, Bsa, FmaxMismatch), y=0.99,
+            size=14)
+        fig.savefig('{}/{}_projection_matrix.png'.format(
+            self.outdir, self.label))
+
+        fig, axes = projection_matrix(data, xyz, projection='max_slice',
+                                      factor=1.6, labels=labels)
+        axes[-1][-1].axvline((lal.DAYJUL_SI - lal.DAYSID_SI)/60.0, color='C3')
+        plt.suptitle('T={:.1f} days, $f$={:.2f} Hz' .format(
+            self.duration/86400, self.F0), y=0.99, size=14)
+        fig.savefig('{}/{}_max_slice.png'.format(self.outdir, self.label))
+
+    def plot(self, key, prior_widths=None):
+        Bsa, FmaxMismatch = self.marginalised_bayes_factor(prior_widths)
+
+        rescales_defaults = {'deltaRadius': 1/lal.REARTH_SI,
+                             'phaseOffset': 1/np.pi,
+                             'deltaPspin': 1}
+        labels = {'deltaRadius': r'$\frac{\Delta R}{R_\mathrm{Earth}}$',
+                  'phaseOffset': r'$\frac{\Delta \phi}{\pi}$',
+                  'deltaPspin': r'$\Delta P_\mathrm{spin}$ [s]'
+                  }
+
+        fig, ax = self.plot_1D(key, xrescale=rescales_defaults[key],
+                               xlabel=labels[key], savefig=False)
+        ax.set_title(
+            'T={} days, $f$={} Hz, $\log\mathcal{{B}}_{{S/A}}$={:.1f}'
+            .format(self.duration/86400, self.F0, Bsa))
+        fig.tight_layout()
+        fig.savefig('{}/{}_1D.png'.format(self.outdir, self.label))
 
 
 class DMoff_NO_SPIN(GridSearch):
-- 
GitLab