-
Notifications
You must be signed in to change notification settings - Fork 1
/
openMA_modes_fit_with_errors.m
224 lines (200 loc) · 8.47 KB
/
openMA_modes_fit_with_errors.m
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
function [ alpha, cov_alpha, ux, uy ] = openMA_modes_fit_with_errors( varargin )
% OPENMA_MODES_FIT_WITH_ERRORS - fits modes to data with error propagation
%
% Usage: [alpha,cov_alpha,ux,uy] = openMA_modes_fit_with_errors( ...
% ux, uy, K, theta, magn, ...
% weight, covaraince_matrix )
% [alpha,cov_alpha,ux,uy] = openMA_modes_fit_with_errors( ...
% vfm, dfm, bm, K, cc, theta, ...
% magn, weight, covariance_matrix )
%
% vfm, dfm and bm are the vorticity-free, divergence-free and boundary mode structures
% produced by the openMA_pdetool solver functions. cc is a two column
% matrix of coordinates (in the dimensions of the modes themselves).
%
% ux and uy are matrices of the format produced by openMA_modes_interp -
% i.e. each column is a mode and each row is a grid point. Grid points
% must be the same as those found in magn.
%
% K is "homogenization term" introduced by Francois Lekien to avoid
% velocities becoming large. It can be a scalar, vector of the same
% length as the total number of modes (in which case, a diagonal matrix
% with those coefficients will be used) or a square matrix with the number
% of rows equal to the total number of modes. If it is empty, zero will
% be used for K. Note that this definition of K includes the maximum
% current value for each mode and the "constant K" (see Kaplan & Lekien
% paper).
%
% theta and magn indicate the direction (in radians measured in the normal
% cartesian sense) and magnitude along that direction of the currents at
% each of the grid points in cc (i.e. length(theta) == size(magn,1) ==
% size(cc,1)). Multiple columns of magn can be used for different
% timesteps.
%
% weight are the weights to apply to each of the components vectors. This
% can either be a scalar (same weight for all grip points) or a column
% vector of size(magn(:,1)). weight must be given, though it can be
% empty, in which case the weights are set to 1. A vector of weights should
% normally sum to the total number of grid points for the formulas to work
% out naturally.
%
% The covariance_matrix will be a set of variances and covarainces among
% measured data points for each timestep. This can take a number of
% forms: (1) a scalar, in which case a uniform error is assumed for all
% measured data. (2) a 2D matrix with the same number of rows as grid
% points and the same number of columns as timesteps, in which case it is
% assumed that each column represents the *variances* (i.e. not standard
% deviations). The covariances are assumed zero in this case. (3) a 3D
% matrix with the first two dimensions having the same size, that of the
% number of grid points, and the third dimension having the same size as
% the number of timesteps. In this case, covariance_matrix(:,:,k) is the
% full covariance matrix among data points for timestep k.
%
% The output argument alpha will be as in the other fit functions
% (size(alpha,1) = number of modes, size(alpha,2) = number of timesteps).
%
% cov_alpha is the covariances among the alpha's. It will have three
% dimensions - the first two of the same size as the number of modes, the
% last of the same size as the number of timesteps.
% diag(cov_alpha(:,:,k)) are the variances for each alpha at timestep k
% (the square-root of which would be the error estimation for the alpha's
% at timestep k).
%
% Unlike some of the other fit functions, this one does not allow
% multiple sets of theta, magn, weights, covariance_matrix for
% simplicity. If you have multiple currents components at each grid
% points (e.g. you are using totals data), then ux and uy (or cc), theta,
% magn, weights and covariance matrix should be appropriately vertically
% concatenated.
%
% Note that this function assumes all the velocity components are good
% data. If you have some missing data, use openMA_modes_fit_with_errors_NaNs
% instead.
%
% cc must be in the coordinate system of the modes. If you have lon,lat
% coordinates, use openMA_modes_interp_lonlat to get the coordinates in
% the modes coordinate system.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% $Id: openMA_modes_fit_with_errors.m 70 2007-02-22 02:24:34Z dmk $
%
% Copyright (C) 2005 David M. Kaplan
% Licence: GPL (Gnu Public License)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Find out type of input arguments - mode structures or matrices
if isa(varargin{1},'struct') | isempty( varargin{1} )
[K,cc] = deal( varargin{4:5} );
% This next line is usually very time consuming relative to the time it
% takes to fit data to the interpolated modes.
[ux,uy] = openMA_modes_interp( cc, varargin{1:3} );
varargin = varargin(6:end);
else
[ux,uy,K] = deal( varargin{1:3} );
varargin = varargin(4:end);
end
if length(varargin) ~= 4
error( 'Bad input arguments' )
end
if isempty(K), K = 0; end
% K is a scalar or vector
if prod(size(K)) == max(size(K))
K = diag(K);
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Get basic matrices that go into linear model.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[theta,magn,weights,covmat] = deal( varargin{:} );
% Deal with empty weights
if isempty( weights )
weights = 1;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Figure out what type of covariance matrix we were given
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if length(size(covmat)) == 3
% Must be full covariance matrix for each timestep
cmt = 3;
% Check to make sure size is correct
if ~all( size(covmat) == [ size(magn,1), size(magn,1), size(magn,2) ] )
error( 'covaraince matrix does not appear to be correct size. cmt=3' );
end
elseif prod(size(covmat)) == 1
% Scalar
cmt = 1;
else % Must be a 2D matrix - now need to figure out what that means.
if all( size(covmat) == size(magn) )
% Case where we have multiple timesteps, and each column are the
% variances for that timestep (covariances are zero).
cmt = 2;
elseif size(magn,2)==1 & ...
all( size(covmat) == [size(magn,1),size(magn,1)] )
% Case where we have one timestep, but use a full covariance matrix.
cmt = 3;
else
error( 'covaraince matrix does not appear to be correct size. cmt=2' );
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Components of modes along directions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
modes = ux .* repmat( cos(theta), [1,size(ux,2)] ) + ...
uy .* repmat( sin(theta), [1,size(uy,2)] );
clear theta
if nargout < 3, clear ux, end
if nargout < 4, clear uy, end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Remove points outside of domain.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
ii = any( isnan(modes), 2 );
if any( ii )
modes(ii,:) = [];
magn(ii,:) = [];
if prod(size(weights)) ~= 1
weights(ii,:) = [];
end
% Remove extra stuff from covmat
switch cmt
case 2
covmat(ii,:) = [];
case 3
covmat = covmat(~ii,~ii,:);
end
warning( 'openMA:openMA_modes_fit:data_outside_domain', ...
[ 'Some data points outside of domain or containing NaNs' ...
' eliminated. Watch out for weights!' ] );
end
q = size(modes,1); % Number of points with current components.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Linear model: modes' * weights * magn = ...
% ( modes' * weights * modes + q/2 * K ) * alpha
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Multiply modes and weights beforehand for speed.
if prod(size(weights))==1
weights = modes' * weights;
else
% repmat is MUCH quicker than using a diagonal matrix.
weights = modes' .* repmat( weights(:)', [size(modes,2),1] );
end
% now: weights = modes' * weights
T = (weights * modes + q/2 * K);
alpha = T \ ( weights * magn);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Loop over time steps and calculate cov_alpha for each one.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
cov_alpha = zeros( [ size(alpha,1), size(alpha,1), size(alpha,2) ] );
for n = 1:size(alpha,2)
switch cmt
case 1 % Scalar case.
covmagn = weights * covmat * weights';
case 2 % Only specifying variances
% Use repmat instead of normal matrix multiplaction for speed with
% diagonal matrices.
covmagn = (weights .* repmat( covmat(:,n)', [size(weights,1),1] ) ) * ...
weights';
case 3 % Full covariance matrix
covmagn = weights * covmat(:,:,n) * weights';
end
cov_alpha(:,:,n) = inv( T' * inv(covmagn) * T );
end