0001 function J= jacobian_adjoint_2p5d_1st_order( fwd_model, img)
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027 if ischar(fwd_model) && strcmp(fwd_model,'UNIT_TEST'); do_unit_test; return; end
0028
0029 if nargin == 1
0030 img= fwd_model;
0031 elseif strcmp(getfield(warning('query','EIDORS:DeprecatedInterface'),'state'),'on')
0032 warning('EIDORS:DeprecatedInterface', ...
0033 ['Calling JACOBIAN_ADJOINT with two arguments is deprecated and will cause' ...
0034 ' an error in a future version. First argument ignored.']);
0035 end
0036
0037 img = data_mapper(img);
0038 if ~ismember(img.current_params, supported_params)
0039 error('EIDORS:PhysicsNotSupported', '%s does not support %s', ...
0040 'JACOBIAN_ADJOINT',img.current_params);
0041 end
0042
0043 org_params = img.current_params;
0044
0045 img = convert_img_units(img, 'conductivity');
0046
0047 img.elem_data = check_elem_data(img);
0048
0049 fwd_model= img.fwd_model;
0050
0051 pp= fwd_model_parameters( fwd_model, 'skip_VOLUME' );
0052 gnd = img.fwd_model.gnd_node;
0053
0054 img.fwd_model.system_mat = @system_mat_2p5d_1st_order;
0055 gnd = fwd_model.gnd_node;
0056 img.fwd_model.system_mat_2p5d_1st_order.k = 0;
0057 img.fwd_model.system_mat_2p5d_1st_order.factory = 1;
0058
0059 pp.Sf = system_mat_2p5d_1st_order( img );
0060 pp.FC= system_mat_fields( fwd_model );
0061 pp.FT= system_mat_2p5d_fields( fwd_model );
0062
0063 c2f = speye(pp.n_elem); try c2f = img.fwd_model.coarse2fine; end
0064 k = [0 Inf]; try k = img.fwd_model.jacobian_adjoint_2p5d_1st_order.k; end
0065 method = 'quadv'; try method = img.fwd_model.jacobian_adjoint_2p5d_1st_order.method; end
0066
0067 if length(k) == 1
0068 J = 2*jacobian_k(k, pp, gnd, img.fwd_model.stimulation, c2f);
0069 else
0070 switch method
0071 case 'trapz'
0072
0073 trace = 0;
0074 if trace; fprintf('%8s %12s %16s %16s\n','fcnt','a','b-a','||Q||'); end
0075 n = 0; kil = k(1);
0076 tol = 1e-8;
0077 k(isinf(k)) = tol^(-1/6);
0078 Jf = zeros(pp.n_meas, size(c2f,2), length(k));
0079 for ki = k
0080 n = n + 1;
0081 Jf(:,:,n) = jacobian_k(ki, pp, gnd, img.fwd_model.stimulation, c2f);
0082 if trace; fprintf('%8d %12e %16e %16e\n',n,ki,ki-kil,norm(Jf(1,1,n))); kil = ki; end
0083 end
0084 J=2/pi*trapz(k,Jf,3);
0085
0086 Jff = squeeze(reshape(Jf,pp.n_meas*size(c2f,2),1,length(k)));
0087 assert(max(abs(Jff(:,end)) < tol), sprintf('trapz k=%e truncated too early as k->Inf',k(end)));
0088 slope = (Jff(:,3)-Jff(:,2))./Jff(:,2); slope(Jff(:,2) == 0) = nan;
0089
0090
0091 assert(median(abs(slope)) < sqrt(tol)*10, sprintf('trapz k->0 (tol=%e), slope=%e != 0',tol,median(abs(slope))));
0092 if 0
0093 clf;semilogx(repmat(k,pp.n_meas*size(c2f,2),1)',Jff');
0094 xlabel('k'); ylabel('J_k'); title('J_k')
0095 drawnow; pause(1);
0096 end
0097 clear Jff;
0098 clear slope;
0099 case 'quadv'
0100
0101 trace = 0;
0102 tol = norm(jacobian_k(0, pp, gnd, img.fwd_model.stimulation, 1))*1e-2;
0103 kend = min(tol^(-1/6), max(k));
0104
0105
0106 if trace; fprintf('%8s %12s %16s %16s\n','fcnt','a','b-a','Q(1)'); end
0107 J=2/pi*quadv(@(kk) jacobian_k(kk, pp, gnd, img.fwd_model.stimulation, c2f), k(1), kend, tol, trace);
0108 case 'integral'
0109 reltol = 1e-8;
0110 tol = norm(jacobian_k(0, pp, gnd, img.fwd_model.stimulation, 1))*reltol;
0111 kend = min(tol^(-1/6), max(k));
0112 opts = {'ArrayValued', true,
0113 'AbsTol', tol,
0114 'RelTol',reltol};
0115 opts = opts';
0116
0117
0118 J=2/pi*integral(@(kk) jacobian_k(kk, pp, gnd, img.fwd_model.stimulation, c2f), k(1), kend, 'ArrayVAlued', true);
0119 otherwise
0120 error(['unrecognized method: ' method]);
0121 end
0122 end
0123
0124 if ~strcmp(org_params,'conductivity')
0125 J = apply_chain_rule(J, img, org_params);
0126 if isfield(fwd_model, 'coarse2fine') && ...
0127 size(img.elem_data,1)==size(fwd_model.coarse2fine,1)
0128 J=J*fwd_model.coarse2fine;
0129 nparam = size(fwd_model.coarse2fine,2);
0130 end
0131 end
0132
0133
0134 if ~strcmp(org_params,'conductivity') || isfield(img, org_params)
0135 img = rmfield(img,'elem_data');
0136 img.current_params = [];
0137 end
0138
0139
0140 if pp.normalize
0141 data= fwd_solve( img );
0142 J= J ./ (data.meas(:)*ones(1,nparam));
0143
0144 end
0145
0146 function J = jacobian_k(k, pp, gnd, stim, c2f)
0147 SS = pp.Sf(k);
0148 idx = 1:size(SS,1);
0149 idx( gnd ) = [];
0150
0151 sv = potential_k(SS, pp, gnd);
0152
0153 zi2E= zeros(pp.n_elec, pp.n_node);
0154 zi2E(:, idx)= -pp.N2E(:,idx)/ SS(idx,idx);
0155
0156
0157 DE = jacobian_calc(pp, zi2E, pp.FC, k*pp.FT, sv, c2f);
0158
0159 J = zeros( pp.n_meas, size(c2f,2) );
0160 idx=0;
0161 for j= 1:pp.n_stim
0162 meas_pat= stim(j).meas_pattern;
0163 n_meas = size(meas_pat,1);
0164 DEj = reshape( DE(:,j,:), pp.n_elec, size(c2f,2) );
0165 J( idx+(1:n_meas),: ) = meas_pat*DEj;
0166 idx= idx+ n_meas;
0167 end
0168
0169 function v = potential_k(S, pp, gnd)
0170 idx = 1:size(S,1);
0171 idx( gnd ) = [];
0172 idx2 = find(any(pp.N2E));
0173 v = zeros(pp.n_node, pp.n_stim);
0174 v(idx,:) = left_divide( S(idx,idx), 1/2*pp.QQ(idx,:) );
0175
0176
0177
0178
0179
0180 function DE = jacobian_calc(pp, zi2E, FC, FT, sv, c2f)
0181 d0 = pp.n_dims + 0;
0182 d1 = pp.n_dims + 1;
0183
0184 zi2E_FCt = zi2E * FC';
0185 zi2E_FTt = zi2E * FT';
0186
0187 FC_sv = FC * sv;
0188 FT_sv = FT * sv;
0189
0190 DE= zeros(pp.n_elec, pp.n_stim, size(c2f,2) );
0191 de0= pp.n_elem * d0;
0192 de1= pp.n_elem * d1;
0193 if all(all(speye(size(c2f)) == c2f))
0194 for k= 1:size(c2f,2);
0195 idx = d0*(k-1)+1 : d0*k;
0196 dq1= zi2E_FCt(:,idx) * FC_sv(idx,:);
0197 dq2= zi2E_FTt(:,idx) * FT_sv(idx,:);
0198 DE(:,:,k)= dq1 + dq2;
0199 end
0200 elseif 0
0201 for k= 1:size(c2f,2);
0202 chg_col = kron( c2f(:,k), ones(d0,1));
0203 dDD_dEj = spdiags(chg_col, 0, de0, de0);
0204 dq1 = zi2E_FCt * dDD_dEj * FC_sv;
0205 chg_col = kron( c2f(:,k), ones(d1,1));
0206 dDD_dEj = spdiags(chg_col, 0, de1, de1);
0207 dq2 = zi2E_FTt * dDD_dEj * FT_sv;
0208 DE(:,:,k)= dq1 + dq2;
0209 end
0210 else
0211 for k= 1:size(c2f,2);
0212 ff = find( c2f(:,k) > 1e-8 );
0213 lff= length(ff)*d0;
0214 ff1= ones(d0,1) * ff(:)';
0215 ffd= d0*ff1 + (-(d0-1):0)'*ones(1,length(ff));
0216 dDD_dEj = spdiags(c2f(ff1,k), 0, lff, lff);
0217 dq1= zi2E_FCt(:,ffd) * dDD_dEj * FC_sv(ffd,:);
0218 lff= length(ff)*d1;
0219 ff1= ones(d1,1) * ff(:)';
0220 ffd= d1*ff1 + (-(d1-1):0)'*ones(1,length(ff));
0221 dDD_dEj = spdiags(c2f(ff1,k), 0, lff, lff);
0222 dq2= zi2E_FTt(:,ffd) * dDD_dEj * FT_sv(ffd,:);
0223 DE(:,:,k)= dq1 + dq2;
0224 end
0225 end
0226
0227 function J = apply_chain_rule(J, img, org_params)
0228 switch(org_params)
0229 case 'resistivity'
0230 dCond_dPhys = -img.elem_data.^2;
0231 case 'log_resistivity'
0232 dCond_dPhys = -img.elem_data;
0233 case 'log_conductivity'
0234 dCond_dPhys = img.elem_data;
0235 otherwise
0236 error('not implemented yet')
0237 end
0238 J = J.*repmat(dCond_dPhys ,1,size(J,1))';
0239
0240 function elem_data = check_elem_data(img)
0241 elem_data = img.elem_data;
0242 sz_elem_data = size(elem_data);
0243 if sz_elem_data(2) ~= 1;
0244 error('jacobian_adjoin: can only solve one image (sz_elem_data=%)', ...
0245 sz_elem_data);
0246 end
0247
0248 if isfield(img.fwd_model, 'coarse2fine');
0249 c2f = img.fwd_model.coarse2fine;
0250 sz_c2f = size(c2f);
0251 switch sz_elem_data(1)
0252 case sz_c2f(1);
0253 case sz_c2f(2); elem_data = c2f * elem_data;
0254 otherwise; error(['jacobian_adjoint: provided elem_data ' ...
0255 ' (sz=%d) does not match c2f (sz=%d %d)'], sz_elem_data(1), sz_c2f);
0256 end
0257 else
0258 if sz_elem_data(1) ~= num_elems(img.fwd_model)
0259 error(['jacobian_adjoint: provided elem_data (sz=%d) does ' ...
0260 ' not match fwd_model (sz=%d)'], sz_elem_data(1), num_elems(sz_c2f));
0261 end
0262 end
0263
0264 function str = supported_params
0265 str = {'conductivity'
0266 'resistivity'
0267 'log_conductivity'
0268 'log_resistivity'};
0269
0270 function do_unit_test()
0271 imdl2 = mk_geophysics_model('h22c',16);
0272 assert(length(imdl2.rec_model.elems) > 15, 'expect sufficient rec_model density');
0273 img2 = mk_image(imdl2,1);
0274 if 0
0275 clf; subplot(121); show_fem(imdl2.fwd_model); subplot(122); show_fem(imdl2.rec_model);
0276 c2f=imdl2.fwd_model.coarse2fine; img2r = mk_image(imdl2); img2r.fwd_model = imdl2.rec_model; img2r.elem_data = zeros(size(c2f,2),1);
0277 for i=1:size(c2f,2)
0278 subplot(121);
0279 img2.elem_data = c2f(:,i); show_fem(img2); title(sprintf('%d',i));
0280 subplot(122);
0281 img2r.elem_data(:) = 0; img2r.elem_data(i)=1; show_fem(img2r); title(sprintf('%d',i));
0282 drawnow; pause(0.25);
0283 end
0284 end
0285
0286
0287
0288
0289 imdl3 = mk_geophysics_model('h32c',16);
0290 for s = {'nodes', 'elems', 'boundary', 'name','electrode'}
0291 imdl3.rec_model.(s{:}) = imdl2.rec_model.(s{:});
0292 end
0293 disp('recalculating 3D c2f, so that coarse meshes agree (2D vs 3D)');
0294 [c2f,bkgnd] = mk_approx_c2f(imdl3.fwd_model, imdl3.rec_model);
0295 imdl3.fwd_model.coarse2fine = c2f;
0296 imdl3.fwd_model.background = bkgnd;
0297 img3 = mk_image(imdl3,1);
0298 if 0
0299 clf; subplot(121); show_fem(imdl3.fwd_model); subplot(122); show_fem(imdl3.rec_model);
0300 c2f=imdl3.fwd_model.coarse2fine; img3r = mk_image(imdl3); img3r.fwd_model = imdl3.rec_model; img3r.elem_data = zeros(size(c2f,2),1);
0301 for i=1:size(c2f,2)
0302 subplot(121);
0303 img3.elem_data = c2f(:,i); show_fem(img3); title(sprintf('%d',i));
0304 subplot(122);
0305 img3r.elem_data(:) = 0; img3r.elem_data(i)=1; show_fem(img3r); title(sprintf('%d',i));
0306 drawnow; pause(0.25);
0307 end
0308 end
0309
0310 t = tic;
0311 eidors_cache clear_name jacobian
0312 J2 = calc_jacobian(img2);
0313 fprintf(' 2D = %.2f sec\n', toc(t));
0314
0315 t = tic;
0316 eidors_cache clear_name jacobian
0317 img2.fwd_model.jacobian = @jacobian_adjoint_2p5d_1st_order;
0318 img2.fwd_model.jacobian_adjoint_2p5d_1st_order.k = 0;
0319 J2p50 = calc_jacobian(img2);
0320 fprintf(' 2.5D (k=0) = %.2f sec\n', toc(t));
0321
0322 ke = [0 0 0];
0323 t = tic;
0324 eidors_cache clear_name jacobian
0325 img2.fwd_model.jacobian = @jacobian_adjoint_2p5d_1st_order;
0326 img2.fwd_model.jacobian_adjoint_2p5d_1st_order.k = [0 logspace(-4,1,100)];
0327 img2.fwd_model.jacobian_adjoint_2p5d_1st_order.method = 'trapz';
0328 J2p5kt = calc_jacobian(img2);
0329 ke(1) = img2.fwd_model.jacobian_adjoint_2p5d_1st_order.k(end);
0330 fprintf(' 2.5D (k=0..%.1f, trapz) = %.2f sec\n', ke(1), toc(t));
0331 t = tic;
0332 img2.fwd_model.nodes(1,:) = img2.fwd_model.nodes(1,:) + rand(1,2)*1e-8;
0333 img2.fwd_model.jacobian = @jacobian_adjoint_2p5d_1st_order;
0334 img2.fwd_model.jacobian_adjoint_2p5d_1st_order.k = [0 Inf];
0335 img2.fwd_model.jacobian_adjoint_2p5d_1st_order.method = 'quadv';
0336 J2p5kq = calc_jacobian(img2);
0337 ke(2) = img2.fwd_model.jacobian_adjoint_2p5d_1st_order.k(end);
0338 fprintf(' 2.5D (k=0..%.1f, quadv) = %.2f sec\n', ke(2), toc(t));
0339 t = tic;
0340 eidors_cache clear_name jacobian
0341 img2.fwd_model.jacobian = @jacobian_adjoint_2p5d_1st_order;
0342 img2.fwd_model.jacobian_adjoint_2p5d_1st_order.k = [0 Inf];
0343 img2.fwd_model.jacobian_adjoint_2p5d_1st_order.method = 'integral';
0344 J2p5ki = calc_jacobian(img2);
0345 ke(3) = img2.fwd_model.jacobian_adjoint_2p5d_1st_order.k(end);
0346 fprintf(' 2.5D (k=0..%.1f, integral) = %.2f sec\n', ke(3), toc(t));
0347
0348 t = tic;
0349 eidors_cache clear_name jacobian
0350 J3 = calc_jacobian(img3);
0351 fprintf(' 3D = %.2f sec\n', toc(t));
0352
0353
0354 tol = 1e-8;
0355 reltol = norm(J3)/2;
0356 unit_test_cmp('2D vs 3D', J2, J3, -tol);
0357 unit_test_cmp('2.5D (k=0) vs 2D', J2p50, J2, tol);
0358 unit_test_cmp(sprintf('2.5D (k=0..%-4.1f) (trapz) vs 3D',ke(1)), J2p5kt, J3, 2*reltol);
0359 unit_test_cmp(sprintf('2.5D (k=0..%-4.1f) (quadv) vs 3D',ke(2)), J2p5kq, J3, reltol);
0360 unit_test_cmp(sprintf('2.5D (k=0..%-4.1f) (integral) vs 3D',ke(3)), J2p5ki, J3, reltol);
0361
0362 imgr = img2;
0363 imgr.fwd_model = imdl2.rec_model;
0364 clf; subplot(221); imgr.elem_data = sens(J3); show_fem(imgr,1); title('3D [log_{10}]');
0365 subplot(222); imgr.elem_data = sens(J2); show_fem(imgr,1); title('2D [log_{10}]');
0366 subplot(223); imgr.elem_data = sens(J2p5kq); show_fem(imgr,1); title('2.5D (k=0..3.0) [log_{10}]');
0367 subplot(224); imgr.elem_data = sens(J2p50); show_fem(imgr,1); title('2.5D (k=0) [log_{10}]');
0368 if 1
0369 subplot(222); imgr.elem_data = sens(J2p5kt); show_fem(imgr,1); title('2.5D (k=0..3.0) trapz [log_{10}]');
0370 subplot(223); imgr.elem_data = sens(J2p5kq); show_fem(imgr,1); title('2.5D (k=0..Inf) quadv [log_{10}]');
0371 subplot(224); imgr.elem_data = sens(J2p5ki); show_fem(imgr,1); title('2.5D (k=0..Inf) integral [log_{10}]');
0372 end
0373
0374 function S = sens(J)
0375 S = sum(J.^2,1);
0376 S = log10(max(S,1e-10)');