From e28bc81b8a46b4728b864f164b095e50706a3083 Mon Sep 17 00:00:00 2001 From: Paul Shen Date: Wed, 27 Nov 2024 01:22:52 -0500 Subject: [PATCH] n --- .gitignore | 3 +- src/core/modes.jl | 12 +- src/pic/run.jl | 384 ---------------------------------------------- src/pic/utils.jl | 169 -------------------- src/sim/setup.jl | 306 ------------------------------------ src/sim/solve.jl | 150 ------------------ 6 files changed, 9 insertions(+), 1015 deletions(-) delete mode 100644 src/pic/run.jl delete mode 100644 src/pic/utils.jl delete mode 100644 src/sim/setup.jl delete mode 100644 src/sim/solve.jl diff --git a/.gitignore b/.gitignore index e3eaf34..7d0a563 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,8 @@ docs/build/ *.*.pyc *.pyo _*.py -# /src/sim +/src/sim +/src/pic Manifest.* _.png diff --git a/src/core/modes.jl b/src/core/modes.jl index dcb4d8f..ae56c44 100644 --- a/src/core/modes.jl +++ b/src/core/modes.jl @@ -124,15 +124,17 @@ function solvemodes(ϵ, dx, λ, neigs) end # global _as = ϵ, x, y solver = VectorModesolver.VectorialModesolver(λ, x, y, boundary, f) - modes = VectorModesolver.solve(solver, neigs, tol) + modes = VectorModesolver.solve(solver, 2, tol) # plot_mode_fields(modes[1]) |> display # error() # display(heatmap(ϵ)) # display(heatmap(real(transpose(modes[1].Ex)))) - # Ex = modes[1].Ex - # e = mean(Ex .* ϵ, dims=2) ./ mean(Ex, dims=2) - # display(lines(abs.(vec(e)))) - # error() + for i = 1:2 + Ex = modes[i].Ex + e = mean(Ex .* ϵ, dims=2) ./ mean(Ex, dims=2) + display(lines(abs.(vec(e)))) + end + error() modes = [namedtuple([k => getfield(mode, k)[:, m+1:end-n] for k = (:Ex, :Ey, :Hx, :Hy)]) for mode in modes] diff --git a/src/pic/run.jl b/src/pic/run.jl deleted file mode 100644 index 4f4ccc4..0000000 --- a/src/pic/run.jl +++ /dev/null @@ -1,384 +0,0 @@ -function picrun(path; kw...) - Random.seed!(1) - println("setting up simulation...") - global PROB_PATH = joinpath(path, "problem.json") - SOL_PATH = joinpath(path, "solution.json") - - io = open(PROB_PATH) - s = read(io, String) - global prob = JSON.parse(s) - # merge!(prob, kw) - for (k, v) = pairs(kw) - prob[string(k)] = v - end - @unpack name, N, dtype, xmargin, ymargin, dx0, source_margin, runs, ports, dl, xs, ys, zs, components, study, zmode, hmode, zmin, zcenter, gpu_backend, magic, framerate, layer_stack, materials, L = prob - if study == "inverse_design" - @unpack designs, targets, weights, eta, iters, restart, save_memory, design_config, stoploss = prob - end - # iters = 15 - # for (k, v) in pairs(kw) - # @show k, v - # @eval $k = $k - # @eval $k = $v - # end - # @show eta - # eta = 0.02 - # N = 2 - # heatmap(debug.mask) - - F = Float32 - alg = :spectral - alg = nothing - if contains(dtype, "16") - F = Float16 - println("Float16 selected. make sure your cpu or GPU supports it. otherwise will be emulated and very slow.") - end - λ = median(prob.wavelengths) - ticks = [ - begin - round((v - v[1]) / dl) - end for v = [xs, ys, zs] - ] - spacings = diff.(ticks) - x = spacings[1][1] - spacings = [x, x, spacings[3]] - dx = x * dl - popfirst!.(ticks) - deltas = spacings * dl - - - ϵ2 = nothing - sz = round.(L / dl) - ϵ3 = zeros(F, Tuple(sz)) - layer_stack = sort(collect(pairs(layer_stack)), by=kv -> -kv[2].mesh_order) |> OrderedDict - ϵmin = Inf - for (k, v) = pairs(layer_stack) - a = stack(map(sort(collect(readdir(joinpath(path, "temp", string(k)), join=true)))) do file - a = F.(Gray.(FileIO.load(file))) - reverse(a', dims=2) - end) - # a = a[Base.OneTo.(min.(size(a), sz))...] - @unpack material, thickness = v - - start = 1 + round.([(v.origin / dl)..., (v.zmin - zmin) / dl]) - I = range.(start, start + size(a) - 1) - overhang = max.(last.(I) .- sz, 0) - a = a[Base.oneto.(size(a) - overhang)...] - I = range.(start, start + size(a) - 1) - - ϵ = materials(material).epsilon - ϵmin = min(ϵ, ϵmin) - ϵ3[I...] .*= 1 - a - ϵ3[I...] .+= a .* ϵ - end - ϵ3 = max.(ϵmin, ϵ3) - ϵ2 = ϵ3[:, :, 1+round((zcenter - zmin) / dl)] - # heatmap(ϵ2) |> display - # GLMakie.volume(ϵ3) |> display - # error("stop") - - # s = run_probs[1].source_instances[1] - # ab =Functors.functor(s) - # a = gpu(s) - # aa = gpu(s.g.Jy) - - global models = nothing - lb = components.device.bbox[1] - if N == 2 - ϵ = ϵ2 - else - ϵ = ϵ3 - end - if study == "inverse_design" - targets = fmap(F, targets) - if isfile(SOL_PATH) - sol = open(SOL_PATH, "r") do f - JSON.parse(f) - end - else - sol = nothing - end - models = [ - begin - @unpack init, bbox = design - L = bbox[2] - bbox[1] - szd = Tuple(round.(Int, L / dl)) # design region size - symmetries = [Int(s) + 1 for s = design.symmetries] - - lvoid = design.lvoid / dl - lsolid = design.lsolid / dl - frame = ϵ2 - frame = frame .>= 0.99maximum(frame) - # frame = nothing - start = round((bbox[1] - lb) / dl + 1) - b = Blob(szd; init, lvoid, lsolid, symmetries, F, frame, start) - display(heatmap(b.frame)) - - if !isnothing(sol) && !restart - println("loading saved design...") - b.a .= sol.params[i] |> typeof(b.a) - end - b - end for (i, design) = enumerate(designs) - ] - end - - boundaries = [] # unspecified boundaries default to PML - - device = 0 - λ = F(λ) - # guess = convert.(F,guess) - i = int(v2i(zmode - zmin, deltas[3])) - j = int(v2i(zmode + hmode - zmin, deltas[3])) - mode_spacings = [spacings[1][1], adddims(spacings[3][i+1:j], dims=1)] - mode_deltas = mode_spacings * dl - global runs = [SortedDict([k => isa(v, AbstractDict) ? SortedDict(v) : v for (k, v) = pairs(run)]) for run in runs] - global runs_sources = [ - begin - sources = [] - for (port, sig) = SortedDict(run.sources) |> pairs - @unpack center, wavelength_mode_numbers = sig - # i = findfirst(mode_solutions) do v - # abs(_λ - v.wavelength) < 0.001 && string(port) in string.(v.ports) - # end - # sum(abs.(aa.source_instances[1].g.Jy)) - # heatmap(abs.(bb.source_instances[1]._g.Jy)) - n = -sig.normal - tangent = [-n[2], n[1]] - center = (sig.center - lb) / λ - L = tangent * sig.mode_width / λ - L3 = [L..., hmode / λ] - center3 = [center..., (zcenter - zmin) / λ] - if N == 3 - L = L3 - center = center3 - end - dimsperm = getdimsperm([L..., 1]) - λmodenums = SortedDict([(F(_λ) / λ) => v for (_λ, v) in pairs(wavelength_mode_numbers)]) - push!(sources, Source(center, -L / 2, L / 2, dimsperm, N, center3, -L3 / 2, L3 / 2; λmodenums, label="s$(string(port)[2:end])")) - end - sources - end for run in runs - ] - # sort!(runs_sources, by=x -> x.label) - - global runs_monitors = [[ - begin - center = (m.center - lb) / λ - n = m.normal - tangent = [-n[2], n[1]] - # n, tangent, = vcat.((n, tangent,), ([0],)) - - λmodenums = SortedDict([(F(_λ) / λ) => v for (_λ, v) in pairs(m.wavelength_mode_numbers)]) - - L = tangent * m.mode_width / λ - L3 = [L..., hmode / λ] - center3 = [center..., (zcenter - zmin) / λ] - if N == 3 - L = L3 - center = center3 - end - dimsperm = getdimsperm(L3) - - Monitor(center, -L / 2, L / 2, dimsperm, N, center3, -L3 / 2, L3 / 2; λmodenums, label=port) - end for (port, m) = SortedDict(run.monitors) |> pairs] for run in runs] - - global run_probs = - [ - begin - setup(dl / λ, boundaries, sources, monitors, deltas[1:N] / λ, mode_deltas[1:N-1] / λ, ; - F, ϵ, ϵ3, deltas3=deltas / λ, λ) - end for (i, (run, sources, monitors)) in enumerate(zip(runs, runs_sources, runs_monitors)) - ] - - if !isempty(gpu_backend) - println("using $gpu_backend backend.") - # Flux.gpu_backend!(gpu_backend) - if gpu_backend == "CUDA" - @assert CUDA.functional() - end - for prob = run_probs - for k = keys(prob) - if k in (:u0, :_geometry, :geometry, :source_instances, :monitor_instances) - prob[k] = prob[k] |> gpu - end - end - end - models = models |> gpu - else - println("using CPU backend.") - end - t0 = time() - lb3 = (lb..., zmin) - if study == "sparams" - println("Computing s-parameters...") - @unpack S, sols = write_sparams(runs, run_probs, lb, dl; - F, verbose=true, framerate, path) - plotsols(sols, run_probs, path) - sol = (; sparam_family(S)..., - path, study) - open(SOL_PATH, "w") do f - write(f, json(cpu(sol))) - end - println("Done in $(time() - t0) s") - elseif study == "inverse_design" - if length(lb) == 3 - if magic != "summersale" - error("3D inverse design feature must be requested from Luminescent AI info@luminescentai.com") - end - end - opt = Flux.Adam(eta) - opt_state = Flux.setup(opt, models) - println("starting optimization... first iter will be slow due to adjoint compilation.") - img = nothing - best = best0 = 0 - println("") - for i = 1:iters - println("($i) ") - stop = i == iters - if :phase_shifter == first(keys(targets)) - @time l, (dldm,) = Flux.withgradient(models) do models - @unpack S, sols, lminloss = write_sparams(runs, run_probs, lb, dl, - designs, design_config, models; - F, img, alg)#(1)(1) - k = keys(S) |> first - s = S[k][Symbol("o2@0,o1@0")] - - @unpack S = write_sparams(runs, run_probs, lb, dl, - designs, design_config, models; - F, img, alg, save_memory, perturb=:ϵ,)#(1)(1) - s_ = S[k][Symbol("o2@0,o1@0")] - - T = abs2(s) - dϕ = angle(s_ / s) - println("T: $T, dϕ: $dϕ") - (exp(T - 1) * dϕ / π) + lminloss - # T * dϕ / π - end - else - @time global l, (dldm,) = Flux.withgradient(models) do models - # sols = get_sols(runs, run_probs, path, lb, deltas, - res = write_sparams(runs, run_probs, lb, dl, - designs, design_config, models; - F, img, alg, save_memory) - # l = res - # println("loss: $l") - # return l - @unpack S, sols = res - l = 0 - for k = keys(targets) - y = targets[k] - err = - - if :phasediff == k - yhat = namedtuple([ - _λ => namedtuple([ - ps => - begin - ks = ignore_derivatives() do - ps = split(string(ps), ",") - ks = keys(S[_λ]) - [ks[findfirst(k -> startswith(string(k), p), ks)] for p = ps] - end - s1, s2 = [S[_λ][k] for k in ks] - angle(s1 / s2) - end - for ps = keys(targets[k][_λ])]) - for _λ = keys(targets[k])]) - err = (x, y) -> angle(cis(x) / cis(y)) - yhat = flatten(yhat) - y = flatten(y) - Z = length(y) * convert.(F, 2π) - else - yhat = if "sparams" == string(k) - S - elseif "tparams" == string(k) - fmap(abs2, S) - end - - # global a1 = S, y - yhat = [[yhat(_λ)(k) for k = keys(y[_λ])] for _λ = keys(y)] - yhat = flatten(yhat) - y = flatten(y) - Z = sum(abs, y) - end - _l = sum(abs, err.(yhat, y),) * weights(k) / Z - println("$(k) loss: $_l ") - l += _l - end - println(" weighted total loss $l") - l - end - end - @assert !isnothing(dldm) - if !isnothing(stoploss) && l < stoploss - println("Loss below threshold, stopping optimization.") - stop = true - end - if i == 1 || i % 2 == 0 || stop - println("saving checkpoint...") - ckptpath = joinpath(path, "checkpoints", replace(string(now()), ':' => '_', '.' => '_')) - - mkpath(ckptpath) - for (i, (m, design)) = enumerate(zip(models, designs)) - # a = Gray.(m() .< 0.5) - - # Images.save(joinpath(ckptpath, "optimized_design_region_$i.png"), a) - # Images.save(joinpath(path, "optimized_design_region_$i.png"), a) - end - plotsols(sols, run_probs, (path, ckptpath)) - - sol = (; - sparam_family(S)..., - optimized_designs=[m() .> 0.5 for m in models], dl, - params=getfield.(models, :p), - designs, - design_config, path, - dx, - study, - ) - - open(joinpath(ckptpath, "solution.json"), "w") do f - write(f, json(cpu(sol))) - end - open(joinpath(path, "solution.json"), "w") do f - write(f, json(cpu(sol))) - end - end - if stop - break - end - # Flux.update!(opt_state, models, dldm)# |> gpu) - - da = Inf - α = 1 - masks0 = [m() for m in models] - models0 = deepcopy(models) - Flux.update!(opt_state, models, dldm)# |> gpu) - models1 = deepcopy(models) - while da > 0.001 + 0.002l - for (m, m0, m1) = zip(models, models0, models1) - m.p .= α * m1.p + (1 - α) * m0.p - end - masks = [m() for m in models] - da = sum(masks - masks0) do a - sum(abs, a) - end - da /= sum(masks) do a - prod(size(a)) - end - α *= 0.95 - end - # @show α - println("fractional change in design: $da") - println("") - end - if framerate > 0 - write_sparams(runs, run_probs, lb, dl, - designs, design_config, models; - F, img, alg, framerate, path) - end - println("Done in $(time() - t0) .") - - end - sol -end \ No newline at end of file diff --git a/src/pic/utils.jl b/src/pic/utils.jl deleted file mode 100644 index 2b08890..0000000 --- a/src/pic/utils.jl +++ /dev/null @@ -1,169 +0,0 @@ - -function julia_main()::Cint - if !isempty(ARGS) - picrun(ARGS[1]) - end - return 0 -end - -function lastrun(; name=nothing, study=nothing, wd=joinpath(pwd(), "luminescent_runs")) - !isnothing(name) && return joinpath(wd, name) - - l = filter(isdir, readdir(wd, join=true)) - sort!(l, by=p -> Dates.unix2datetime(mtime(p)), rev=true) - - if !isnothing(study) - for p = l - try - open(joinpath(p, "solution.json")) do f - JSON.parse(f)["study"] - end == study && return p - catch e - println(e) - end - end - end - return l[1] -end - -function write_sparams(runs, run_probs, lb, dl, - designs=nothing, design_config=nothing, models=nothing; - alg=nothing, save_memory=false, verbose=false, perturb=nothing, framerate=0, path="", kw...) - F = run_probs[1].grid.F - - # prob = run_probs[1] - # return make_geometry(models, lb, dl, prob._geometry, designs, design_config; F, perturb).ϵ |> sum - sols = [ - begin - prob[:_geometry] = make_geometry(models, lb, dl, prob._geometry, designs, design_config; F, perturb) - #@debug typeof(prob.u0.E.Ex), typeof(prob.geometry.ϵ) - solve(prob; alg, save_memory, verbose, framerate, path) - end for (i, prob) in enumerate(run_probs) - # end for (i, prob) in enumerate(run_probs) - ] - # return sols[1] - # S = sols[1]("a+", 1) |> abs2 - # return (; S, sols) - - ulims = sols[1].ulims - # return sol - coeffs = OrderedDict() - for (sol, run) in zip(sols, runs) - sources = values(run.sources) - monitors = values(run.monitors) - source_port = first(sources).port - # source_mn = first(sources).wavelength_mode_numbers(1)[1] - source_mn = first(sources).wavelength_mode_numbers |> Porcupine.first |> Porcupine.first - for (m, monitor) = enumerate(monitors) - for (w, λ) = enumerate(keys(monitor.wavelength_mode_numbers)) - for mn = monitor.wavelength_mode_numbers[λ] - monitor_port = monitor.port - λ = Symbol(λ) - if !haskey(coeffs, λ) - coeffs[λ] = OrderedDict() - end - s = "$monitor_port@$mn," * "$source_port@$source_mn" - s = Symbol(s) - coeffs[λ][s] = (sol("a+", m, w, mn), sol("a-", m, w, mn)) - end - end - end - end - # return coeffs(1)(1)[1] |> abs2 - - S = OrderedDict([λ => OrderedDict([k => begin - s = ignore() do - split(string(k), ",")[2] - end - # Symbol( - coeffs[λ][k][1] / coeffs[λ][Symbol("$s,$s")][2] - end for (k) = keys(coeffs[λ])]) for (λ) = keys(coeffs)]) - # if source_mn == mn == 0 - # coeffs[λ]["$monitor_port,$source_port")] = v - # end - return (; S, sols) -end -function make_geometry(models, lb, dl, geometry, designs, design_config; F=Float32, perturb=nothing) - isnothing(models) && return geometry - namedtuple([k => begin - a = geometry[k] - k = if k == :ϵ - :epsilon - else - k - end - - ks = keys(design_config.fill) - if k in ks || string(k) in ks - f = design_config.fill(k) |> F - v = minimum(a) - if perturb == k - f *= convert.(F, 1.001) - end - - b = Zygote.Buffer(a) - copyto!(b, a) - - for (m, design) in zip(models, designs) - mask = m() * (f - v) + v - - o = round.(Int, (design.bbox[1] - lb) / dl) + 1 - if ndims(b) == 3 - o = [o..., 1 + round(Int, (zcore - zmin) / dl)] - mask = stack(fill(mask, round(Int, thickness / dl))) - end - o -= m.margin - # b[range.(o, o .+ size(mask) .- 1)...] = mask - b[[i:j for (i, j) = zip(o, o .+ size(mask) .- 1)]...] = mask - end - copy(b) - else - # println("no fill for $k") - a - end - end for k = keys(geometry)]) -end -# using GLMakie: volume -function plotsols(sols, probs, path,) - # try - for (i, (prob, sol)) in enumerate(zip(probs, sols)) - # try - @unpack u, p, _p = sol |> cpu - prob = prob |> cpu - @unpack monitor_instances, source_instances, λ, = prob - @unpack deltas, spacings, bbox, dl = prob.grid - u = u.Hz - N = ndims(u) - # if N == 3 - # volume(u) |> display - # heatmap(u[:, :, round(Int, size(u, 3) / 2)]) |> display - # else - # heatmap(u) |> display - # end - # return - g = _p.ϵ - u = upsample(u, spacings) - g = imresize(g, size(u)) - bbox /= dl - ratio = int(deltas[1] / dl) - - plt = quickie(u, g; dl, λ, monitor_instances, ratio, source_instances, bbox) - display(plt) - - if !isa(path, Base.AbstractVecOrTuple) - path = (path,) - end - for path = path - try - CairoMakie.save(joinpath(path, "run_$i.png"), plt,) - catch e - println("save plot failed") - println(e) - end - end - end - # catch e - # println("plot failed") - # println(e) - # end -end diff --git a/src/sim/setup.jl b/src/sim/setup.jl deleted file mode 100644 index 3218693..0000000 --- a/src/sim/setup.jl +++ /dev/null @@ -1,306 +0,0 @@ -""" - function setup(boundaries, sources, monitors, L, dx, polarization=nothing; F=Float32) - -Args -... -- L: vector of lengths in wavelengths of simulation domain -- polarization: only applies to 2d which can be :TM (Ez, Hx, Hy) or :TE (Hz, Ex, Ey) -""" -function setup(dl, boundaries, sources, monitors, deltas, mode_deltas; - polarization=:TE, - transient_duration=0, steady_state_duration=0, - ϵ=1, μ=1, σ=0, m=0, - ϵ3=1, - F=Float32, - pml_depths=nothing, pml_ramp_fracs=0.2, - Courant=0.9, - deltas3=deltas, - kw...) - (deltas, mode_deltas, ϵ, μ, σ, m) = F.((deltas, mode_deltas, ϵ, μ, σ, m)) - N = length(deltas) - L = size(ϵ) * dl - sz = Tuple([isa(d, Number) ? int(l / d) : length(d) for (d, l) = zip(deltas, L)]) - a = ones(F, Tuple(sz)) - _geometry = (; ϵ, μ, σ, m) |> pairs |> OrderedDict - geometry = OrderedDict() - # global _a = geometry, _geometry, ϵ3 - for (k, v) = pairs(_geometry) - geometry[k] = if isa(v, AbstractArray) - @time downsample(v, int(deltas / dl)) - elseif k ∈ (:σ, :m) - a * v - else - v - end - end - ϵ3 = if N == 3 - geometry.ϵ - else - downsample(ϵ3, int(deltas3 / dl)) - end - - ϵmin, ϵmax = extrema(geometry.ϵ) - μmin, μmax = extrema(geometry.μ) - nmax = sqrt(ϵmax * μmax) - nmin = sqrt(ϵmin * μmin) - N = length(sz) - if isa(pml_depths, Number) - pml_depths = fill(pml_depths, N) - end - # if isa(pml_ramp_fracs, Number) - # pml_ramp_fracs = fill(pml_ramp_fracs, N) - # end - maxdeltas = maximum.(deltas) - if isnothing(pml_depths) - mpml = σpml = 2.0 - @show δ = 4 / nmin / (σpml + mpml) - pml_depths = max.([δ, δ, 0.2δ][1:N], maxdeltas) - end - pml_depths = trim.(pml_depths, maxdeltas) - - - - if N == 1 - field_names = (:Ez, :Hy) - polarization = nothing - elseif N == 2 - if polarization == :TM - Enames = (:Ez,) - Hnames = (:Hx, :Hy) - field_names = (:Ez, :Hx, :Hy) - elseif polarization == :TE - Enames = (:Ex, :Ey) - Hnames = (:Hz,) - field_names = (:Ex, :Ey, :Hz) - end - else - polarization = nothing - Enames = (:Ex, :Ey, :Ez) - Hnames = (:Hx, :Hy, :Hz) - field_names = (:Ex, :Ey, :Ez, :Hx, :Hy, :Hz) - end - - nodes = fill(:U, N, 2) - # pml_depths = [xpml, ypml, zpml] - # pml_ramp_fracs = [xpml_ramp_frac, ypml_ramp_frac, zpml_ramp_frac] - db = Any[PML(j * i, pml_depths[i], σpml, mpml) for i = 1:N, j = (-1, 1)] - field_boundvals = DefaultDict(() -> Array{Any,2}(fill(nothing, N, 2))) - geometry_padvals = DefaultDict(() -> Array{Any,2}(fill(nothing, N, 2))) - geometry_padamts = DefaultDict(() -> zeros(Int, N, 2)) - _geometry_padamts = DefaultDict(() -> zeros(Int, N, 2)) - is_field_on_lb = Dict([k => zeros(Int, N) for k = field_names]) - is_field_on_ub = Dict([k => zeros(Int, N) for k = field_names]) - bbox = zeros(F, N, 2) - bbox[:, 2] .= L - field_sizes = Dict([k => collect(sz) for k = field_names]) - - for b = boundaries - for i = b.dims - if typeof(b) == Periodic - db[i, :] = [Periodic(-abs(i)), Periodic(abs(i))] - else - if i > 0 - db[i, 2] = b - else - db[abs(i), 1] = b - - end - end - end - end - - for i = 1:N - for j = 1:2 - b = db[i, j] - t = typeof(b) - if t == PML - elseif t == PEC - nodes[i, j] = :E - elseif t == PMC - nodes[i, j] = :H - end - end - end - - for i = 1:N - if nodes[i, :] == [:U, :E] - - nodes[i, 1] = :H - elseif nodes[i, :] == [:U, :H] - - nodes[i, 1] = :E - elseif nodes[i, :] == [:E, :U] - - nodes[i, 2] = :H - elseif nodes[i, :] == [:H, :U] - - nodes[i, 2] = :E - elseif nodes[i, :] == [:U, :U] - - nodes[i, :] = [:E, :H] - elseif nodes[i, :] == [:U, :U] - - nodes[i, :] = [:E, :H] - elseif nodes[i, :] == [:E, :E] - - elseif nodes[i, :] == [:H, :H] - - end - - end - - - for i = 1:N - select = i .== 1:N - xyz = para = perp = [:x, :y, :z] - perp = [popat!(para, i)] - for j = 1:2 - b = db[i, j] - if isa(b, PML) - npml = round(b.d / maxdeltas[i]) - _npml = round(b.d / dl) - # l1 = max.(1, round.(b.ramp_frac * l)) - # r1 = max.(1, round.(b.ramp_frac * r)) - # if any(l1 .> 0) || any(r1 .> 0) - # # rr=ReplicateRamp(convert.(F,b.σ)) - # rr = convert.(F, b.σ) - # push!(geometry_padvals[:σ], OutPad(rr, l1, r1, sz)) - # push!(geometry_padvals[:m], OutPad(rr, l1, r1, sz)) - # end - if !isa(deltas[i], Number) - if j == 1 - pushfirst!(deltas[i], fill(maxdeltas[i], npml)...) - else - push!(deltas[i], fill(maxdeltas[i], npml)...) - end - end - for k = (:σ, :m, :ϵ, :μ) - geometry_padamts[k][i, j] = npml - _geometry_padamts[k][i, j] = _npml - end - for k = (:ϵ, :μ) - geometry_padvals[k][i, j] = :replicate - end - geometry_padvals[:σ][i, j] = b.σ - geometry_padvals[:m][i, j] = b.m - - if j == 1 - bbox[i, :] .-= b.d - end - for k = keys(field_sizes) - field_sizes[k][i] += npml - end - end - f = nodes[i, j] - for k = field_names - q = startswith(String(k), String(f)) - if (q ? k[2] in para : k[2] in perp) - if isa(b, Periodic) - field_boundvals[k][i, j] = :periodic - else - field_boundvals[k][i, j] = 0 - end - end - end - end - end - - - for (k, v) = pairs(field_boundvals) - is_field_on_lb[k] = !isnothing.(v[:, 1]) - is_field_on_ub[k] = !isnothing.(v[:, 2]) - end - - add_current_keys!(field_sizes) - - field_sizes = NamedTuple([k => Tuple(field_sizes[k]) for (k) = keys(field_sizes)]) - fielmaxdeltas = NamedTuple([k => zeros(F, Tuple(field_sizes[k])) for (k) = field_names]) - if N == 1 - elseif N == 3 - u0 = NamedTuple([k => zeros(F, Tuple(field_sizes[k])) for k = (:Ex, :Ey, :Ez, :Hx, :Hy, :Hz, :Jx, :Jy, :Jz)]) - else - if polarization == :TM - u0 = NamedTuple([k => zeros(F, Tuple(field_sizes[k])) for k = (:Ez, :Hx, :Hy, :Jz)]) - else - u0 = NamedTuple([k => zeros(F, Tuple(field_sizes[k])) for k = (:Ex, :Ey, :Hz)]) - end - end - - geometry_sizes = NamedTuple([k => sz .+ sum(geometry_padamts[k], dims=2) for k = keys(geometry_padamts)]) - # field_lims = NamedTuple(Pair.(keys(geometry_sizes), Base.oneto.(values(geometry_sizes)))) - field_lims = OrderedDict{Symbol,Any}() - field_grimaxdeltas = Dict{Symbol,Any}() - for k = keys(geometry_sizes) - if k in (:μ, :m) - names = Hnames - elseif k in (:ϵ, :σ) - names = Enames - end - v = NamedTuple([ - begin - xyz = f[2] - terminations = zip(is_field_on_lb[f], is_field_on_ub[f]) - g = Symbol("$(k)$xyz$xyz") - start = 0.5 - 0.5is_field_on_lb[f] - v = [(start) (start + geometry_sizes[k] - 1)] - f => v - end for f = names - ]) - field_lims[k] = v - end - field_lims = merge(values(field_lims)...) - field_lims = add_current_keys(field_lims) - lb = bbox[:, 1] - - field_deltas = [_make_field_deltas(d, N, field_boundvals, field_sizes, i) for (i, d) = enumerate(deltas)] - field_diffdeltas = [_make_field_deltas(d, N, field_boundvals, field_sizes, i, true) for (i, d) = enumerate(deltas)] - - geometry_padvals = NamedTuple(geometry_padvals) - field_boundvals = NamedTuple(field_boundvals) - field_diffpadvals = kmap(a -> reverse(a, dims=2), field_boundvals) - field_spacings = int(field_deltas / dl) - spacings = int(deltas / dl) - - grid = (; F, N, L, bbox, sz, deltas, deltas3, lb, field_lims, field_sizes, field_boundvals, field_deltas, field_diffdeltas, field_diffpadvals, geometry_padvals, geometry_padamts, _geometry_padamts, dl, spacings,) - - source_instances = SourceInstance.(sources, (grid,), (ϵ3,)) - monitor_instances = MonitorInstance.(monitors, (grid,), (ϵ3,)) - - dt = nmin / √(sum(dx -> 1 / minimum(dx)^2, deltas)) * Courant - dt = 1 / ceil(1 / dt) |> F - sz = Tuple(sz) - - # geometry_padvals[:invϵ] = geometry_padvals[:ϵ] - - - - if transient_duration == 0 - transient_duration = sum(L * nmax) - end - if steady_state_duration == 0 - if isempty(monitors) - steady_state_duration = 1 - else - v = reduce(vcat, wavelengths.(monitor_instances)) - v = v |> Set |> collect |> sort |> reverse - if length(v) == 1 - steady_state_duration = 6 - else - steady_state_duration = 6 / minimum(diff([0, (1 ./ v)...])) - end - end - end - transient_duration, steady_state_duration = convert.(F, (transient_duration, steady_state_duration)) - global res = (; - grid, - source_instances, monitor_instances, field_names, - mode_deltas, - polarization, Courant, - transient_duration, steady_state_duration, - geometry, _geometry, nmax, nmin, - is_field_on_lb, is_field_on_ub, - u0, dt, kw...) |> pairs |> OrderedDict - -end -update = update -setup = setup \ No newline at end of file diff --git a/src/sim/solve.jl b/src/sim/solve.jl deleted file mode 100644 index e088441..0000000 --- a/src/sim/solve.jl +++ /dev/null @@ -1,150 +0,0 @@ -function f1(((u,), p, (dt, field_diffdeltas, field_diffpadvals, source_instances)), t) - u = update(u, p, t, dt, field_diffdeltas, field_diffpadvals, source_instances) - ((u,), p, (dt, field_diffdeltas, field_diffpadvals, source_instances)) -end - -function f2(((u, mf), p, (dt, field_diffdeltas, field_diffpadvals, source_instances), (T, monitor_instances)), t) - u = update(u, p, t, dt, field_diffdeltas, field_diffpadvals, source_instances;) - mf += dt / T * [[ - begin - # global _b = u - namedtuple([k => (field(u, k, m) * cispi(-2t / λ)) for k = keys(u)]) - end for λ = wavelengths(m) - ] for m = monitor_instances] - ((u, mf), p, (dt, field_diffdeltas, field_diffpadvals, source_instances), (T, monitor_instances)) -end - -function solve(prob, ; - save_memory=false, ulims=(-3, 3), framerate=0, path="", - kwargs...) - @unpack mode_deltas, polarization, dt, u0, geometry, _geometry, source_instances, monitor_instances, transient_duration, steady_state_duration, = prob - @unpack F, N, sz, field_diffdeltas, field_diffpadvals, field_lims, dl, spacings, geometry_padvals, geometry_padamts, _geometry_padamts = prob.grid - - p = geometry - _p = _geometry - # return sum(_p.ϵ) - - # ϵ = downsample(_geometry.ϵ, int(deltas / dl)) - # p[:ϵ] = ϵ - - p = pad_geometry(p, geometry_padvals, geometry_padamts) - p = apply_subpixel_averaging(p, field_lims) - - _p = pad_geometry(_p, geometry_padvals, _geometry_padamts) - invϵ = tensorinv(_p.ϵ, values(field_lims(r"E.*")), spacings) - - p = merge(p, (; invϵ)) - durations = [transient_duration, steady_state_duration] - T = cumsum(durations) - us0 = (u0,) - init = (us0, p, (dt, field_diffdeltas, field_diffpadvals, source_instances)) - - if save_memory - (u,), = adjoint_reduce(f1, 0:dt:T[1], init, ulims) - else - (u,), = reduce(0:dt:T[1]; init) do us, t - ignore() do - if framerate > 0 && t > 0 - if t % (1 / framerate) < dt - (u,), p, = us - a = u.Hz - g = p.ϵxx - - _path = joinpath(path, "temp") - mkpath(_path) - CairoMakie.save(joinpath(_path, "$t.png"), quickie(a, g; monitor_instances, source_instances, ulims),) - # quickie(a, g; monitor_instances, source_instances) - end - end - end - f1(us, t) - end - end - ts = T[1]+dt:dt:T[2]+F(0.001) - init = ((u, 0), p, (dt, field_diffdeltas, field_diffpadvals, source_instances), (durations[2], monitor_instances)) - - if save_memory - (u, mf), = adjoint_reduce(f2, ts, init, ulims) - else - (u, mf), = reduce(f2, ts; init) - end - # return sum(abs, mf[1][1].Ex + mf[1][1].Ey + mf[1][1].Hz) - ulims = 0 - # ulims = ignore_derivatives() do - # map(vcat(extrema.(leaves(u)), [ - # begin - # c = maximum(abs.(a)) - # (-c, c) - # end for a = leaves(mf) - # ])) do l - # l[1] == l[2] ? convert.(F, (-1, 1)) : l - # end - # end - - - v = map(mf, monitor_instances) do mf, m - map(mf, wavelengths(m)) do u, λ - dftfields = permutexyz(u, m.dimsperm, N) - # if N == 2 - # if polarization == :TE - # Ex, Hy, Ez = invreframe(frame(m), vcat(E, H)) - # Ex += 0sum(Ez[1:2]) - # Hy += 0sum(Ez[1:2]) - # dftfields = (; Ex, Hy, Ez) - # else - # Hx, Ey, Hz = invreframe(frame(m), vcat(H, E)) - # Hx += 0real(Hz[1]) - # Ey += 0real(Hz[1]) - # dftfields = (; Hx, Ey, Hz) - # end - # elseif N == 3 - # Ex, Ey, Ez, = invreframe(frame(m), E) - # Hx, Hy, Hz = invreframe(frame(m), H) - # dftfields = (; Ex, Ey, Ez, Hx, Hy, Hz) - # end - # dftfields = keepxy(dftfields) - fp = rp = c = nothing - if !isnothing(m.λmodes) - wm = m.λmodes[λ] - c = mode_decomp.(wm, (dftfields,), (mode_deltas,)) - # fp = [abs(v[1])^2 for v = c] - # rp = [abs(v[2])^2 for v = c] - end - - dftfields, c - end - end - um = [[v[1] for v = v] for v in v] - ap = [[isnothing(v[2]) ? nothing : getindex.(v[2], 1) for v = v] for v in v] - am = [[isnothing(v[2]) ? nothing : getindex.(v[2], 2) for v = v] for v in v] - return Solution(u, p, _p, ulims, um, ap, am) -end - -struct Solution - u - p - _p - ulims - um - ap - am -end -@functor Solution - -function (s::Solution)(k, m, w=1, mn=0) - @unpack u, ulims, um, ap, am = s - if k == "a+" - return s.ap[m][w][mn+1] - elseif k == "a-" - return s.am[m][w][mn+1] - elseif k == "P_TE" - return flux(um[m][w], :TE) - elseif k == "P_TM" - return flux(um[m][w], :TM) - elseif k == "P" - return flux(um[m][w]) - elseif k == "um" - return um[m][w] - end -end -# heatmap(___p.invϵ[1, 1]) \ No newline at end of file