Skip to content

Commit

Permalink
Merge branch 'percentiles'
Browse files Browse the repository at this point in the history
  • Loading branch information
JonKing93 committed Feb 18, 2020
2 parents 6927b26 + 06737f1 commit cbec8c7
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 14 deletions.
26 changes: 22 additions & 4 deletions 4. DA Analyses/@kalmanFilter/jointENSRF.m
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
function[output] = jointENSRF( M, D, R, F, w, yloc, meanOnly, fullDevs, reconstruct )
function[output] = jointENSRF( M, D, R, F, w, yloc, meanOnly, fullDevs, percentiles, reconstruct )
%% Implements an ensemble square root kalman filter updating observations jointly
%
% [output] = dash.jointENSRF( M, D, R, F, w, yloc, meanOnly, reconstruct )
% [output] = dash.jointENSRF( M, D, R, F, w, yloc, meanOnly, fullDevs, percentiles, reconstruct )
%
% ----- Inputs -----
%
Expand All @@ -22,6 +22,9 @@
%
% fullDevs: Whether to return full ensemble deviations. Scalar logical
%
% percentiles: Which percentiles to return. A vector of values between 0
% and 100 (nPerc x 1)
%
% reconstruct: Logical vector indicating which state vector elements to
% reconstruct. (nState x 1)
%
Expand All @@ -37,6 +40,8 @@
%
% Adev - Updated ensemble deviations (nState x nEns x nTime)
%
% Aperc - Percentiles of the updated ensemble. (nState x nPercentile x nTime)
%
% Ye - Proxy estimates (nObs x nEns)
%
% calibRatio - The calibration ratio. (nObs x nTime)
Expand All @@ -49,6 +54,7 @@

% Get sizes
[nObs, nTime] = size(D);
nPerc = numel( percentiles );
nEns = size(M,2);

% Preallocate PSM outputs and calibration ratio
Expand All @@ -74,6 +80,7 @@
elseif ~meanOnly
Avar = NaN( nState, nTime );
end
Aperc = NaN( nState, nPerc, nTime );

% Get (static) Kalman numerator. Clear M for space
[Mmean, Mdev] = dash.decompose( M );
Expand All @@ -92,13 +99,20 @@
Amean(:,t) = Mmean + K * ( D(obs,t) - Ymean(obs) );
calibRatio( obs, t ) = abs( D(obs,t) - Ymean(obs) ).^2 ./ ( diag(Kdenom) );

% Optionally update the deviations / variance
% Optionally update the deviations / variance / percentiles
if ~meanOnly
Ka = kalmanFilter.jointKalman( 'Ka', Knum(:,obs), Kdenom, R(obs,t) );
if fullDevs
Adev(:,:,t) = Mdev - Ka * Ydev(obs,:);
else
if nPerc > 0
Aperc(:,:,t) = Amean(:,t) + prctile( Adev(:,:,t), percentiles, 2 );
end
elseif nPerc == 0
Avar(:,t) = sum( (Mdev - Ka * Ydev(obs,:)).^2, 2) ./ (nEns-1);
else
Adev = Mdev - Ka * Ydev(obs,:);
Avar(:,t) = sum( Adev.^2, 2 ) ./ (nEns-1);
Aperc(:,:,t) = Amean(:,t) + prctile( Adev, percentiles, 2 );
end
end

Expand All @@ -117,6 +131,10 @@
elseif ~meanOnly
output.Avar = Avar;
end
if nPerc > 0
output.settings.percentiles = percentiles(:)';
output.Aperc = Aperc;
end
output.Ye = Ye;
output.calibRatio = calibRatio;
output.R = R;
Expand Down
8 changes: 5 additions & 3 deletions 4. DA Analyses/@kalmanFilter/kalmanFilter.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
append; % Whether to use the appended Ye method
meanOnly; % Whether to only calculate the ensemble mean
fullDevs; % Whether to return full ensemble deviations
percentiles; % Which percentiles of the ensemble to return
reconstruct; % Which state vector elements to reconstruct
reconH; % Whether all H indices are reconstructed
end
Expand Down Expand Up @@ -54,10 +55,11 @@
obj.append = false;
obj.meanOnly = false;
obj.fullDevs = false;
obj.percentiles = [];

% Block empty constructor, set values
if isempty(M) || isempty(D) || isempty(R) || isempty(F)
error('M, D, R, and F not be empty.');
error('M, D, R, and F cannot be empty.');
end
obj.setValues( M, D, R, F );

Expand Down Expand Up @@ -90,10 +92,10 @@
methods (Static)

% Serial updating scheme
[output] = serialENSRF( M, D, R, F, w, fullDevs );
[output] = serialENSRF( M, D, R, F, w, fullDevs, percentiles );

% Full inversion
[output] = jointENSRF( M, D, R, F, w, yloc, meanOnly, fullDevs, reconstruct );
[output] = jointENSRF( M, D, R, F, w, yloc, meanOnly, fullDevs, percentiles, reconstruct );

% Serial kalman gain
[K, a] = serialKalman( Mdev, Ydev, w, R );
Expand Down
7 changes: 5 additions & 2 deletions 4. DA Analyses/@kalmanFilter/run.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
%
% Adev - Updated ensemble deviations (nState x nEns x nTime)
%
% Aperc - Percentiles of the updated ensemble (nState x nPercentile x nTime)
% (See output.settings for the percentiles calculated)
%
% Ye - Proxy estimates
% Joint Updates: (nObs x nEns)
% Serial Updates: (nObs x nEns x nTime)
Expand Down Expand Up @@ -69,7 +72,7 @@
F = obj.adjustH( F, reconstruct );

