File: synthesisSphericalHarmonicsMatrix.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 (149 lines) | stat: -rw-r--r-- 6,423 bytes parent folder | download
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
/***********************************************/
/**
* @file synthesisSphericalHarmonicsMatrix.cpp
*
* @brief Matrix for synthesizing spherical harmonics on a grid.
*
* @author Torsten Mayer-Guerr
* @date 2025-01-23
*/
/***********************************************/

// Latex documentation
#define DOCSTRING docstring
static const char *docstring = R"(
This program builds a linear operator matrix for spherical harmonic analysis or synthesis based on
the points defined in \configClass{grid}{gridType}. Depending on the chosen
\config{type} (synthesis, quadrature, or leastSquares), the resulting matrix can be used to:
\begin{itemize}
  \item \textbf{synthesis}: Map spherical harmonic coefficients to values on a grid,
  \item \textbf{quadrature}: Integrate grid-based functionals into spherical harmonic coefficients by
        a simple quadrature formula,
  \item \textbf{leastSquares}: Estimate coefficients from grid data via a least squares approach.
\end{itemize}

he spherical harmonic degree range is constrained by
\config{minDegree} and \config{maxDegree}, and the ordering of the coefficients is given by
\configClass{numbering}{sphericalHarmonicsNumberingType}. The reference gravitational
constant is \config{GM}, and the reference radius is \config{R}.

The computed matrix is written to \configFile{outputfileMatrix}{matrix} with dimensions
(number of grid points)~$\times$~(number of spherical harmonic coefficients). For
\config{type} = \emph{leastSquares}, the program applies a QR-based pseudo-inverse so that the
output matrix can directly form the normal-equation building blocks for a blockwise
least-squares solution in spherical harmonic space.

See also \program{Gravityfield2GriddedData}, \program{GriddedData2PotentialCoefficients},
\program{Gravityfield2SphericalHarmonicsVector}, and \program{MatrixCalculate} for additional
tools to convert between grids and spherical harmonics.
)";

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

#include "programs/program.h"
#include "base/sphericalHarmonics.h"
#include "files/fileMatrix.h"
#include "classes/grid/grid.h"
#include "classes/kernel/kernel.h"
#include "classes/sphericalHarmonicsNumbering/sphericalHarmonicsNumbering.h"

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

/** @brief Matrix for synthesizing spherical harmonics on a grid.
* @ingroup programsGroup */
class SynthesisSphericalHarmonicsMatrix
{
public:
  void run(Config &config, Parallel::CommunicatorPtr comm);
};

GROOPS_REGISTER_PROGRAM(SynthesisSphericalHarmonicsMatrix, PARALLEL, "matrix for synthesizing spherical harmonics on a grid", Misc, PotentialCoefficients, Matrix)

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

void SynthesisSphericalHarmonicsMatrix::run(Config &config, Parallel::CommunicatorPtr comm)
{
  try
  {
    FileName    fileNameOut;
    GridPtr     grid;
    KernelPtr   kernel;
    UInt        minDegree, maxDegree;
    Double      GM, R;
    SphericalHarmonicsNumberingPtr numbering;
    enum Type {SYNTHESIS, QUADRATURE, LEASTSQUARES};
    Type        type;
    std::string choice;

    readConfig(config, "outputfileMatrix", fileNameOut, Config::OPTIONAL, "",  "");
    readConfig(config, "grid",             grid,        Config::MUSTSET,  "",  "");
    readConfig(config, "kernel",           kernel,      Config::MUSTSET,  "",  "");
    readConfig(config, "minDegree",        minDegree,   Config::DEFAULT,  "0", "");
    readConfig(config, "maxDegree",        maxDegree,   Config::MUSTSET,  "",  "");
    readConfig(config, "GM",               GM,          Config::DEFAULT,  STRING_DEFAULT_GM, "Geocentric gravitational constant");
    readConfig(config, "R",                R,           Config::DEFAULT,  STRING_DEFAULT_R,  "reference radius");
    readConfig(config, "numbering",        numbering,   Config::MUSTSET,  "",  "numbering scheme of sh coefficients");
    if(readConfigChoice(config, "type", choice, Config::MUSTSET, "", ""))
    {
      if(readConfigChoiceElement(config, "synthesis",    choice, "synthesize spherical harmonics on a grid")) type = SYNTHESIS;
      if(readConfigChoiceElement(config, "quadrature",   choice, "calculate spherical harmonics from grid"))  type = QUADRATURE;
      if(readConfigChoiceElement(config, "leastSquares", choice, "estimated spherical harmonics from grid"))  type = LEASTSQUARES;
      endChoice(config);
    }
    if(isCreateSchema(config)) return;

    std::vector<std::vector<UInt>> idxC, idxS;
    numbering->numbering(maxDegree, minDegree, idxC, idxS);

    logStatus<<"calcalute matrix for "<<numbering->parameterCount(maxDegree, minDegree)<<" coefficients"<<Log::endl;
    Matrix A = Matrix(grid->points().size(), numbering->parameterCount(maxDegree, minDegree));
    Parallel::forEach(grid->points().size(), [&](UInt k)
    {
      Matrix Cnm, Snm;
      Vector factors;
      SphericalHarmonics::CnmSnm(1./R * grid->points().at(k), maxDegree, Cnm, Snm, (type == QUADRATURE)/*isInterior*/);
      if(type == QUADRATURE)
        factors = grid->points().at(k).r()/(4*PI*GM) * grid->areas().at(k) * kernel->coefficients(grid->points().at(k), maxDegree);
      else
        factors = GM/R * kernel->inverseCoefficients(grid->points().at(k), maxDegree);

      for(UInt n=minDegree; n<=maxDegree; n++)
      {
        if(idxC[n][0]!=NULLINDEX) A(k, idxC[n][0]) = factors(n) * Cnm(n,0);
        for(UInt m=1; m<=n; m++)
        {
          if(idxC[n][m]!=NULLINDEX) A(k, idxC[n][m]) = factors(n) * Cnm(n,m);
          if(idxS[n][m]!=NULLINDEX) A(k, idxS[n][m]) = factors(n) * Snm(n,m);
        }
      }
    }, comm);
    Parallel::reduceSum(A, 0, comm);
    if(!Parallel::isMaster(comm))
      return;

    // A' := (A'PA)^(-1) A'P
    if(type == LEASTSQUARES)
    {
      logStatus<<"compute pseudo inverse"<<Log::endl;
      for(UInt k=0; k<grid->points().size(); k++)
        A.row(k) *= std::sqrt(grid->areas().at(k));
      const Vector tau = QR_decomposition(A);
      const Matrix R   = A.row(0, A.columns());
      generateQ(A, tau);
      triangularSolve(1., R, A.trans());
      for(UInt k=0; k<grid->points().size(); k++)
        A.row(k) *= std::sqrt(grid->areas().at(k));
    }

    // write
    // -----
    logStatus<<"write matrix to file <"<<fileNameOut<<">"<<Log::endl;
    writeFileMatrix(fileNameOut, (type == SYNTHESIS) ? A : A.trans());
  }
  catch(std::exception &e)
  {
    GROOPS_RETHROW(e)
  }
}

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