7Eigen::MatrixXd EnsembleKalmanFilter(Eigen::MatrixXd prior,
8 Eigen::VectorXd measurements,
9 Eigen::MatrixXd measurementsCov,
10 Eigen::MatrixXd observedState)
12 M_Assert(measurements.rows() == observedState.rows(),
13 "The observed state should have the same dimention of the measurements");
14 M_Assert(observedState.cols() == prior.cols(),
15 "The input matrices should all have the samples on the columns");
16 M_Assert(measurementsCov.rows() == measurements.rows()
17 && measurementsCov.cols() == measurements.rows(),
18 "Wrong measurements covariance matrix");
19 unsigned Nseeds = prior.cols();
20 unsigned stateDim = prior.rows();
21 unsigned measDim = measurements.size();
22 Eigen::MatrixXd HA(measDim, Nseeds);
23 Eigen::VectorXd observedStateMean = observedState.rowwise().mean();
24 Eigen::MatrixXd A(stateDim, Nseeds);
25 Eigen::VectorXd priorMean = prior.rowwise().mean();
26 auto measNoise = std::make_shared<muq::Modeling::Gaussian>
27 (Eigen::VectorXd::Zero(measDim), measurementsCov);
28 Eigen::MatrixXd D(measDim, Nseeds);
30 for (
unsigned i = 0; i < Nseeds; i++)
32 A.col(i) = prior.col(i) - priorMean;
33 HA.col(i) = observedState.col(i) - observedStateMean;
34 D.col(i) = measurements + measNoise->Sample();
37 Eigen::MatrixXd Y = D -
39 Eigen::MatrixXd P = HA * HA.transpose() / (Nseeds - 1.) + measurementsCov;
40 Eigen::FullPivLU<Eigen::MatrixXd> lu_decomp(P);
41 auto P_rank = lu_decomp.rank();
43 if (P_rank < P.cols() || P_rank < P.rows())
45 Info <<
"Pseudo inverse of P should be implemented in the EnKF, exiting" <<
56 Eigen::MatrixXd M = P * Y;
57 Eigen::MatrixXd Z = (1. / (Nseeds - 1.)) * A * HA.transpose();
59 Eigen::MatrixXd K = (1. / (Nseeds - 1.)) * A * HA.transpose() * P;
61 if ((K.array() < 0.0).any())
63 Info <<
"WARNING: Kalman Gain is negative.\nK = \n" << K << endl <<
70Eigen::MatrixXd EnsembleKalmanFilter(PtrList<volScalarField>& prior,
71 Eigen::VectorXd measurements,
72 Eigen::MatrixXd measurementsCov,
73 Eigen::MatrixXd observedState)
75 M_Assert(measurements.rows() == observedState.rows(),
76 "The observed state should have the same dimention of the measurements");
77 M_Assert(observedState.cols() == prior.size(),
78 "The input matrices should all have the samples on the columns");
79 M_Assert(measurementsCov.rows() == measurements.rows()
80 && measurementsCov.cols() == measurements.rows(),
81 "Wrong measurements covariance matrix");
82 unsigned Nseeds = prior.size();
83 unsigned stateDim = prior[0].size();
84 unsigned measDim = measurements.size();
86 Eigen::MatrixXd HA(measDim, Nseeds);
87 Eigen::VectorXd observedStateMean = observedState.rowwise().mean();
88 Eigen::MatrixXd A(stateDim, Nseeds);
89 Eigen::VectorXd priorMean = priorMatrix.rowwise().mean();
90 auto measNoise = std::make_shared<muq::Modeling::Gaussian>
91 (Eigen::VectorXd::Zero(measDim), measurementsCov);
92 Eigen::MatrixXd D(measDim, Nseeds);
94 for (
unsigned i = 0; i < Nseeds; i++)
96 A.col(i) = priorMatrix.col(i) - priorMean;
97 HA.col(i) = observedState.col(i) - observedStateMean;
98 D.col(i) = measurements + measNoise->Sample();
101 Eigen::MatrixXd Y = D -
103 Eigen::MatrixXd P = HA * HA.transpose() / (Nseeds - 1.) + measurementsCov;
104 Eigen::FullPivLU<Eigen::MatrixXd> lu_decomp(P);
105 auto P_rank = lu_decomp.rank();
107 if (P_rank < P.cols() || P_rank < P.rows())
109 Info <<
"Pseudo inverse of P should be implemented in the EnKF, exiting" <<
120 Eigen::MatrixXd M = P * Y;
121 Eigen::MatrixXd Z = (1. / (Nseeds - 1.)) * A * HA.transpose();
123 Eigen::MatrixXd K = (1. / (Nseeds - 1.)) * A * HA.transpose() * P;
125 if ((K.array() < 0.0).any())
127 Info <<
"WARNING: Kalman Gain is negative.\nK = \n" << K << endl <<
131 return priorMatrix + Z * M;
134double quantile(Eigen::VectorXd samps,
double p,
int method)
137 int n = samps.size();
140 std::sort(samps.data(), samps.data() + samps.size());
145 j = std::floor(p * n + m);
146 double g = p * n + m - j * 1.0;
153 else if (method == 2)
157 j = std::floor(p * n + m);
158 double g = p * n + m - j * 1.0;
165 else if (method == 3)
169 j = std::floor(p * n + m);
170 double g = p * n + m - j * 1.0;
172 if (g == 0 && j % 2 == 0)
179 Info <<
"Quantile method not implemented. Exiting." << endl;
183 if (j >= samps.size() - 1)
185 j = samps.size() - 2;
188 return (1 - gamma) * samps(j) + gamma * samps(j + 1);
191Eigen::VectorXd quantile(Eigen::MatrixXd samps,
double p,
int method)
193 Eigen::VectorXd output(samps.rows());
195 for (
int i = 0; i < samps.rows(); i++)
197 Eigen::VectorXd sampsRow = samps.row(i);
198 output(i) = quantile(sampsRow, p, method);
static Eigen::MatrixXd PtrList2Eigen(PtrList< GeometricField< Type, PatchField, GeoMesh > > &fields, label Nfields=-1)
Convert a PtrList of snapshots to Eigen matrix (only internal field).
Header file of the muq2ithaca namespace.