function [phi,P] = reconstruct(pos,flow,boundary,xg,options) arguments pos (:,2) {mustBeNumeric} flow (:,2) {mustBeNumeric} boundary (:,2) {mustBeNumeric} xg (1,1) struct {mustBeNonempty} options.numf (1,1) int32 {mustBePositive} = 32 options.x2inc_lim (1,1) double = Inf options.usepar (1,1) logical = false options.maxiter (1,1) int16 = 400 options.maxfuneval (1,1) int32 = 1000 options.Bmag (1,1) {mustBeNumeric} = 5e-05 options.res (1,1) int16 = 1 end global F_opts %#ok<GVMIS> % assertions assert(isequal(size(pos),size(flow)),'Size of position and flow arrays must match.') % unpack bag-of-vectors xdata = pos(1:options.res:end,:); Edata = flip(flow(1:options.res:end,:).*[-1,1]*options.Bmag,2); % v = ExB/B^2 disp(['Size of bag = ',num2str(length(xdata))]) % unpack grid [X2,X3] = ndgrid(double(xg.x2(3:end-2)),double(xg.x3(3:end-2))); % interpolate boundary Nm = options.numf; % number of basis functions Nj = 1; Q_0 = [zeros(Nj,1), zeros(Nj,1), zeros(Nj,1), ones(Nj,1)]; Q = lsqcurvefit(@(Q,x) Fb(Q,x),Q_0,boundary(:,1),boundary(:,2)); F_opts.Q = Q; F_opts.Nm = Nm; % F_opts.b = griddedInterpolant(boundary(:,1),boundary(:,2)); % F_opts.db = griddedInterpolant(boundary(1:end-1,1),diff(boundary(:,2))./diff(boundary(:,1))); plot(boundary(:,1),Fb(Q,boundary(:,1))) % optimize tic if options.usepar; parpool([6,16]); end P_01 = max(X3(:))/1e5; P_0 = [linspace(-P_01,P_01,Nm); ones(1,Nm); zeros(1,Nm); ones(1,Nm)]; % initial parameter matrix: [x3pos (100 km), x3sig (100 km), x2inc (kV / 1e5 km), x2amp (kV)] lb = repmat(-[Inf Inf options.x2inc_lim Inf]',[1 Nm]); % width structure limits, TBD: somehow largest s/c sep. %%% CHANGE ub = repmat( [Inf Inf options.x2inc_lim Inf]',[1 Nm]); lsq_opts = optimoptions('lsqcurvefit' ... ,'Algorithm','levenberg-marquardt' ... ,'UseParallel',options.usepar ... ,'StepTolerance',1e-4 ... ,'MaxIterations',options.maxiter ... ,'MaxFunctionEvaluations',2*numel(P_0)*options.maxfuneval ... ,'Display','iter' ... ); [P,~,~,exitflag] = lsqcurvefit(@(P,xdata) F(P,xdata),P_0,xdata,Edata,lb,ub,lsq_opts); % optimized parameter matrix if options.usepar; delete(gcp('nocreate')); end recon_time = toc; disp(['Reconstruction time = ',num2str(recon_time),' seconds']) if exitflag <= 0 warning('LSQCURVEFIT HAS NOT REACHED LOCAL MINIMUM') end phi = potential(P,X2,X3); phi = phi - mean(phi,'all'); % boundary function function b = Fb(Q,x) Q = Q*1e5; b = zeros(size(x)); for i = 1:size(Q,1) b = b + Q(i,1) + Q(i,2)*tanh((x-Q(i,3))/Q(i,4)); end end % boundary derivative function function db = Fdb(Q,x) Q = Q*1e5; db = zeros(size(x)); for i = 1:size(Q,1) db = db + Q(i,2)*sech((x-Q(i,3))/Q(i,4)).^2/Q(i,4); end end % phi basis function function phi = potential(P,x2,x3) x3pos = P(1,:)*1e5; x3sig = P(2,:)*1e5; x2inc = P(3,:)*1e-4; x2int = P(4,:)*1e3; phi = 0; for m = 1:F_opts.Nm % b = F_opts.b(x2); b = Fb(F_opts.Q,x2); phi = phi + (x2inc(m).*x2 + x2int(m)).*exp(-((x3-x3pos(m)-b)./x3sig(m)).^2); end end % least-square curve fitting function function E = F(P,xdata) Ni = size(xdata,1); % number of vectors in bag x3pos = P(1,:)*1e5; % latitudinal positions [m] (P entries are near unity) x3sig = P(2,:)*1e5; % latitudinal widths [m] x2inc = P(3,:)*1e-4; % longitudenal slope of potential ridge [V/m] x2amp = P(4,:)*1e3; % central amplitude of potential ridge [V] E = zeros(Ni,2); % electric field for i = 1:Ni % iterate through bag of vectors x2 = xdata(i,1); % current east position x3 = xdata(i,2); % current north position % b = F_opts.b(x2); % db = F_opts.db(x2); b = Fb(F_opts.Q,x2); db = Fdb(F_opts.Q,x2); % caluclate the elements of -grad(phi) = -sum_m grad(phi_m) (see documentation for details) for m = 1:F_opts.Nm % iterate through number of basis functions expf = exp(-((x3-x3pos(m)-b)/x3sig(m))^2); E(i,1) = E(i,1) + (-x2inc(m)-(2/x3sig(m)^2)*(x2inc(m)*x2+x2amp(m))*(x3-x3pos(m)-b)*db)*expf; E(i,2) = E(i,2) + (2/x3sig(m)^2)*(x2inc(m)*x2+x2amp(m))*(x3-x3pos(m)-b)*expf; end end end end