1% Form the inverse of the background error covariance matrix.
2%
3% s = divand_background(mask,pmn,Labs,alpha,moddim)
4%
5% Form the inverse of the background error covariance matrix with
6% finite-difference operators on a curvilinear grid
7%
8% Input:
9%   mask: binary mask delimiting the domain. 1 is inside and 0 outside.
10%         For oceanographic application, this is the land-sea mask.
11%
12%   pmn: scale factor of the grid.
13%
14%   Labs: correlation length
15%
16%   alpha: a dimensional coefficients for norm, gradient, laplacian,...
17%      alpha is usually [1 2 1].
18%
19%
20% Output:
21%   s: stucture containing
22%   s.iB: inverse of the background error covariance
23%   s.L: spatial average correlation length
24%   s.n: number of dimenions
25%   s.coeff: scaling coefficient such that the background variance
26%     diag(inv(iB)) is one far away from the boundary.
27
28function s = divand_background(mask,pmn,Labs,alpha,moddim,mapindex)
29
30sz = size(mask);
31% number of dimensions
32pmn = cat_cell_array(pmn);
33n = size(pmn,ndims(pmn));
34
35if isempty(moddim)
36  moddim = zeros(1,n);
37end
38
39if nargin == 5
40    mapindex = [];
41end
42
43iscyclic = moddim > 0;
44
45
46if isscalar(Labs)
47    Labs = Labs * ones([size(mask) n]);
48elseif iscell(Labs)
49    for i=1:n
50        if isscalar(Labs{i})
51            Labs{i} = Labs{i} * ones(size(mask));
52        else
53            if ~isequal(size(mask),size(Labs{i}))
54                error('mask (%s) and correlation length (%s) have incompatible size',...
55                      formatsize(size(mask)),formatsize(size(Labs{i})));
56            end
57        end
58    end
59
60    Labs = cat_cell_array(Labs);
61else
62    if ~isequal(size(mask),size(Labs))
63        error('mask (%s) and correlation length (%s) have incompatible size',...
64              formatsize(size(mask)),formatsize(size(Labs)));
65    end
66
67    Labs = repmat(Labs,[ones(1,ndims(mask)) n]);
68end
69
70if isempty(alpha)
71  % kernel should has be continuous derivative
72
73  % highest derivative in cost function
74  m = ceil(1+n/2);
75
76  % alpha is the (m+1)th row of the Pascal triangle:
77  % m=0         1
78  % m=1       1   1
79  % m=1     1   2   1
80  % m=2   1   3   3   1
81  % ...
82
83  alpha = arrayfun (@(k) nchoosek(m,k), 0:m);
84end
85
86%if ~isequal([size(mask) n],size(pmn))
87%  error('mask (%s) and metric (%s) have incompatible size',formatsize(size(mask)),formatsize(size(pmn)));
88%end
89
90s = divand_operators(mask,pmn,Labs.^2,iscyclic,mapindex);
91D = s.D; % laplacian (a dimensional, since nu = Labs.^2)
92sv = s.sv;
93n = s.n;
94
95
96% Labsp: 1st index represents the dimensions
97Labsp = permute(Labs,[n+1 1:n]);
98pmnp = permute(pmn,[n+1 1:n]);
99
100% must handle the case when Labs is zero in some dimension
101% thus reducing the effective dimension
102
103% mean correlation length in every dimension
104Ld = mean(reshape(Labsp,[n sv.numels_all]),2);
105neff = sum(Ld > 0);
106%L = mean(Ld(Ld > 0));
107
108% geometric mean
109L = geomean(Ld(Ld > 0));
110
111
112% norm taking only dimension into account with non-zero correlation
113% WE: units length^(neff/2)
114d = prod(pmnp(find(Ld > 0),:),1)';
115WE = sparse_diag(statevector_pack(sv,1./sqrt(d)));
116
117
118% scale iB such that the diagonal of inv(iB) is 1 far from
119% the boundary
120% we use the effective dimension neff to take into account that the
121% correlation length-scale might be zero in some directions
122
123Ln = prod(Ld(Ld > 0));
124
125if any(Ld <= 0)
126   pmnd = mean(reshape(pmnp,[n sv.numels_all]),2);
127   %Ln = Ln * prod(pmnd(Ld <= 0));
128end
129
130
131try
132  coeff = divand_kernel(neff,alpha) * Ln; % units length^n
133catch ME
134  if strcmp(ME.identifier, 'divand:divand_kernel:unsupported_alpha')
135      warning('divand:divand_background:noscaling','no scaling');
136      coeff = 1;
137  else
138      rethrow(ME);
139  end
140end
141
142pmnv = reshape(pmn,numel(mask),n);
143pmnv(:,Ld == 0) = 1;
144
145% staggered version of norm
146
147for i=1:n
148  S = sparse_stagger(sz,i,iscyclic(i));
149  ma = (S * mask(:) == 1);
150  d = sparse_pack(ma) * prod(S * pmnv,2);
151  d = 1./d;
152  s.WEs{i} = sparse_diag(sqrt(d));
153end
154
155% staggered version of norm scaled by length-scale
156
157s.WEss = cell(n,1);
158s.Dxs = cell(n,1);
159for i=1:n
160  Li2 = Labsp(i,:).^2;
161
162  S = sparse_stagger(sz,i,iscyclic(i));
163  % mask for staggered variable
164  m = (S * mask(:) == 1);
165
166  tmp = sparse_pack(m) * sqrt(S*Li2(:));
167
168  s.WEss{i} = sparse_diag(tmp) * s.WEs{i};
169%  s.Dxs{i} = sparse_diag(sqrt(tmp)) * s.Dx{i};
170end
171
172% adjust weight of halo points
173if ~isempty(mapindex)
174  % ignore halo points at the center of the cell
175
176  WE = sparse_diag(s.isinterior) * WE;
177
178  % divide weight be two at the edged of halo-interior cell
179    % weight of the grid points between halo and interior points
180    % are 1/2 (as there are two) and interior points are 1
181  for i=1:n
182    s.WEs{i} = sparse_diag(sqrt(s.isinterior_stag{i})) * s.WEs{i};
183  end
184end
185
186s.WE = WE;
187s.coeff = coeff;
188% number of dimensions
189s.n = n;
190
191[iB_,iB] = divand_background_components(s,alpha);
192
193if 0
194iB_ = cell(length(alpha),1);
195
196% constrain of total norm
197
198iB_{1} =  (1/coeff) * (WE'*WE);
199
200
201% loop over all derivatives
202
203for j=2:length(alpha)
204    % exponent of laplacian
205    k = floor((j-2)/2);
206
207    if mod(j,2) == 0
208        % constrain of derivative with uneven order (j-1)
209        % (gradient, gradient*laplacian,...)
210        % normalized by surface
211
212        iB_{j} = sparse(size(D,1),size(D,1));
213
214        for i=1:n
215            Dx = s.WEss{i} * s.Dx{i} * D^k;
216
217            iB_{j} = iB_{j} + Dx'*Dx;
218        end
219    else
220        % constrain of derivative with even order (j-1)
221        % (laplacian, biharmonic,...)
222
223        % normalize by surface of each cell
224        % such that inner produces (i.e. WE'*WE)
225        % become integrals
226        % WD: units length^(n/2)
227
228        WD = WE * D^(k+1);
229        iB_{j} = WD'*WD;
230    end
231
232    iB_{j} = iB_{j}/coeff;
233end
234
235
236% sum all terms of iB
237% iB is adimentional
238iB = alpha(1) * iB_{1};
239for j=2:length(alpha)
240  iB = iB + alpha(j) * iB_{j};
241end
242
243s.coeff = coeff;
244end
245
246% inverse of background covariance matrix
247s.iB = iB;
248
249% individual terms of background covariance matrix (corresponding alpha)
250s.iB_ = iB_;
251
252s.L = L;
253s.Ln = Ln;
254
255s.moddim = moddim;
256s.iscyclic = iscyclic;
257
258s.alpha = alpha;
259s.neff = neff;
260s.WE = WE; % units length^(n/2)
261
262% Copyright (C) 2014 Alexander Barth <a.barth@ulg.ac.be>
263%
264% This program is free software; you can redistribute it and/or modify it under
265% the terms of the GNU General Public License as published by the Free Software
266% Foundation; either version 2 of the License, or (at your option) any later
267% version.
268%
269% This program is distributed in the hope that it will be useful, but WITHOUT
270% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
271% FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
272% details.
273%
274% You should have received a copy of the GNU General Public License along with
275% this program; if not, see <http://www.gnu.org/licenses/>.
276