File: kalmanFilter.cpp

package info (click to toggle)
groops 0%2Bgit20250907%2Bds-1
  • links: PTS, VCS
  • area: non-free
  • in suites: forky, sid
  • size: 11,140 kB
  • sloc: cpp: 135,607; fortran: 1,603; makefile: 20
file content (165 lines) | stat: -rw-r--r-- 7,526 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/***********************************************/
/**
* @file kalmanFilter.cpp
*
* @brief Computes time variable gravity fields using Kalman filter approach
*
* @author Enrico Kurtenbach
* @author Andreas Kvas
* @date 2008-11-01
*/
/***********************************************/

// Latex documentation
#define DOCSTRING docstring
static const char *docstring = R"(
The program computes time variable gravity fields using the Kalman filter approach of

Kurtenbach, E., Eicker, A., Mayer-Gürr, T., Holschneider, M., Hayn, M., Fuhrmann, M., and Kusche, J. (2012).
Improved daily GRACE gravity field solutions using a Kalman smoother. Journal of Geodynamics, 59–60, 39–48.
\url{https://doi.org/10.1016/j.jog.2012.02.006}.

The updated state $\mathbf{x}_t^+$ is determined by solving the least squares adjustment
\begin{equation}
\mathbf{l}_t = \mathbf{A}_t \mathbf{x}_t + \mathbf{e}_t \hspace{25pt} \mathbf{e}_t \sim \mathcal{N}(0, \mathbf{R}_t)\\
\mathbf{B} \mathbf{x}^+_{t-1} = \mathbf{I} \mathbf{x}_t + \mathbf{v}_t\hspace{25pt} \mathbf{v} \sim \mathcal{N}(0,\mathbf{Q} + \mathbf{B} \mathbf{P}^+_{t-1}\mathbf{B}^T).
\end{equation}
In normal equation form this can be written as
\begin{equation}
\hat{\mathbf{x}}_t = \mathbf{x}^+_t = (\mathbf{N}_t + \mathbf{P}^{-^{-1}}_t)^{-1}(\mathbf{n}_t + \mathbf{P}^{-^{-1}}_t \mathbf{x}^-_t),
\end{equation}
where $\mathbf{x}_t^- = \mathbf{B} \mathbf{x}^+_{t-1}$ and $\mathbf{P}_t^{-} = \mathbf{Q} + \mathbf{B} \mathbf{P}^+_{t-1}\mathbf{B}^T$
are the predicted state and its covariance matrix.

The process dynamic $\mathbf{B}, \mathbf{Q}$ is represented as an \reference{autoregressive model}{fundamentals.autoregressiveModel},
and passed to the program through \configFile{inputfileAutoregressiveModel}{matrix}.
The sequence of normal equations $\mathbf{N}_t, \mathbf{n}_t$ are given as list of \configFile{inputfileNormalEquations}{normalEquation},
which can be generated using \configClass{loops}{loopType}.
In the same way, the \file{matrix files}{matrix} for \config{outputfileUpdatedState} and \config{inputfileUpdatedStateCovariance}
can also be specified using \configClass{loops}{loopType}.

If no \configFile{inputfileInitialState}{matrix} is set, a zero vector with appropriate dimensions is used.
The \configFile{inputfileInitialStateCovarianceMatrix}{matrix} however must be given.

See also \program{KalmanBuildNormals}, \program{KalmanSmoother}.
)";

/***********************************************/

#include "programs/program.h"
#include "parser/dataVariables.h"
#include "files/fileMatrix.h"
#include "files/fileNormalEquation.h"
#include "classes/timeSeries/timeSeries.h"
#include "misc/kalmanProcessing.h"

/***** CLASS ***********************************/

/** @brief Computes time variable gravity fields using Kalman filter approach
* @ingroup programsGroup */
class KalmanFilter
{
public:
  void run(Config &config, Parallel::CommunicatorPtr comm);
};

GROOPS_REGISTER_PROGRAM(KalmanFilter, SINGLEPROCESS, "Computes time variable gravity fields using Kalman filter approach", KalmanFilter, NormalEquation)

/***********************************************/

