src/harmonic.h
Harmonic decomposition
The harmonic_decomposition()
function performs a continuous, least-square fit of the coefficients Z and (a_i,b_i) of the harmonic fonction \displaystyle
Z + \sum_i a_i \cos(\omega_i t) + b_i \sin(\omega_i t)
to the scalar field s
, with the \omega_i given as input.
If the optional argument e
is given, it is used to store the residual (i.e. the standard deviation) of the fit.
attribute {
struct {
double * omega;
scalar * A, * B, Z, E;
double ** M, ** Mn;
bool invertible;
} harmonic;
}
static double de (int n, double * ha, double * hx, double ** M)
{
double xm = ha[2*n];
double e = xm*(M[2*n][2*n]*xm - 2.*hx[2*n]);
for (int i = 0; i < n; i++) {
e += 2.*(ha[i]*(xm*M[i][2*n] - hx[i]) +
ha[n + i]*(xm*M[n + i][2*n] - hx[n + i]));
for (int j = 0; j < n; j++)
e += (ha[i]*ha[j]*M[j][i] +
ha[n + i]*ha[n + j]*M[n + j][n + i] +
2.*ha[i]*ha[n + j]*M[n + j][i]);
}
return e;
}
void harmonic_decomposition (scalar s, double t, double * omega, scalar e = {-1})
{
if (!s.harmonic.omega) {
int n = 0;
for (double * o = omega; *o > 0.; o++, n++);
s.harmonic.omega = malloc(n*sizeof(double));
memcpy (s.harmonic.omega, omega, n*sizeof(double));
s.harmonic.M = (double **) matrix_new (2*n + 1, 2*n + 1, sizeof (double));
s.harmonic.Mn = (double **) matrix_new (2*n + 1, 2*n + 1, sizeof (double));
for (int i = 0; i < 2*n + 1; i++)
for (int j = 0; j < 2*n + 1; j++) {
s.harmonic.M[i][j] = 0.;
s.harmonic.Mn[i][j] = (i == j);
}
s.harmonic.E = e;
scalar Z = new scalar;
s.harmonic.Z = Z;
s.harmonic.A = s.harmonic.B = NULL;
for (int i = 0; i < n; i++) {
scalar a = new scalar, b = new scalar;
char name[80];
snprintf (name, 79, "%s%da", s.name, i);
free (a.name); a.name = strdup (name);
snprintf (name, 79, "%s%db", s.name, i);
free (b.name); b.name = strdup (name);
s.harmonic.A = list_append (s.harmonic.A, a);
s.harmonic.B = list_append (s.harmonic.B, b);
}
reset (s.harmonic.A, 0.);
reset (s.harmonic.B, 0.);
s.harmonic.invertible = false;
}
scalar * A = s.harmonic.A, * B = s.harmonic.B;
scalar Z = s.harmonic.Z, E = s.harmonic.E;
double ** Mn = s.harmonic.Mn;
int n = 0;
for (scalar a in A)
n++;
double vsin[n], vcos[n];
for (int i = 0; i < n; i++) {
vsin[i] = sin (s.harmonic.omega[i]*t);
vcos[i] = cos (s.harmonic.omega[i]*t);
}
double ** M = s.harmonic.M;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
M[i][j] += vcos[j]*vcos[i];
M[i][n + j] += vsin[j]*vcos[i];
M[n + i][j] += vcos[j]*vsin[i];
M[n + i][n + j] += vsin[j]*vsin[i];
}
M[i][2*n] += vcos[i];
M[n + i][2*n] += vsin[i];
}
for (int j = 0; j < n; j++) {
M[2*n][j] += vcos[j];
M[2*n][n + j] += vsin[j];
}
M[2*n][2*n] += 1.;
double ** iM = (double **) matrix_new (2*n + 1, 2*n + 1, sizeof (double));
for (int i = 0; i < 2*n + 1; i++)
for (int j = 0; j < 2*n + 1; j++)
iM[i][j] = M[i][j];
if (!matrix_inverse (iM, 2*n + 1, 1e-6)) {
assert (!s.harmonic.invertible);
foreach() {
double x = s[];
scalar a, b;
int i = 0;
for (a,b in A,B) {
a[] += x*vcos[i];
b[] += x*vsin[i];
i++;
}
Z[] += x;
if (E.i >= 0)
E[] += x*x;
}
}
else {
foreach() {
double x = s[], sx2 = 0.;
/* A^n */
double ha[2*n + 1];
scalar a, b;
int i = 0;
for (a,b in A,B) {
ha[i] = a[];
ha[i + n] = b[];
i++;
}
ha[2*n] = Z[];
/* X^n = M^n.A^n */
double hx[2*n + 1];
for (int i = 0; i < 2*n + 1; i++) {
hx[i] = 0.;
for (int j = 0; j < 2*n + 1; j++)
hx[i] += Mn[i][j]*ha[j];
}
if (E.i >= 0) {
if (s.harmonic.invertible)
sx2 = x*x + Mn[2*n][2*n]*E[] - de (n, ha, hx, Mn);
else
sx2 = x*x + E[];
}
/* X^n+1 = X^n + Delta^n */
for (int i = 0; i < n; i++) {
hx[i] += x*vcos[i];
hx[i + n] += x*vsin[i];
}
hx[2*n] += x;
/* A^n+1 = (M^n+1)^-1.X^n+1 */
for (int i = 0; i < 2*n + 1; i++) {
ha[i] = 0.;
for (int j = 0; j < 2*n + 1; j++)
ha[i] += iM[i][j]*hx[j];
}
i = 0;
for (a,b in A,B) {
a[] = ha[i];
b[] = ha[i + n];
i++;
}
Z[] = ha[2*n];
if (E.i >= 0)
E[] = (sx2 + de (n, ha, hx, M))/M[2*n][2*n];
}
s.harmonic.invertible = true;
for (int i = 0; i < 2*n + 1; i++)
for (int j = 0; j < 2*n + 1; j++)
Mn[i][j] = M[i][j];
}
matrix_free (iM);
}
event cleanup (t = end)
{
for (scalar s in all)
if (s.harmonic.omega) {
free (s.harmonic.omega); s.harmonic.omega = NULL;
matrix_free (s.harmonic.M);
matrix_free (s.harmonic.Mn);
scalar Z = s.harmonic.Z;
delete ({Z});
delete (s.harmonic.A);
free (s.harmonic.A);
delete (s.harmonic.B);
free (s.harmonic.B);
}
}