File: preprocessingVariationalEquationOrbitFit.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 (292 lines) | stat: -rw-r--r-- 12,582 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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
/***********************************************/
/**
* @file preprocessingVariationalEquationOrbitFit.cpp
*
* @brief Fit variational equations to orbit observations.
*
* @author Torsten Mayer-Guerr
* @date 2012-05-30
*
*/
/***********************************************/

// Latex documentation
#define DOCSTRING docstring
static const char *docstring = R"(
This program fits an \configFile{inputfileVariational}{variationalEquation} to an observed \configFile{inputfileOrbit}{instrument} by estimating parameters
in a least squares adjustment. Additional to the initial satellite state for each arc, these parameters can be
\configClass{parametrizationGravity}{parametrizationGravityType}, satellite \configClass{parametrizationAcceleration}{parametrizationAccelerationType}
and stochastic pulses (velocity jumps) at given times, \configClass{stochasticPulse}{timeSeriesType}. The estimated parameters can be stored with
\configFile{outputfileSolution}{matrix} and an extra file with the parameter names is created. The fitted orbit is written
as new reference in \configFile{outputfileVariational}{variationalEquation} and additionally in \configFile{outputfileOrbit}{instrument}.

The observed orbit positions (\configFile{inputfileOrbit}{instrument}) together with the epoch wise covariance matrix
(\configFile{inputfileCovariancePodEpoch}{instrument}) must be splitted in the same arcs as the variational equations but not
necessarily uniform distributed (use irregularData in \program{InstrumentSynchronize}). An iterative downweighting of
outliers is performed by M-Huber method.

The observation equations (parameter sensitity matrix) are computed by integration of the variational equations
(\configFile{inputfileVariational}{variationalEquation}) using a polynomial with \config{integrationDegree} and interpolated to the
observation epochs using a polynomial with \config{interpolationDegree}.

All parameters used here must be reestimated in the full least squares adjustment
for the gravity field determination to get a solution which is not biased towards the reference field.
The solution of additional estimations are relative (deltas) as the parameters are already used as Taylor point
in the reference orbit.

See also \program{PreprocessingVariationalEquation}.
)";

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

#include "programs/program.h"
#include "base/polynomial.h"
#include "files/fileMatrix.h"
#include "files/fileInstrument.h"
#include "files/fileVariationalEquation.h"
#include "files/fileParameterName.h"
#include "classes/ephemerides/ephemerides.h"
#include "classes/parametrizationGravity/parametrizationGravity.h"
#include "classes/parametrizationAcceleration/parametrizationAcceleration.h"
#include "classes/timeSeries/timeSeries.h"
#include "misc/varianceComponentEstimation.h"
#include "misc/observation/variationalEquationFromFile.h"

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

/** @brief Fit variational equations to orbit observations
* @ingroup programsGroup */
class PreprocessingVariationalEquationOrbitFit
{
public:
  VariationalEquationFromFile    variationalEquationFromFile;
  InstrumentFile                 podFile;
  InstrumentFile                 covPodEpochFile;
  EphemeridesPtr                 ephemerides;
  ParametrizationAccelerationPtr parameterAcceleration;
  ParametrizationGravityPtr      parameterGravity;
  UInt                           interpolationDegree;
  UInt                           arcCount;

  // normal equations
  // ----------------
  Matrix N;           // =A'PA, Normal matrix
  Vector n;           // =A'Pl, right hand side
  Double lPl;         // =l'Pl, weighted norm of the observations
  UInt   obsCount;    // number of observations
  UInt   outlierCount;
  Vector x;
  Double sigma0;

  void run(Config &config, Parallel::CommunicatorPtr comm);

  void buildNormals(UInt arcNo);
};

GROOPS_REGISTER_PROGRAM(PreprocessingVariationalEquationOrbitFit, PARALLEL, "fit variational equations to orbit observations", Preprocessing, VariationalEquation)

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

