-
Notifications
You must be signed in to change notification settings - Fork 54
Add adapt_to_depletion_and_bias_voltage! #612
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -204,6 +204,76 @@ function _find_depletion_voltage_candidates(ϕᵨ::AbstractArray{T, 3}, ϕᵥ::A | |
| minimum(Umin), maximum(Umax) | ||
| end | ||
|
|
||
| """ | ||
| adapt_to_depletion_and_bias_voltage!(sim::Simulation{T}, dep::RealQuantity, bias::RealQuantity; | ||
| contact_id::Int = determine_bias_voltage_contact_id(sim.detector), | ||
| verbose::Bool = true, | ||
| kwargs...) where {T <: AbstractFloat} | ||
|
|
||
| Adapts a [`Simulation`](@ref) in place so that it matches a target depletion voltage `dep` | ||
| and bias voltage `bias`, **without re-solving the field**. | ||
|
|
||
| This works by exploiting the linearity of the electric potential in both the impurity density | ||
| and the applied bias. The impurity density model is rescaled by `f = dep / dep_sim`, where `dep_sim` | ||
| is the current depletion voltage estimated via [`estimate_depletion_voltage`](@ref), and the bias | ||
| contact potential is swapped to `bias`. The stored `sim.electric_potential` is updated accordingly via | ||
| superposition with the [`WeightingPotential`](@ref) of the bias contact, so no new field calculation | ||
| is performed. | ||
|
|
||
| `dep` and `bias` must share the same (non-zero) sign, and `bias` must exceed `dep` in magnitude | ||
| (the detector must be over-depleted at the operating voltage). | ||
|
|
||
| ## Arguments | ||
| * `sim::Simulation{T}`: [`Simulation`](@ref) to be adapted in place. | ||
| * `dep::RealQuantity`: Target depletion voltage. If no units are given, this value is parsed in units of `$(internal_voltage_unit)`. | ||
| * `bias::RealQuantity`: Target operating (bias) voltage. If no units are given, this value is parsed in units of `$(internal_voltage_unit)`. | ||
|
|
||
| ## Keywords | ||
| * `contact_id::Int`: The `id` of the [`Contact`](@ref) at which the bias voltage is applied. | ||
| The default is determined automatically via `determine_bias_voltage_contact_id(sim.detector)`. | ||
| * `verbose::Bool = true`: Activate or deactivate additional info output. Default is `true`. | ||
|
|
||
| Additional `kwargs...` are passed on to [`estimate_depletion_voltage`](@ref). | ||
|
|
||
| ## Example | ||
| ```julia | ||
| using SolidStateDetectors | ||
| sim = Simulation(SSD_examples[:InvertedCoax]) | ||
| calculate_electric_potential!(sim) | ||
| adapt_to_depletion_and_bias_voltage!(sim, 1000u"V", 1500u"V") | ||
| ``` | ||
|
|
||
| !!! note | ||
| The accuracy of the result depends on the precision of the initial simulation, since the | ||
| impurity density is rescaled rather than re-solved. | ||
|
|
||
| See also [`estimate_depletion_voltage`](@ref). | ||
| """ | ||
| function adapt_to_depletion_and_bias_voltage!(sim::Simulation{T}, dep::RealQuantity, bias::RealQuantity; | ||
| contact_id::Int = determine_bias_voltage_contact_id(sim.detector), | ||
| verbose::Bool = true, | ||
| kwargs...) where {T <: AbstractFloat} | ||
| if ismissing(sim.weighting_potentials[contact_id]) || sim.weighting_potentials[contact_id].grid != sim.electric_potential.grid | ||
| _adapt_weighting_potential_to_electric_potential_grid!(sim, contact_id) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In |
||
| end | ||
| dep::T = _parse_value(T, dep, internal_voltage_unit) | ||
| bias::T = _parse_value(T, bias, internal_voltage_unit) | ||
| @assert dep * bias > 0 "The depletion voltage ($(dep)$(internal_voltage_unit)) and operating voltage ($(bias)$(internal_voltage_unit)) must have the same (non-zero) sign." | ||
| @assert abs(bias) > abs(dep) "The operating voltage ($(bias)$(internal_voltage_unit)) must exceed the depletion voltage ($(dep)$(internal_voltage_unit)) in magnitude (the detector must be over-depleted)." | ||
|
hervasa2 marked this conversation as resolved.
|
||
| dep_sim = _parse_value(T, estimate_depletion_voltage(sim; contact_id = contact_id, verbose = verbose, kwargs...), internal_voltage_unit) | ||
|
hervasa2 marked this conversation as resolved.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make sense to allow the user to pass the expected depletion voltage also to the function? I can't judge how long |
||
| f = T(dep/dep_sim) | ||
| if verbose | ||
| @info """Adapting `sim.electric_potential`, | ||
| scaling `impurity_density_model` by $f, | ||
| and swapping in the contact potential on `sim.detector.contacts[$contact_id]` | ||
| such that the simulation matches the given depletion and bias voltages.""" | ||
| end | ||
| ϕV = sim.weighting_potentials[contact_id].data | ||
| sim.electric_potential.data .= f * sim.electric_potential.data .+ (bias - f * sim.detector.contacts[contact_id].potential) .* ϕV | ||
| sim.detector = SolidStateDetector(sim.detector, contact_id = contact_id, contact_potential = bias) | ||
| sim.detector = SolidStateDetector(sim.detector, f*sim.detector.semiconductor.impurity_density_model) | ||
| nothing | ||
| end | ||
| #= | ||
| """ | ||
| old_estimate_depletion_voltage( sim::Simulation{T}, contact_id::Int, field_sim_settings = (verbose = true,))::T | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,4 +32,29 @@ T = Float32 | |
| @test_throws Exception estimate_depletion_voltage(sim, -10, 0, tolerance = 20) | ||
| @test_throws Exception estimate_depletion_voltage(sim, 0u"kg", 20u"kg") | ||
| @test_logs (:info,) (:info,) (:warn, r".*not in the specified range.*") estimate_depletion_voltage(sim, U_est/3, 0) | ||
|
|
||
| # `adapt_to_depletion_and_bias_voltage!` rescales the impurity density and swaps in a new | ||
| # contact potential so the simulation matches a target depletion voltage `dep` and | ||
| # bias voltage `bias`, without re-solving the field. Check the round-trip: after adapting, | ||
| # the estimated depletion voltage should be ≈ `dep` and the bias contact should sit at `bias`. | ||
| dep_target = 2000u"V" | ||
| bias_target = 2500u"V" | ||
| imp_model_before = sim.detector.semiconductor.impurity_density_model | ||
| adapt_to_depletion_and_bias_voltage!(sim, dep_target, bias_target, check_for_depletion = false, verbose = false) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you also add tests to show that the errors are thrown correctly? |
||
| @test sim.detector.contacts[id].potential == SolidStateDetectors._parse_value(T, bias_target, SolidStateDetectors.internal_voltage_unit) | ||
| dep_sim = estimate_depletion_voltage(sim, check_for_depletion = false, verbose = false) | ||
| @test abs(dep_sim - dep_target) < 5u"V" | ||
| @test sim.detector.semiconductor.impurity_density_model != imp_model_before | ||
|
|
||
| # Re-run simulation in place and check depletion voltage matches again. This is a check that impurity_density_model and | ||
| # contact_potential where adapted correctly | ||
| timed_calculate_electric_potential!(sim, refinement_limits=0.01, depletion_handling=true) | ||
| @test abs(estimate_depletion_voltage(sim, check_for_depletion = false, verbose = false) - dep_sim) < 5u"V" | ||
|
|
||
| # Finally, compare to fresh simulation which is changed manually | ||
| sim_fresh = Simulation{T}(joinpath(@__DIR__, "test_config_files/BEGe_01.yaml")) | ||
| sim_fresh.detector = SolidStateDetector(sim_fresh.detector, contact_id = id, contact_potential = bias_target) | ||
| sim_fresh.detector = SolidStateDetector(sim_fresh.detector, sim.detector.semiconductor.impurity_density_model) | ||
| timed_calculate_electric_potential!(sim_fresh, refinement_limits=0.01, depletion_handling=true) | ||
| @test abs(estimate_depletion_voltage(sim_fresh, check_for_depletion = false, verbose = false) - dep_sim) < 5u"V" | ||
| end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like we can probably find a better name to make clear that ONLY the electric potential (but e.g. not the electric field) is updated.
adaptto avoid confusion withadaptfrom Adapt.jl? I know we usedadaptfor the internal function to map the grids of electric and weighting potentials, but for an exported function I would like to be a bit more careful with wording.biasand just lets thebiasremain whatever it was before. Same thing if someone were to just change the bias without planning on changing the depletion voltage. Would it make sense to split this into two functions (one called something likeupdate_electric_potential_to_match_depletion!and the other oneupdate_electric_potential_to_match_bias!-- these are probably not the best names to choose from), which are then called by this overall function that allows to change both if wanted? Not sure what's the best way to go here, so I'm open for suggestions.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am in favor of splitting it into
update_electric_potential_to_match_depletion!andupdate_electric_potential_to_match_bias!. But I would not add a third function that calls both. I am happy with the names you choseThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I like the verb
update, because that's usually the verb we use to actually run the SOR, and this is explicitly not doing that, though 😅There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if there's any suggestion other than
updateoradapt, I'd be happy to discuss :)