From 8ac2cd1c4b646be5b31e267b4793ad8c47164a7e Mon Sep 17 00:00:00 2001
From: Arthus <arthus@gfz-potsdam.de>
Date: Tue, 22 Jun 2021 14:10:13 +0200
Subject: [PATCH] Add a routine to extract block diagonals and fix temporal
 errors.

---
 corbam/inversion.py     | 31 ++++++++++++++++++++-----------
 corbam/utils.py         | 15 ++++++++++++++-
 tests/test_inversion.py |  4 ++--
 3 files changed, 36 insertions(+), 14 deletions(-)

diff --git a/corbam/inversion.py b/corbam/inversion.py
index b893b4c..03999da 100644
--- a/corbam/inversion.py
+++ b/corbam/inversion.py
@@ -45,7 +45,8 @@ from scipy import linalg
 
 import pyfield
 
-from corbam.utils import scaling, read_data, dif2nez, nez2dif
+from corbam.utils import scaling, read_data, dif2nez, nez2dif, \
+    extractBlockDiag, blockDiag
 
 __all__ = ['grad_d', 'grad_i', 'grad_f', 'Inversion']
 
@@ -218,7 +219,7 @@ class Inversion:
                                             self.idx_NEZ_F))
 
         # Temporal errors
-        errs_T_full = data[['dt', 'dt', 'dt']].loc[self.idx_full]**2
+        errs_T_full = data[['dt']].loc[self.idx_full]**2
         errs_T_full = np.asfortranarray(errs_T_full).ravel()
         self.errs_T_full = errs_T_full
 
@@ -300,15 +301,14 @@ class Inversion:
         self.cov_NEZ_full += lapl_appr
 
         # Noisy input correction term for complete records
-        corr_full = 2*np.diagonal((alph/theta_Dip)**2
-                                  * cov_NEZ_obs_Dip[self.idx_NEZ_full[:, None],
-                                                    self.idx_NEZ_full[None, :]]
-                                  + (lamb/theta_WoDip)**2
-                                  * cov_NEZ_obs_WoDip[self.idx_NEZ_full[:,
-                                                                        None],
-                                                      self.idx_NEZ_full[None,
-                                                                        :]])
-        self.corr_full = np.diag(corr_full * self.errs_T_full)
+        corr_full = \
+            2*extractBlockDiag((alph/theta_Dip)**2
+                               * cov_NEZ_obs_Dip[self.idx_NEZ_full[:, None],
+                                                 self.idx_NEZ_full[None, :]]
+                               + (lamb/theta_WoDip)**2
+                               * cov_NEZ_obs_WoDip[self.idx_NEZ_full[:, None],
+                                                   self.idx_NEZ_full[None, :]])
+        self.corr_full = blockDiag(corr_full * self.errs_T_full[:, None, None])
         self.cov_NEZ_full += noisy * self.corr_full
         # Invert covariance using Cholesky
         L = linalg.cholesky(self.cov_NEZ_full, lower=True)
@@ -383,6 +383,9 @@ class Inversion:
             + (lamb/theta_WoDip)**2 \
             * cov_NEZ_obs_WoDip[self.idx_NEZ_icmp[:, None],
                                 self.idx_NEZ_icmp[None, :]]
+
+        corr_icmp2 = extractBlockDiag(corr_icmp)
+
         corr_icmp = corr_icmp.reshape(self.n_icmp, 3,
                                       self.n_icmp, 3)
 
@@ -392,6 +395,12 @@ class Inversion:
                               self.grad_DIF)
         corr_icmp = 2*np.diagonal(corr_icmp)
 
+        corr_icmp2 = np.einsum('ij,ijl,il->i',
+                               self.grad_DIF,
+                               corr_icmp2,
+                               self.grad_DIF)
+        print(np.allclose(corr_icmp, 2*corr_icmp2))
+
         self.corr_icmp = np.diag(corr_icmp * self.errs_T_icmp)
         self.cov_DIF_icmp += noisy * self.corr_icmp
 
diff --git a/corbam/utils.py b/corbam/utils.py
index ed442d8..7025133 100644
--- a/corbam/utils.py
+++ b/corbam/utils.py
@@ -35,7 +35,20 @@ import pyfield
 path = Path(__file__).parent
 
 __all__ = ['read_data', 'load', 'scaling', 'scale_coeffs', 'scale_vars',
-           'scale_covs', 'nez2dif', 'dif2nez']
+           'scale_covs', 'nez2dif', 'dif2nez', 'extractBlockDiag',
+           'blockDiag']
+
+
+def extractBlockDiag(A, dim=3):
+    n = int(A.shape[0] / dim)
+    return np.copy(A.reshape(n, dim, n, dim)[np.arange(n), :, np.arange(n), :])
+
+
+def blockDiag(B):
+    n, dim, _ = B.shape
+    ret = np.zeros((n, dim, n, dim))
+    ret[np.arange(n), :, np.arange(n), :] = B
+    return ret.reshape(n*dim, n*dim)
 
 
 def read_data(fname):
diff --git a/tests/test_inversion.py b/tests/test_inversion.py
index a4acdd8..7eef99c 100644
--- a/tests/test_inversion.py
+++ b/tests/test_inversion.py
@@ -24,10 +24,10 @@ from corbam.inversion import Inversion
 from corbam.utils import load
 
 
-pars = load('../tests/test_parfile.py')
+pars = load('./tests/test_parfile.py')
 inv = Inversion(pars.data)
 
-with np.load('../dat/benchmark.npz') as fh:
+with np.load('./dat/benchmark.npz') as fh:
     times = fh['times']
     loc = fh['loc']
     x = fh['x']
-- 
GitLab