void PreprocessingVariationalEquationOrbitFit::run(Config &config, Parallel::CommunicatorPtr comm)
{
  try
  {
    FileName fileNameOutVariational, fileNameOutOrbit, fileNameOutSolution;
    FileName fileNameInVariational;
    FileName podName, covPodEpochName;
    UInt              integrationDegree;
    UInt              iterCount;
    std::vector<Time> stochasticPulse;
    TimeSeriesPtr     stochasticPulsePtr;

    renameDeprecatedConfig(config, "representation", "parametrizationGravity",      date2time(2020, 6, 3));
    renameDeprecatedConfig(config, "parameter",      "parametrizationAcceleration", date2time(2020, 6, 3));

    readConfig(config, "outputfileVariational",       fileNameOutVariational, Config::MUSTSET,  "",    "approximate position and integrated state matrix");
    readConfig(config, "outputfileOrbit",             fileNameOutOrbit,       Config::OPTIONAL, "",    "integrated orbit");
    readConfig(config, "outputfileSolution",          fileNameOutSolution,    Config::OPTIONAL, "",    "estimated calibration and state parameters");
    readConfig(config, "inputfileVariational",        fileNameInVariational,  Config::MUSTSET,  "",    "approximate position and integrated state matrix");
    readConfig(config, "inputfileOrbit",              podName,                Config::MUSTSET,  "",    "kinematic positions of satellite as observations");
    readConfig(config, "inputfileCovariancePodEpoch", covPodEpochName,        Config::OPTIONAL, "",    "3x3 epoch wise covariances");
    readConfig(config, "ephemerides",                 ephemerides,            Config::OPTIONAL, "jpl", "may be needed by parametrizationAcceleration");
    readConfig(config, "parametrizationGravity",      parameterGravity,       Config::DEFAULT,  "",    "gravity field parametrization");
    readConfig(config, "parametrizationAcceleration", parameterAcceleration,  Config::DEFAULT,  "",    "orbit force parameters");
    readConfig(config, "stochasticPulse",             stochasticPulsePtr,     Config::DEFAULT,  "",    "");
    readConfig(config, "integrationDegree",           integrationDegree,      Config::DEFAULT,  "7",   "integration of forces by polynomial approximation of degree n");
    readConfig(config, "interpolationDegree",         interpolationDegree,    Config::DEFAULT,  "7",   "orbit interpolation by polynomial approximation of degree n");
    readConfig(config, "iterationCount",              iterCount,              Config::DEFAULT,  "10",  "for the estimation of calibration parameter and error PSD");
    if(isCreateSchema(config)) return;

    if(integrationDegree%2 == 0)
      throw(Exception("polnomial degree for integration must be odd."));

    if(stochasticPulsePtr)
      stochasticPulse = stochasticPulsePtr->times();

    // init
    // ----
    podFile.open(podName);
    covPodEpochFile.open(covPodEpochName);
    InstrumentFile::checkArcCount({podFile, covPodEpochFile});
    variationalEquationFromFile.open(fileNameInVariational, parameterGravity, parameterAcceleration, stochasticPulse, ephemerides, integrationDegree);

    // =============================================

    x = Vector(variationalEquationFromFile.parameterCount());
    sigma0 = 1;
    for(UInt iter=0; iter<iterCount; iter++)
    {
      // build normals
      // -------------
      logStatus<<"accumulate normal equations"<<Log::endl;
      N            = Matrix(variationalEquationFromFile.parameterCount(), Matrix::SYMMETRIC);
      n            = Vector(variationalEquationFromFile.parameterCount());
      lPl          = 0;
      obsCount     = 0;
      outlierCount = 0;

      Parallel::forEach(podFile.arcCount(), [this](UInt arcNo) {buildNormals(arcNo);}, comm);

      Parallel::reduceSum(N,            0, comm);
      Parallel::reduceSum(n,            0, comm);
      Parallel::reduceSum(lPl,          0, comm);
      Parallel::reduceSum(obsCount,     0, comm);
      Parallel::reduceSum(outlierCount, 0, comm);

      // Estimate parameters
      // -------------------
      if(Parallel::isMaster(comm))
      {
        // Regularize unused parameters
        for(UInt i=0; i<N.rows(); i++)
          if(N(i,i) == 0)
            N(i,i) = 1.0;

        logStatus<<"solve system of normal equations"<<Log::endl;
        x = solve(N,n);
        sigma0 = Vce::standardDeviation(lPl-inner(n,x), obsCount-x.rows(), 2.5/*huber*/, 1./*huberPower*/);
        logInfo<<"  aposteriori sigma: "<<sigma0<<Log::endl;
        logInfo<<"  outlier "<<outlierCount<<" of "<<obsCount<<" ("<<100.*outlierCount/obsCount<<"%)"<<Log::endl;
      }

      Parallel::broadCast(outlierCount, 0, comm);
      if((iter>0) && (outlierCount==0))
        break;

      logInfo<<"  parameter count = "<<variationalEquationFromFile.parameterCount()<<Log::endl;
      Parallel::broadCast(x, 0, comm);
      Parallel::broadCast(sigma0, 0, comm);
    } // for(iter)

    if(Parallel::isMaster(comm) && !fileNameOutSolution.empty())
    {
      logStatus<<"write solution to <"<<fileNameOutSolution<<">"<<Log::endl;
      writeFileMatrix(fileNameOutSolution, x);

      std::vector<ParameterName> parameterName;
      variationalEquationFromFile.parameterName(parameterName);
      writeFileParameterName(fileNameOutSolution.replaceFullExtension(".parameterName.txt"), parameterName);
    }

    // =============================================================================

    // Improve orbits with estimated parameters
    // ----------------------------------------
    std::vector<VariationalEquationArc> arcs(variationalEquationFromFile.arcCount());
    Parallel::forEach(arcs, [this](UInt arcNo) {return variationalEquationFromFile.refineVariationalEquationArc(arcNo, x);}, comm);

    // =============================================================================

    if(Parallel::isMaster(comm) && !fileNameOutVariational.empty())
    {
      logStatus<<"write variational equation to file <"<<fileNameOutVariational<<">"<<Log::endl;
      writeFileVariationalEquation(fileNameOutVariational, variationalEquationFromFile.satellite(), arcs);
    }

    // =============================================

    if(Parallel::isMaster(comm) && !fileNameOutOrbit.empty())
    {
      logStatus<<"write orbit to file <"<<fileNameOutOrbit<<">"<<Log::endl;
      std::list<Arc> arcList;
      for(UInt arcNo=0; arcNo<arcs.size(); arcNo++)
        arcList.push_back( arcs.at(arcNo).orbitArc() );
      InstrumentFile::write(fileNameOutOrbit, arcList);
    }

    // =============================================
  }
  catch(std::exception &e)
  {
    GROOPS_RETHROW(e)
  }
}

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

