diff --git a/src/expressions/dynamic.jl b/src/expressions/dynamic.jl index 7b200d69..d2adf72a 100644 --- a/src/expressions/dynamic.jl +++ b/src/expressions/dynamic.jl @@ -611,6 +611,18 @@ dyexp["pulse_schedule.ic.power.reference"] = dyexp["pulse_schedule.lh.power.reference"] = (time; lh, _...) -> sum(antenna.power.reference for antenna in lh.antenna) +#= ==== =# +# risk # +#= ==== =# +dyexp["risk.engineering.loss[:].risk"] = + (; loss, _...) -> (sum((fm.probability * fm.weight) for fm in loss.failure_mode) * loss.severity) + +dyexp["risk.plasma.risk"] = + (; dd, _...) -> isempty(dd.risk.plasma) ? 0.0 : sum(loss.risk for loss in dd.risk.plasma.loss) + +dyexp["risk.engineering.risk"] = + (; dd, _...) -> isempty(dd.risk.engineering) ? 0.0 : sum(loss.risk for loss in dd.risk.engineering.loss) + #= ====== =# # limits # #= ====== =# diff --git a/src/extract/extract.jl b/src/extract/extract.jl index 05938ad3..7b728f73 100644 --- a/src/extract/extract.jl +++ b/src/extract/extract.jl @@ -249,7 +249,7 @@ function print_tiled(io::IO, xtract::AbstractDict{Symbol,ExtractFunction}; termi end end -function select_direct_captial_cost(dd::IMAS.dd, what::String) +function select_direct_capital_cost(dd::IMAS.dd, what::String) for sys in dd.costing.cost_direct_capital.system idx = findfirst(x -> x.name == what, sys.subsystem) if !isnothing(idx) @@ -380,6 +380,9 @@ function update_ExtractFunctionsLibrary!() ExtractLibFunction(:costing, :blanket_of_total, "%", dd -> 100 * select_direct_captial_cost(dd,"blanket") / dd.costing.cost_direct_capital.cost) ExtractLibFunction(:costing, :cryostat_of_total, "%", dd -> 100 * select_direct_captial_cost(dd,"cryostat") / dd.costing.cost_direct_capital.cost) + ExtractLibFunction(:risk, :total_engineering_risk, "\$M", dd -> dd.risk.engineering.risk) + ExtractLibFunction(:risk, :total_plasma_risk, "\$/kWh", dd -> dd.risk.plasma.risk) + #! format: on return EFL diff --git a/src/extract/objectives.jl b/src/extract/objectives.jl index d43b0b0f..7fa28f92 100644 --- a/src/extract/objectives.jl +++ b/src/extract/objectives.jl @@ -97,6 +97,9 @@ 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) + ObjectiveFunction(:min_engineering_risk, "\$M", dd -> dd.risk.engineering.risk, -Inf) + ObjectiveFunction(:min_plasma_risk, "\$/kWh", dd -> dd.risk.plasma.risk, -Inf) + #! format: on return ObjectiveFunctionsLibrary end diff --git a/src/plot.jl b/src/plot.jl index 8d90c8a9..4980fce3 100644 --- a/src/plot.jl +++ b/src/plot.jl @@ -451,6 +451,103 @@ end end end +# ==== # +# risk # +# ==== # + +@recipe function plot_risk(rsk::IMAS.risk) + eng_loss = rsk.engineering.loss + eng_names = ["$(sys_loss.description)" for sys_loss in reverse(eng_loss)] + eng_risks = [sys_loss.risk for sys_loss in reverse(eng_loss)] + eng_perc = ["$(round(sys_loss.risk/sum(eng_risks)*100))%" for sys_loss in reverse(eng_loss)] + + plasma_loss = rsk.plasma.loss + plasma_names = ["$(sys_loss.description)" for sys_loss in reverse(plasma_loss)] + plasma_risks = [sys_loss.risk for sys_loss in reverse(plasma_loss)] + plasma_perc = ["$(round(sys_loss.risk/sum(plasma_risks)*100))%" for sys_loss in reverse(plasma_loss)] + + dd = parent(rsk) + direct_capital_cost = dd.costing.cost_direct_capital.cost + levelized_cost = dd.costing.levelized_CoE + + size --> (800, 800) + layout := RecipesBase.@layout (2, 2) + + @series begin + subplot := 1 + seriestype := :bar + orientation := :horizontal + title := "Engineering risk" * " " * @sprintf("[Total = %.3g \$M]", sum(eng_risks)) + titlefontsize := 10 + ylim := (0, length(eng_risks)) + label := "" + annotation := [(0.0, kk - 0.5, (" $x $(titlecase(n,strict=false))", :left, 8)) for (kk, (c, x, n)) in enumerate((collect(zip(eng_risks, eng_perc, eng_names))))] + annotationvalign := :center + label := "" + xticks := 0:round(maximum(eng_risks) / 4, digits = 1):round(maximum(eng_risks), digits = 1) + xlim := (0, maximum(eng_risks)) + xlabel := "[\$M]" + showaxis := :x + yaxis := nothing + alpha := 0.5 + linecolor := :match + color := PlotUtils.palette(:tab10)[1] + eng_names, eng_risks + end + + @series begin + subplot := 2 + seriestype := :bar + orientation := :horizontal + title := "Plasma risk" * " " * @sprintf("[Total = %.3g \$/kWh]", sum(plasma_risks)) + titlefontsize := 10 + ylim := (0, length(plasma_risks)) + label := "" + annotation := [(0.0, kk - 0.5, (" $x $(titlecase(n,strict=false))", :left, 8)) for (kk, (c, x, n)) in enumerate((collect(zip(plasma_risks, plasma_perc, plasma_names))))] + annotationvalign := :center + label := "" + xticks := 0:round(maximum(plasma_risks) / 4, digits = 1):round(maximum(plasma_risks), digits = 1) + xlim := (0, maximum(plasma_risks)) + xlabel := "[\$/kWh]" + showaxis := :x + yaxis := nothing + alpha := 0.5 + linecolor := :match + color := PlotUtils.palette(:tab10)[2] + plasma_names, plasma_risks + end + + @series begin + subplot := 3 + seriestype := :bar + label := "Direct capital cost with error" + ylabel := "Direct capital cost (\$M)" + orientation := :vertical + legend := :bottomleft + title := "Error on direct capital cost" + alpha := 0.5 + yerror := [rsk.engineering.risk] + color := PlotUtils.palette(:tab10)[1] + [""], [direct_capital_cost] + end + + @series begin + subplot := 4 + seriestype := :bar + label := "Levelized cost with error" + ylabel := "Levelized cost of electricity (\$/kWh)" + orientation := :vertical + legend := :bottomleft + title := "Error on levelized cost" + right_ylabel := "Levelized cost of electricity with error" + alpha := 0.5 + yerror := [rsk.plasma.risk] + color := PlotUtils.palette(:tab10)[2] + [""], [levelized_cost] + end + +end + # =========== # # equilibrium # # =========== #