From daaee383537ee10b4be1a8b837d246094d3c6572 Mon Sep 17 00:00:00 2001 From: Francisco Munguia-Wulftange Date: Mon, 21 Jul 2025 15:41:43 -0700 Subject: [PATCH 1/8] Update objectives.jl --- src/extract/objectives.jl | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/extract/objectives.jl b/src/extract/objectives.jl index c6d862b1..8da245e7 100644 --- a/src/extract/objectives.jl +++ b/src/extract/objectives.jl @@ -81,9 +81,37 @@ end # ========================= # const ObjectiveFunctionsLibrary = OrderedCollections.OrderedDict{Symbol,ObjectiveFunction}() +# Helper function for Greenwald fraction calculation +function calculate_greenwald_fraction(dd::IMAS.dd) + try + eqt = dd.equilibrium.time_slice[] + cp1d = dd.core_profiles.profiles_1d[] + ne_line = IMAS.ne_line(eqt, cp1d) + Ip_MA = eqt.global_quantities.ip / 1e6 # Convert to MA + a_minor = eqt.boundary.minor_radius # Get minor radius from equilibrium + n_Greenwald = (Ip_MA / (π * a_minor^2)) * 1e20 # Greenwald limit [m⁻³] + return ne_line / n_Greenwald + catch + return 0.0 + end +end + +# Helper function for bootstrap fraction calculation +function calculate_bootstrap_fraction(dd::IMAS.dd) + try + I_bootstrap = @ddtime(dd.summary.global_quantities.current_bootstrap.value) + I_p = dd.equilibrium.time_slice[].global_quantities.ip + return I_bootstrap / I_p + catch + return 0.0 + end +end + function update_ObjectiveFunctionsLibrary!() empty!(ObjectiveFunctionsLibrary) #! format: off + + # Original FUSE objectives ObjectiveFunction(:min_levelized_CoE, "\$/kWh", dd -> dd.costing.levelized_CoE, -Inf) ObjectiveFunction(:min_log10_levelized_CoE, "log₁₀(\$/kW)", dd -> log10(dd.costing.levelized_CoE), -Inf) ObjectiveFunction(:min_capital_cost, "\$B", dd -> dd.costing.cost_direct_capital.cost / 1E3, -Inf) @@ -97,6 +125,14 @@ function update_ObjectiveFunctionsLibrary!() ObjectiveFunction(:min_βn, "", dd -> dd.equilibrium.time_slice[].global_quantities.beta_normal, -Inf) ObjectiveFunction(:min_R0, "m", dd -> dd.equilibrium.time_slice[].boundary.geometric_axis.r, -Inf) ObjectiveFunction(:max_zeff, "", dd -> @ddtime(dd.summary.volume_average.zeff.value), Inf) + + # New physics objectives for my studies + ObjectiveFunction(:max_βp, "", dd -> dd.equilibrium.time_slice[].global_quantities.beta_pol, Inf) + ObjectiveFunction(:max_h98, "", dd -> @ddtime(dd.summary.global_quantities.h_98.value), Inf) + ObjectiveFunction(:max_tau_e, "s", dd -> @ddtime(dd.summary.global_quantities.tau_energy.value), Inf) + ObjectiveFunction(:max_fbs, "", dd -> calculate_bootstrap_fraction(dd), Inf) + ObjectiveFunction(:max_greenwald_fraction, "", dd -> calculate_greenwald_fraction(dd), Inf) + #! format: on return ObjectiveFunctionsLibrary end From f71469dd66bad42908559a36cd2a5dc2a42c0961 Mon Sep 17 00:00:00 2001 From: Francisco Munguia-Wulftange Date: Mon, 21 Jul 2025 15:41:43 -0700 Subject: [PATCH 2/8] Update objectives.jl --- src/extract/objectives.jl | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/extract/objectives.jl b/src/extract/objectives.jl index c6d862b1..8da245e7 100644 --- a/src/extract/objectives.jl +++ b/src/extract/objectives.jl @@ -81,9 +81,37 @@ end # ========================= # const ObjectiveFunctionsLibrary = OrderedCollections.OrderedDict{Symbol,ObjectiveFunction}() +# Helper function for Greenwald fraction calculation +function calculate_greenwald_fraction(dd::IMAS.dd) + try + eqt = dd.equilibrium.time_slice[] + cp1d = dd.core_profiles.profiles_1d[] + ne_line = IMAS.ne_line(eqt, cp1d) + Ip_MA = eqt.global_quantities.ip / 1e6 # Convert to MA + a_minor = eqt.boundary.minor_radius # Get minor radius from equilibrium + n_Greenwald = (Ip_MA / (π * a_minor^2)) * 1e20 # Greenwald limit [m⁻³] + return ne_line / n_Greenwald + catch + return 0.0 + end +end + +# Helper function for bootstrap fraction calculation +function calculate_bootstrap_fraction(dd::IMAS.dd) + try + I_bootstrap = @ddtime(dd.summary.global_quantities.current_bootstrap.value) + I_p = dd.equilibrium.time_slice[].global_quantities.ip + return I_bootstrap / I_p + catch + return 0.0 + end +end + function update_ObjectiveFunctionsLibrary!() empty!(ObjectiveFunctionsLibrary) #! format: off + + # Original FUSE objectives ObjectiveFunction(:min_levelized_CoE, "\$/kWh", dd -> dd.costing.levelized_CoE, -Inf) ObjectiveFunction(:min_log10_levelized_CoE, "log₁₀(\$/kW)", dd -> log10(dd.costing.levelized_CoE), -Inf) ObjectiveFunction(:min_capital_cost, "\$B", dd -> dd.costing.cost_direct_capital.cost / 1E3, -Inf) @@ -97,6 +125,14 @@ function update_ObjectiveFunctionsLibrary!() ObjectiveFunction(:min_βn, "", dd -> dd.equilibrium.time_slice[].global_quantities.beta_normal, -Inf) ObjectiveFunction(:min_R0, "m", dd -> dd.equilibrium.time_slice[].boundary.geometric_axis.r, -Inf) ObjectiveFunction(:max_zeff, "", dd -> @ddtime(dd.summary.volume_average.zeff.value), Inf) + + # New physics objectives for my studies + ObjectiveFunction(:max_βp, "", dd -> dd.equilibrium.time_slice[].global_quantities.beta_pol, Inf) + ObjectiveFunction(:max_h98, "", dd -> @ddtime(dd.summary.global_quantities.h_98.value), Inf) + ObjectiveFunction(:max_tau_e, "s", dd -> @ddtime(dd.summary.global_quantities.tau_energy.value), Inf) + ObjectiveFunction(:max_fbs, "", dd -> calculate_bootstrap_fraction(dd), Inf) + ObjectiveFunction(:max_greenwald_fraction, "", dd -> calculate_greenwald_fraction(dd), Inf) + #! format: on return ObjectiveFunctionsLibrary end From 0847266d26de9887f2b7cc4073e9ed7f385fdde9 Mon Sep 17 00:00:00 2001 From: Francisco Munguia-Wulftange Date: Mon, 11 Aug 2025 08:31:36 -0700 Subject: [PATCH 3/8] Add get_from support for ne_sep --- src/get_from.jl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/get_from.jl b/src/get_from.jl index 3c8f3b45..90ce8a20 100644 --- a/src/get_from.jl +++ b/src/get_from.jl @@ -121,6 +121,21 @@ function get_from(dd::IMAS.dd{T}, what::Type{Val{:zeff_ped}}, from_where::Symbol return error("`get_from(dd, $what, Val{:$from_where})` doesn't exist yet") end +# ne_sep [m^-3] +function get_from(dd::IMAS.dd, what::Type{Val{:ne_sep}}, from_where::Symbol; time0::Float64=dd.global_time) + if from_where == :core_profiles + cp1d = dd.core_profiles.profiles_1d[time0] + return interp1d(cp1d.grid.rho_tor_norm, cp1d.electrons.density_thermal).(1.0) + elseif from_where == :pulse_schedule + if !ismissing(dd.pulse_schedule.density_control.n_e_separatrix, :reference) + return get_time_array(dd.pulse_schedule.density_control.n_e_separatrix, :reference, time0, :linear) + end + error("`get_from(dd, $what, Val{:$from_where})` does not have data") + end + return error("`get_from(dd, $what, Val{:$from_where})` doesn't exist yet") +end + + Base.Docs.@doc """ get_from(dd::IMAS.dd, what::Symbol, from_where::Symbol; time0::Float64=dd.global_time) From ce19b529c63eec66b5172c0226698af6d818956c Mon Sep 17 00:00:00 2001 From: Francisco Munguia-Wulftange Date: Fri, 15 Aug 2025 12:28:55 -0700 Subject: [PATCH 4/8] Add get_from() function for ne_sep Enables separatrix density retrival from core_profiles and pulse_schedule: - get_from(dd, Val{:ne_sep}, :core_profiles) - get_from(dd, Val{:ne_sep}, :pulse_schedule) --- src/get_from.jl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/get_from.jl b/src/get_from.jl index 90ce8a20..5a955d95 100644 --- a/src/get_from.jl +++ b/src/get_from.jl @@ -122,10 +122,10 @@ function get_from(dd::IMAS.dd{T}, what::Type{Val{:zeff_ped}}, from_where::Symbol end # ne_sep [m^-3] -function get_from(dd::IMAS.dd, what::Type{Val{:ne_sep}}, from_where::Symbol; time0::Float64=dd.global_time) +function get_from(dd::IMAS.dd{T}, what::Type{Val{:ne_sep}}, from_where::Symbol, rho_sep::Float64; time0::Float64=dd.global_time)::T where {T<:Real} if from_where == :core_profiles cp1d = dd.core_profiles.profiles_1d[time0] - return interp1d(cp1d.grid.rho_tor_norm, cp1d.electrons.density_thermal).(1.0) + return cp1d.electrons.density_thermal[end] # Last point is separatrix elseif from_where == :pulse_schedule if !ismissing(dd.pulse_schedule.density_control.n_e_separatrix, :reference) return get_time_array(dd.pulse_schedule.density_control.n_e_separatrix, :reference, time0, :linear) @@ -135,6 +135,10 @@ function get_from(dd::IMAS.dd, what::Type{Val{:ne_sep}}, from_where::Symbol; tim return error("`get_from(dd, $what, Val{:$from_where})` doesn't exist yet") end +function get_from(dd::IMAS.dd{T}, what::Type{Val{:ne_sep}}, from_where::Symbol, rho_sep::Nothing; time0::Float64=dd.global_time)::T where {T<:Real} + rho_sep = 1.0 + return get_from(dd, what, from_where, rho_sep; time0) +end Base.Docs.@doc """ get_from(dd::IMAS.dd, what::Symbol, from_where::Symbol; time0::Float64=dd.global_time) From a108ce280adea54f97199f8389303faa0f4fa841 Mon Sep 17 00:00:00 2001 From: Francisco Munguia-Wulftange Date: Mon, 18 Aug 2025 11:10:52 -0700 Subject: [PATCH 5/8] Update get_from.jl Removed rho_sep as it does not get used. --- src/get_from.jl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/get_from.jl b/src/get_from.jl index 5a955d95..b4208d58 100644 --- a/src/get_from.jl +++ b/src/get_from.jl @@ -122,10 +122,10 @@ function get_from(dd::IMAS.dd{T}, what::Type{Val{:zeff_ped}}, from_where::Symbol end # ne_sep [m^-3] -function get_from(dd::IMAS.dd{T}, what::Type{Val{:ne_sep}}, from_where::Symbol, rho_sep::Float64; time0::Float64=dd.global_time)::T where {T<:Real} +function get_from(dd::IMAS.dd{T}, what::Type{Val{:ne_sep}}, from_where::Symbol; time0::Float64=dd.global_time)::T where {T<:Real} if from_where == :core_profiles cp1d = dd.core_profiles.profiles_1d[time0] - return cp1d.electrons.density_thermal[end] # Last point is separatrix + return cp1d.electrons.density_thermal[end] elseif from_where == :pulse_schedule if !ismissing(dd.pulse_schedule.density_control.n_e_separatrix, :reference) return get_time_array(dd.pulse_schedule.density_control.n_e_separatrix, :reference, time0, :linear) @@ -135,9 +135,8 @@ function get_from(dd::IMAS.dd{T}, what::Type{Val{:ne_sep}}, from_where::Symbol, return error("`get_from(dd, $what, Val{:$from_where})` doesn't exist yet") end -function get_from(dd::IMAS.dd{T}, what::Type{Val{:ne_sep}}, from_where::Symbol, rho_sep::Nothing; time0::Float64=dd.global_time)::T where {T<:Real} - rho_sep = 1.0 - return get_from(dd, what, from_where, rho_sep; time0) +function get_from(dd::IMAS.dd{T}, what::Type{Val{:ne_sep}}, from_where::Symbol, time0::Float64=dd.global_time)::T where {T<:Real} + return get_from(dd, what, from_where; time0) end Base.Docs.@doc """ @@ -160,6 +159,8 @@ Supported quantities for `what`: - Possible sources (`from_where`): `:core_profiles`, `:summary`, `:pulse_schedule` - `:zeff_ped` - Effective charge at the pedestal [-] - Possible sources (`from_where`): `:core_profiles`, `:summary`, `:pulse_schedule` +- `:ne_sep` - Electron density at the separatrix [m^-3] + - Possible sources (`from_where`): `:core_profiles`, `:pulse_schedule` `time0` defines the time point at which to retrieve the value, default is `dd.global_time`. From 643f729c9fd91344b4c11bf586ff2defb34c4409 Mon Sep 17 00:00:00 2001 From: Francisco Munguia-Wulftange Date: Mon, 18 Aug 2025 11:20:36 -0700 Subject: [PATCH 6/8] Update objectives.jl --- src/extract/objectives.jl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/extract/objectives.jl b/src/extract/objectives.jl index 8da245e7..3aa91d62 100644 --- a/src/extract/objectives.jl +++ b/src/extract/objectives.jl @@ -81,7 +81,6 @@ end # ========================= # const ObjectiveFunctionsLibrary = OrderedCollections.OrderedDict{Symbol,ObjectiveFunction}() -# Helper function for Greenwald fraction calculation function calculate_greenwald_fraction(dd::IMAS.dd) try eqt = dd.equilibrium.time_slice[] @@ -96,7 +95,6 @@ function calculate_greenwald_fraction(dd::IMAS.dd) end end -# Helper function for bootstrap fraction calculation function calculate_bootstrap_fraction(dd::IMAS.dd) try I_bootstrap = @ddtime(dd.summary.global_quantities.current_bootstrap.value) From 4d61b9dd54172c86def7d2baa5c72c933183c0a7 Mon Sep 17 00:00:00 2001 From: Francisco Munguia-Wulftange Date: Tue, 2 Sep 2025 21:09:56 -0700 Subject: [PATCH 7/8] Update get_from.jl delete duplicate in get_from --- src/get_from.jl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/get_from.jl b/src/get_from.jl index ee13e595..40e9dac8 100644 --- a/src/get_from.jl +++ b/src/get_from.jl @@ -135,10 +135,6 @@ function get_from(dd::IMAS.dd{T}, what::Type{Val{:ne_sep}}, from_where::Symbol; return error("`get_from(dd, $what, Val{:$from_where})` doesn't exist yet") end -function get_from(dd::IMAS.dd{T}, what::Type{Val{:ne_sep}}, from_where::Symbol, time0::Float64=dd.global_time)::T where {T<:Real} - return get_from(dd, what, from_where; time0) -end - Base.Docs.@doc """ get_from(dd::IMAS.dd, what::Symbol, from_where::Symbol; time0::Float64=dd.global_time) From 3cdd41357bc8ea0f88ec326d235f708931e39396 Mon Sep 17 00:00:00 2001 From: Francisco Munguia-Wulftange Date: Tue, 23 Sep 2025 13:52:49 -0700 Subject: [PATCH 8/8] Update get_from.jl update syntax on get_from --- src/get_from.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/get_from.jl b/src/get_from.jl index 40e9dac8..ce80bc3e 100644 --- a/src/get_from.jl +++ b/src/get_from.jl @@ -122,7 +122,7 @@ function get_from(dd::IMAS.dd{T}, what::Val{:zeff_ped}, from_where::Symbol, rho_ end # ne_sep [m^-3] -function get_from(dd::IMAS.dd{T}, what::Type{Val{:ne_sep}}, from_where::Symbol; time0::Float64=dd.global_time)::T where {T<:Real} +function get_from(dd::IMAS.dd{T}, what::Val{:ne_sep}, from_where::Symbol; time0::Float64=dd.global_time)::T where {T<:Real} if from_where == :core_profiles cp1d = dd.core_profiles.profiles_1d[time0] return cp1d.electrons.density_thermal[end]