void PreprocessingVariationalEquationOrbitFit::buildNormals(UInt arcNo)
{
  try
  {
    OrbitArc pod = podFile.readArc(arcNo);
    if(pod.size() == 0)
      return;

    Vector l(3*pod.size());
    for(UInt k=0; k<pod.size(); k++)
    {
      l(3*k+0) = pod.at(k).position.x();
      l(3*k+1) = pod.at(k).position.y();
      l(3*k+2) = pod.at(k).position.z();
    }

    std::vector<Time> timePod = pod.times();
    VariationalEquationFromFile::ObservationEquation eqn = variationalEquationFromFile.integrateArc(timePod.front(), timePod.back(), TRUE/*position*/, FALSE/*velocity*/);
    Polynomial polynomial(eqn.times, interpolationDegree);
    l -= polynomial.interpolate(timePod, eqn.pos0, 3); // reference orbit
    Matrix A = polynomial.interpolate(timePod, eqn.PosDesign, 3);

    // decorrelation
    Covariance3dArc covPod = covPodEpochFile.readArc(arcNo);
    Arc::checkSynchronized({pod, covPod});
    for(UInt i=0; i<covPod.size(); i++)
    {
      Matrix W = covPod.at(i).covariance.matrix();
      W.setType(Matrix::SYMMETRIC);
      cholesky(W);

      triangularSolve(1., W.trans(), l.row(3*i,3));
      triangularSolve(1., W.trans(), A.row(3*i,3));
    }

    // downweight outliers
    if(quadsum(x))
    {
      const Double huber = 2.5;
      Vector e = l;
      matMult(-1, A, x, e);
      for(UInt k=0; k<pod.size(); k++)
      {
        Double s = sqrt(quadsum(e.row(3*k,3))/3);
        if(s>huber*sigma0)
        {
          l.row(3*k,3) *= huber*sigma0/s;
          A.row(3*k,3) *= huber*sigma0/s;
          outlierCount += 3;
        }
      }
    }

    lPl += quadsum(l);
    obsCount += l.rows();
    rankKUpdate(1., A, N);
    matMult(1., A.trans(), l, n);
  }
  catch(std::exception &e)
  {
    GROOPS_RETHROW(e)
  }
}

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