% Do the updates
output = obj.serialENSRF( M, obj.D, obj.R, F, w, obj.fullDevs );
output = obj.serialENSRF( M, obj.D, obj.R, F, w, obj.fullDevs, obj.percentiles );

% Unappend if necessary
output.Append = false;
Expand All @@ -92,7 +95,7 @@
end

% Do the updates
output = obj.jointENSRF( M, obj.D, obj.R, obj.F, w, yloc, obj.meanOnly, obj.fullDevs, reconstruct );
output = obj.jointENSRF( M, obj.D, obj.R, obj.F, w, yloc, obj.meanOnly, obj.fullDevs, obj.percentiles, reconstruct );
end

end
21 changes: 20 additions & 1 deletion 4. DA Analyses/@kalmanFilter/serialENSRF.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function[output] = serialENSRF( M, D, R, F, w, fullDevs )
function[output] = serialENSRF( M, D, R, F, w, fullDevs, percentiles )
%% Implements an ensemble square root kalman filter with serial updates.
%
% [output] = dash.serialENSRF( M, D, R, F, w )
Expand All @@ -16,6 +16,12 @@
%
% w: Covariance localization weights. (nState x nObs)
%
% percentiles: A vector of values between 0 and 100 specifying which
% percentiles to return. (nPerc x 1)
%
% fullDevs: Scalar logical indicating whether to return full ensemble
% deviations.
%
% ----- Outputs -----
%
% output: A structure with the following fields
Expand All @@ -24,8 +30,12 @@
%
% Amean - The updated ensemble mean (nState x nTime)
%
% Adev - Updated ensemble deviations (nState x nEns x nTime)
%
% Avar - Updated ensemble variance (nState x nTime)
%
% Aperc - Percentiles of the updated ensemble. (nState x nPerc x nTime)
%
% Ye - Proxy estimates (nObs x nEns x nTime)
%
% calibRatio - The calibration ratio. (nObs x nTime)
Expand All @@ -39,6 +49,7 @@
% Get some sizes
[nObs, nTime] = size(D);
[nState, nEns] = size(M);
nPerc = numel( percentiles );

% Decompose the initial ensemble. Clear the ensemble to free memory.
[Mmean, Mdev] = dash.decompose(M);
Expand All @@ -51,6 +62,7 @@
else
Avar = NaN( nState, nTime );
end
Aperc = NaN( nState, nPerc, nTime );
Ye = NaN( nObs, nEns, nTime );
sites = false( nObs, nTime );
calibRatio = NaN( nObs, nTime );
Expand Down Expand Up @@ -90,6 +102,9 @@
else
Avar(:,t) = sum( Ad.^2, 2 ) ./ (nEns - 1);
end
if nPerc > 0
Aperc(:,:,t) = Am + prctile( Ad, percentiles, 2 );
end
progressbar(t/nTime);
end

Expand All @@ -105,6 +120,10 @@
else
output.Avar = Avar;
end
if nPerc > 0
output.settings.percentiles = percentiles(:)';
output.Aperc = Aperc;
end
output.Ye = Ye;
output.calibRatio = calibRatio;
output.R = R;
Expand Down
24 changes: 20 additions & 4 deletions 4. DA Analyses/@kalmanFilter/settings.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
% Specify whether to return full ensembles deviations, or just the
% variance. Default is just the variance.
%
% obj.settings( ..., 'percentiles', percentiles )
% Specify which percentiles of the ensemble to return
%
% obj.settings( ..., 'reconstruct', reconstruct )
% Specify which state vector elements to reconstruct. Not recommended. See
% "kalmanFilter.reconstructVars" instead.
Expand All @@ -50,14 +53,17 @@
% fullDevs: A scalar logical indicating whether to return full ensemble
% deviations. Default is false.
%
% percentiles: A vector of values between 0 and 100, specifying which
% ensemble percentiles to return.
%
% reconstruct: A logical vector specifying which state vector elements to
% reconstruct. (nState x 1)

% Parse inputs
[type, weights, inflate, append, meanOnly, fullDevs, recon] = parseInputs( varargin, ...
{'type','localize','inflate','append','meanOnly','returnDevs','reconstruct'}, ...
{obj.type, obj.localize, obj.inflate, obj.append, obj.meanOnly, obj.fullDevs, obj.reconstruct}, ...
{[],[],[],[],[],[],[]} );
[type, weights, inflate, append, meanOnly, fullDevs, percentiles, recon] = parseInputs( varargin, ...
{'type','localize','inflate','append','meanOnly','returnDevs','percentiles','reconstruct'}, ...
{obj.type, obj.localize, obj.inflate, obj.append, obj.meanOnly, obj.fullDevs, obj.percentiles, obj.reconstruct}, ...
{[],[],[],[],[],[],[],[]} );

% Error checking
if ~isstrflag(type)
Expand Down Expand Up @@ -128,6 +134,15 @@
end
end

% Percentiles
if ~isempty( percentiles )
if meanOnly
error('Cannot calculate ensemble percentiles when only updating the ensemble mean.');
elseif ~isnumeric(percentiles) || ~isvector(percentiles) || any(percentiles<0) || any(percentiles>100) || any(isnan(percentiles))
error('percentiles must be a vector of numeric values between 0 and 100 that do not contain NaN values.');
end
end

% Save values
obj.type = type;
obj.localize = weights;
Expand All @@ -136,6 +151,7 @@
obj.meanOnly = meanOnly;
obj.fullDevs = fullDevs;
obj.reconstruct = reconstruct;
obj.percentiles = percentiles;
obj.reconH = reconH;

end

0 comments on commit cbec8c7

Please sign in to comment.