From 2d9cb5cc17e66c41eb22f757dbf900533a16656e Mon Sep 17 00:00:00 2001 From: Max Herzog <max.herzog@mailbox.tu-dresden.de> Date: Sat, 6 Jan 2024 10:30:34 +0000 Subject: [PATCH] Upload New File --- src/TIE.jl | 374 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 374 insertions(+) create mode 100644 src/TIE.jl diff --git a/src/TIE.jl b/src/TIE.jl new file mode 100644 index 0000000..dd9a7fb --- /dev/null +++ b/src/TIE.jl @@ -0,0 +1,374 @@ +module TIE + +using LinearAlgebra +using SparseArrays +using Statistics +using FFTW +using NLopt +using JSON +using NPZ + + +# constants +global const mu0 = 4*pi*1e-7 +global const qe = 1.6e-19 +global const me = 9.1095e-31 +global const hquer = 1.05459e-34 +global const c = 2.997925e8 + +""" + err(p::Vector, grad::Vector) + +Specifies the cost function to be minimized to find the best fitting boundary condition +TODO: Übergabe der (typdefifinierten) Parameter +""" +function err(p::Vector, grad::Vector, Dreg, rhs0, kx2, ky2, k0, dz, Io, Iu, If, N, N2, bandlimit) + + println("iteration: ", iOpt) + println("boundary params: ",p) + + boundvalF = zeros(ComplexF64,2*(N.x+N.y+2)) + # band limited boundary function + for ii=1:bandlimit + boundvalF[N.x+N.y+2-bandlimit+ii] = p[end+1-2*ii] - im*p[end+2-2*ii] + boundvalF[N.x+N.y+3+ii] = p[2*ii-1] + im*p[2*ii] + end + boundval = real.(ifft(ifftshift(boundvalF))) + rhs0[1:end-1,1] = boundval[1:N.x+1] + rhs0[end,1:end-1] = boundval[N.x+2:N.x+N.y+2] + rhs0[1:end-1,end] = reverse(boundval[N.x+N.y+3:2*N.x+N.y+3]) + rhs0[1,1:end-1] = reverse(boundval[2*N.x+N.y+4:2*N.x+2*N.y+4]) + rhs = vec(rhs0) + + # solution of TIE + ret = Dreg \ rhs + phase = reshape(ret,(N2.x,N2.y)) + ψ = sqrt.(If).*exp.(im*phase[2:end-1,2:end-1]) + + # duplication of domain to avoid propagation artifacts at boundary + ψ2 = [ψ reverse(ψ,dims=2); reverse(ψ,dims=1) reverse(ψ,dims=(1,2))] + + # over- and underfocused intensities from reconstructed wave + IoRec2 = abs2.(ifft(fft(ψ2).*ifftshift(exp.(im*dz/(2*k0)*(kx2.^2 .+ ky2.^2))))) + IoRec = IoRec2[1:N.x,1:N.y] + IuRec2 = abs2.(ifft(fft(ψ2).*ifftshift(exp.(-im*dz/(2*k0)*(kx2.^2 .+ ky2.^2))))) + IuRec = IuRec2[1:N.x,1:N.y] + + # deviation between reconstructed and experimental intensities + dev = norm(IoRec.-Io)/norm(Io)+norm(IuRec.-Iu)/norm(Iu) + println("deviation: ",dev) + + global iOpt = iOpt+1 + + return dev +end + +""" + calculate_TIE(path::String, filename::String) + +This is the main function of this script. It reads the declared .json-file and performs the TIE calculation +in accordance with the settings selected in the .json-file. +""" +function calculate_TIE(path::String, filename::String) + cd(path) + j = open(filename) do file + j = JSON.parse(file) + end + # parameter + Ua = j["Uacc"] # acceleration voltage + E = me*c^2+qe*Ua # electron energy + k0 = sqrt(E^2-me^2*c^4)/(hquer*c) # electron wave number + println("acceleration voltage [kV]: ",Ua/1e3) + + reg = (j["reg"])^2/k0 # reciprocal wave vector regularization parameter (2e7 for MnPtSn) + if reg>0 + println("regularization length [μm]: ",1/sqrt(reg*k0)*1e6) + else + println("no regularization") + end + + dz = j["dz"] + println("defocus [mm]: ",dz*1e3) + + p = (x = j["px"], y = j["py"]) + + save = j["save"] # save result + poi = j["poi"] # also reconstruct Poisson solution + + filename_o = j["Io"] + try + global filename_u = j["Iu"] + catch + global filename_u = "" + end + try + global filename_f = j["If"] + catch + global filename_f = "" + end + + # load image data (at least 2 defocused images) + if ~isempty(filename_o) + Io = npzread(filename_o) + Io = Io./mean(Io) + N = (x = size(Io,1), y = size(Io,2)) + if ~isempty(filename_u) + Iu = npzread(filename_u) + Iu = Iu./mean(Iu) + if ~isempty(filename_f) + If = npzread(filename_f) + If = If./mean(If) + else + If = (Io.+Iu)./2 + end + else + if ~isempty(filename_f) + If = npzread(filename_f) + If = If./mean(If) + Iu = 2*If.-Io + else + If = mean(Io)*ones(N.x,N.y) + Iu = 2*If.-Io + end + end + end + + println("image dimensions [px]: ",N.x,"x",N.y) + + a = (x = N.x*p.x, y = N.y*p.y) + println("image dimensions [μm]: ",a.x*1e6,"x",a.y*1e6) + + thick = j["thick"] #sample thickness + + # grids + xq = 1:N.x + yq = 1:N.y + x = a.x/N.x .* ((0:N.x-1) .- N.x÷2) + x = reshape(x, (N.x, 1)) + y = a.y/N.y .* ((0:N.y-1) .- N.y÷2) + y = reshape(y, (1, N.y)) + kx = 2*pi*N.x/a.x.^2*x + ky = 2*pi*N.y/a.y.^2*y + + x2 = a.x/N.x .* ((0:2*N.x-1) .- N.x) # double size for propagation / Poisson + x2 = reshape(x2, (2*N.x, 1)) + y2 = a.y/N.y .* ((0:2*N.y-1) .- N.y) + y2 = reshape(y2, (1, 2*N.y)) + kx2 = pi*N.x/a.x.^2*x2 + ky2 = pi*N.y/a.y.^2*y2 + + ## Possion solver with pseudo Dirichlet BCs + if poi + + av = mean(Io.+Iu)/2 + dIdz2 = [Io .- Iu -reverse(Io .- Iu,dims=2); -reverse(Io .- Iu,dims=1) reverse(Io .- Iu,dims=(1,2))] + If2 = [If reverse(If,dims=2); reverse(If,dims=1) reverse(If,dims=(1,2))] + + # neglect I variation solution + temp = ifftshift((kx2.^2 .+ky2.^2) ./((kx2.^2 .+ky2.^2).^2 .+ k0^2*reg^2) .*fftshift(fft(dIdz2./If2))) + if reg == 0. + temp[1,1] = 0. + end + temp = -k0/dz*real.(ifft(temp)) + phasePoi1 = temp[1:N.x,1:N.y] + + # const. I solution + temp = ifftshift((kx2.^2 .+ky2.^2) ./((kx2.^2 .+ky2.^2).^2 .+ k0^2*reg^2) .*fftshift(fft(dIdz2))) + if reg == 0. + temp[1,1] = 0. + end + temp = -k0/dz/av*real.(ifft(temp)) + phasePoi2 = temp[1:N.x,1:N.y] + + # irrotational current solution + temp = av*(ifftshift(kx2.^2) .*fft(temp./If2) .+ ifftshift(ky2.^2) .*fft(temp./If2))./ifftshift(kx2.^2 .+ ky2.^2) + temp[1,1] = 0. + temp = real.(ifft(temp)) + phasePoi3 = temp[1:N.x,1:N.y] + + BxPoi1 = zeros(size(phasePoi1)) + BxPoi1[:, 2:end] = -hquer*N.x/(a.x * thick * qe) * diff(phasePoi1, dims=2) + BxPoi1[:, 1] = BxPoi1[:, 2] + ByPoi1 = zeros(size(phasePoi1)) + ByPoi1[2:end, :] = hquer * N.y / (a.y * thick * qe) * diff(phasePoi1, dims=1) + ByPoi1[1, :] = ByPoi1[2, :] + + BxPoi2 = zeros(size(phasePoi2)) + BxPoi2[:, 2:end] = -hquer*N.x/(a.x * thick * qe) * diff(phasePoi2, dims=2) + BxPoi2[:, 1] = BxPoi2[:, 2] + ByPoi2 = zeros(size(phasePoi2)) + ByPoi2[2:end, :] = hquer * N.y / (a.y * thick * qe) * diff(phasePoi2, dims=1) + ByPoi2[1, :] = ByPoi2[2, :] + + BxPoi3 = zeros(size(phasePoi3)) + BxPoi3[:, 2:end] = -hquer*N.x/(a.x * thick * qe) * diff(phasePoi3, dims=2) + BxPoi3[:, 1] = BxPoi3[:, 2] + ByPoi3 = zeros(size(phasePoi3)) + ByPoi3[2:end, :] = hquer * N.y / (a.y * thick * qe) * diff(phasePoi3, dims=1) + ByPoi3[1, :] = ByPoi3[2, :] + + if save + npzwrite("phase_gradIconst.npy", phasePoi1) + npzwrite("phase_Iconst.npy", phasePoi2) + npzwrite("phase_irr.npy", phasePoi3) + npzwrite("B_gradIconst.npy", BxPoi1, ByPoi1) + npzwrite("B_Iconst.npy", BxPoi2, ByPoi2) + npzwrite("B_irr.npy", BxPoi3, ByPoi3) + end + + end + + ## discretized TIE with Dirichlet BCs ## + # sampling points for augmented image containing boundary values + N2 = (x = N.x+2, y = N.y+2) + + # Intensity + temp = zeros(Float64,N2.x,N2.y) + temp[2:end-1,2:end-1] .= If + I = spdiagm(0 => vec(temp)) # sparse matrix trafo + + # x-derivatives + temp0 = ones(Float64, N2.x*N2.y) + temp1 = -ones(Float64, N2.x*N2.y-1) + Dx1 = N.x/a.x*spdiagm(-1 => temp1, 0 => temp0) + Dx2 = N.x/a.x*spdiagm(0 => -temp0, 1 => -temp1) + + # y-derivatives + temp1 = -ones(Float64, N2.x*N2.y-N2.x) + Dy1 = N.y/a.y*spdiagm(-N2.x => temp1, 0 => temp0) + Dy2 = N.y/a.y*spdiagm(0 => -temp0, N2.x => -temp1) + + # differential operator + D = 1/2 .*(Dx2*I*Dx1 .+ Dy2*I*Dy1 .+ Dx1*I*Dx2 .+ Dy1*I*Dy2) + + # regularization + if reg>0 + temp = ones(Float64, N2.x*N2.y) + Dreg = (transpose(D)*D .+ reg^2*k0^2*spdiagm(0 => temp)) + else + Dreg = D + end + + # Dirichlet BCs + for i=1:N2.x + Dreg[i,:] .= 0. + Dreg[i,i] = 1. + end + for i=N2.x*N2.y-N2.x+1:N2.x*N2.y + Dreg[i,:] .= 0. + Dreg[i,i] = 1. + end + for i=1:N2.x:N2.x*N2.y + Dreg[i,:] .= 0. + Dreg[i,i] = 1. + Dreg[i+N2.x-1,:] .= 0. + Dreg[i+N2.x-1,i+N2.x-1] = 1. + end + + dropzeros!(Dreg) + + # right hand side (inhomogeneous term) of TIE + rhs0 = zeros(Float64,N2.x,N2.y) + rhs0[2:end-1,2:end-1] .= k0*(Io.-Iu)./(2*dz) + if reg>0 + rhs0 = reshape(transpose(D)*vec(rhs0),(N2.x,N2.y)) + end + + # 1st run to determine bounds of boundary fourier coefficients + bandlimit = j["bandlim"] # number of non-zero fourier coefficient of boundary function + println("band limit: ",bandlimit) + + boundvalF = zeros(ComplexF64,2*(N.x+N.y+2)) # boundary function set to zero + boundval = real.(ifft(ifftshift(boundvalF))) + rhs0[1:end-1,1] = boundval[1:N.x+1] + rhs0[end,1:end-1] = boundval[N.x+2:N.x+N.y+2] + rhs0[1:end-1,end] = reverse(boundval[N.x+N.y+3:2*N.x+N.y+3]) + rhs0[1,1:end-1] = reverse(boundval[2*N.x+N.y+4:2*N.x+2*N.y+4]) + rhs = vec(rhs0) + + # solution of TIE + ret = Dreg \ rhs + phase = reshape(ret,(N2.x,N2.y)) + + # approximation of upper bounds for fourier coefficients of boundary function from parseval's theorem + bounds = 2*sqrt((maximum(phase)-minimum(phase))^2*(2*(N.x+N.y+2))^2/2) + println("Fourier component bounds: ",bounds) + + ## optimization of boundary conditions ## + # global optimizers: :GN_DIRECT_L, GD_STOGO, GN_CRS2_LM + # local optimizers: :LN_COBYLA, :LN_SBPLX, :LN_NELDERMEAD, :LN_BOBYQA + + opt = Opt(:GN_DIRECT_L, 2 * bandlimit) + opt.lower_bounds = -bounds .*ones(Float64, 2*bandlimit) + opt.upper_bounds = bounds .*ones(Float64, 2*bandlimit) + + #opt.ftol_abs = 1e-15 + opt.xtol_rel = 1e-15 + opt.maxeval = j["maxiter"] + println("maximal number of iterations: ",opt.maxeval) + + opt.min_objective = ((x,grad) -> err(x, grad, Dreg, rhs0, kx2, ky2, k0, dz, Io, Iu, If, N, N2, bandlimit)) + #p0 = zeros(Float64, 2*bandlimit) # starting values for boundary function fourier coefficients + p0 = rand(Float64, 2*bandlimit) + global iOpt = 1 + + (minf,minx,retu) = optimize(opt, p0) + # global p0org=minx + numevals = opt.numevals # the number of function evaluations + println("got $minf at $minx after $numevals iterations (returned $retu)") + #ftbval = minx + if (maximum(abs.(minx))-bounds)/bounds>0.95 + println("warning: bounds for parameter search too small") + end + + boundvalF = zeros(ComplexF64,2*(N.x+N.y+2)) + # band limited boundary function + for ii=1:bandlimit + boundvalF[N.x+N.y+2-bandlimit+ii] = minx[end+1-2*ii] - im*minx[end+2-2*ii] + boundvalF[N.x+N.y+3+ii] = minx[2*ii-1] + im*minx[2*ii] + end + boundval = real.(ifft(ifftshift(boundvalF))) + + rhs0[1:end-1,1] = boundval[1:N.x+1] + rhs0[end,1:end-1] = boundval[N.x+2:N.x+N.y+2] + rhs0[1:end-1,end] = reverse(boundval[N.x+N.y+3:2*N.x+N.y+3]) + rhs0[1,1:end-1] = reverse(boundval[2*N.x+N.y+4:2*N.x+2*N.y+4]) + + rhs = vec(rhs0) + + # solution of TIE + ret = Dreg \ rhs + phase = reshape(ret,(N2.x,N2.y)) + + # final deviation between reconstructed and experimental intensities + ψ = sqrt.(If).*exp.(im*phase[2:end-1,2:end-1]) + ψ2 = [ψ reverse(ψ,dims=2); reverse(ψ,dims=1) reverse(ψ,dims=(1,2))] + Iorec2 = abs2.(ifft(fft(ψ2).*ifftshift(exp.(im*dz/(2*k0)*(kx2.^2 .+ ky2.^2))))) + Iorec = Iorec2[1:N.x,1:N.y] + Iurec2 = abs2.(ifft(fft(ψ2).*ifftshift(exp.(-im*dz/(2*k0)*(kx2.^2 .+ ky2.^2))))) + Iurec = Iurec2[1:N.x,1:N.y] + + dev = norm(Iorec[2:N.x-1,2:N.y-1].-Io[2:N.x-1,2:N.y-1])/norm(Io[2:N.x-1,2:N.y-1])+norm(Iurec[2:N.x-1,2:N.y-1].-Iu[2:N.x-1,2:N.y-1])/norm(Iu[2:N.x-1,2:N.y-1]) + println("deviation: ",dev) + + boundvalexp = [angle.(ψ[1:end-1,1]);angle.(ψ[end,1:end-1]);reverse(angle.(ψ[end,1:end-1]));reverse(angle.(ψ[1:end-1,1]))] + + ## magnetic field ## + # magnetic induction + Bx = zeros(size(phase)) + Bx[:, 2:end] = -hquer*N.x/(a.x*thick*qe) * diff(phase, dims=2) + Bx[:, 1] = Bx[:, 2] + By = zeros(size(phase)) + By[2:end, :] = hquer * N.y / (a.y * thick * qe) * diff(phase, dims=1) + By[1, :] = Bx[2, :] + + ## save results ## + if save + npzwrite("If_TIE.npy", If) + npzwrite("phase_TIE.npy", phase[2:end-1,2:end-1]) + npzwrite("B_TIE.npy", Bx, By) + npzwrite("boundvalexp.npy", boundvalexp) + end +end + +end # module \ No newline at end of file -- GitLab