void KalmanFilter::run(Config &config, Parallel::CommunicatorPtr /*comm*/)
{
  try
  {
    std::vector<FileName> fileNameState, fileNameStateCovarianceMatrix;
    FileName fileNameInitialState, fileNameInitialCovariance;
    FileName fileNameArModel;
    std::vector<FileName> fileNameNormals;

    readConfig(config, "outputfileUpdatedState",                   fileNameState,                          Config::MUSTSET,   "kalman/updatedState/x_{loopTime:%D}.txt",                      "estimated state x+ (nx1-matrix)");
    readConfig(config, "outputfileUpdatedStateCovarianceMatrix",   fileNameStateCovarianceMatrix,          Config::OPTIONAL, "kalman/updatedStateCovariance/covariance_{loopTime:%D}.dat",   "estimated state' s covariance matrix Cov(x+)");
    readConfig(config, "inputfileNormalEquations",                 fileNameNormals,                        Config::MUSTSET,  "", "normal equations input file");
    readConfig(config, "inputfileInitialState",                    fileNameInitialState,                   Config::OPTIONAL,  "", "initial state x0");
    readConfig(config, "inputfileInitialStateCovarianceMatrix",    fileNameInitialCovariance,              Config::MUSTSET,   "", "initial state's covariance matrix Cov(x0)");
    readConfig(config, "inputfileAutoregressiveModel",             fileNameArModel,                        Config::MUSTSET,   "", "file name of autoregressive model");
    if(isCreateSchema(config)) return;

    // check input
    // -----------
    const UInt epochCount = fileNameNormals.size();

    if(fileNameState.size() != epochCount)
      throw(Exception("Number of solution file names and normal equations does not match (" + fileNameState.size()%"%i"s + " vs. " + epochCount%"%i"s + ")."));

    if( (fileNameStateCovarianceMatrix.size() > 0) && (fileNameStateCovarianceMatrix.size() != epochCount))
      throw(Exception("Number of covariance matrix file names and normal equations does not match (" + fileNameStateCovarianceMatrix.size()%"%i"s + " vs. " + epochCount%"%i"s + ")."));

    // set up ar model
    // ---------------
    logStatus<<"read autoregressive model from <"<<fileNameArModel<<">"<<Log::endl;
    Matrix tmp;
    readFileMatrix(fileNameArModel, tmp);
    AutoregressiveModel arModel(tmp);

    Matrix B, Q;
    arModel.orderOneRepresentation(B, Q);

    // load initial state:
    // -------------------
    logStatus<<"initialize state's covariance matrix with <"<<fileNameInitialCovariance<<">"<<Log::endl;
    Matrix updatedStateCovariance;
    readFileMatrix(fileNameInitialCovariance, updatedStateCovariance);

    Matrix updatedState(updatedStateCovariance.rows(),1);
    if(!fileNameInitialState.empty())
    {
      logStatus <<"initialize initial state with <"<<fileNameInitialState<<">"<< Log::endl;
      readFileMatrix(fileNameInitialState, updatedState);
    }

    // Run the filter:
    // ---------------
    for(UInt k = 0; k<fileNameNormals.size(); k++)
    {
      Matrix predictedState = B*updatedState;
      Matrix predictedStateCovariance = Q+B*updatedStateCovariance*B.trans();

      try
      {
        NormalEquationInfo info;
        Matrix N, n;
        readFileNormalEquation(fileNameNormals.at(k), info, N, n);

        inverse(predictedStateCovariance);
        updatedStateCovariance = predictedStateCovariance;
        axpy(1.0, N, updatedStateCovariance.slice(0, 0, N.rows(), n.rows()));
        inverse(updatedStateCovariance);
        fillSymmetric(updatedStateCovariance);

        matMult(-1.0, N, predictedState.row(0, N.rows()), n);
        updatedState = predictedState;
        matMult(1.0, updatedStateCovariance.column(0, n.rows()), n, updatedState);
      }
      catch(std::exception &e)
      {
        logWarning<<e.what()<<Log::endl;
        updatedState = predictedState;
        updatedStateCovariance = predictedStateCovariance;
      }

      logStatus <<"write updated state to <"<<fileNameState.at(k)<<">"<<Log::endl;
      writeFileMatrix(fileNameState.at(k), updatedState);
      if(!fileNameStateCovarianceMatrix.empty())
      {
        logStatus <<"write updated state covariance to <"<<fileNameStateCovarianceMatrix.at(k)<<">"<<Log::endl;
        writeFileMatrix(fileNameStateCovarianceMatrix.at(k), updatedStateCovariance);
      }
    }
  }
  catch(std::exception &e)
  {
    GROOPS_RETHROW(e)
  }
}

/***********************************************/