diff --git a/Project.toml b/Project.toml index 4971895..b36ad9d 100644 --- a/Project.toml +++ b/Project.toml @@ -26,6 +26,7 @@ SparseVariables = "2749762c-80ed-4b14-8f33-f0736679b02b" TimeStruct = "f9ed5ce0-9f41-4eaa-96da-f38ab8df101c" XLSX = "fdbf4ff8-1666-58a4-91e7-1b58723a45e0" YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6" +InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" [weakdeps] EnergyModelsGeography = "3f775d88-a4da-46c4-a2cc-aa9f16db6708" @@ -58,3 +59,4 @@ TimeStruct = "0.9" XLSX = "0.10" YAML = "0.4" julia = "1.10" +InteractiveUtils = "1" diff --git a/src/EnergyModelsGUI.jl b/src/EnergyModelsGUI.jl index 6c47c4c..8aa66ee 100644 --- a/src/EnergyModelsGUI.jl +++ b/src/EnergyModelsGUI.jl @@ -23,6 +23,9 @@ using Colors # To format numbers with @sprintf using Printf +# To retrieve supertypes +using InteractiveUtils + # Use GLMakie front end to visualize the GUI figure using GLMakie diff --git a/src/descriptive_names.yml b/src/descriptive_names.yml index 02b0075..05a34de 100644 --- a/src/descriptive_names.yml +++ b/src/descriptive_names.yml @@ -3,36 +3,17 @@ structures: # EnergyModelsBase ## node.jl - StorCapOpex: + AbstractStorageParameters: capacity: "Installed capacity" opex_var: "Relative variable operating expense per energy unit" opex_fixed: "Relative fixed operating expense per installed capacity" - StorCap: - capacity: "Installed capacity" - - StorCapOpexVar: - capacity: "Installed capacity" - opex_var: "Relative variable operating expense per energy unit" - - StorCapOpexFixed: - capacity: "Installed capacity" - opex_fixed: "Relative fixed operating expense per installed capacity" - - StorOpexVar: - opex_var: "Relative variable operating expense per energy unit" - - RefSource: - cap: "Installed capacity" - opex_var: "Relative variable operating expense per energy unit produced" - opex_fixed: "Relative fixed operating expense per installed capacity" - - RefNetworkNode: + Node: cap: "Installed capacity" opex_var: "Relative variable operating expense per energy unit produced" opex_fixed: "Relative fixed operating expense per installed capacity" - RefSink: + Sink: cap: "Demand" penalty: surplus: "Penalties for surplus" @@ -40,90 +21,42 @@ structures: # EnergyModelsGeography ## mode.jl - RefDynamic: - trans_cap: "Capacity of the transmission mode" - trans_loss: "Relative loss of the transported resource during transmission" - opex_var: "Relative variable operating expense per energy unit transported" - opex_fixed: "Relative fixed operating expense per installed capacity" - - RefStatic: - trans_cap: "Capacity of the transmission mode" - trans_loss: "Relative loss of the transported resource during transmission" - opex_var: "Relative variable operating expense per energy unit transported" - opex_fixed: "Relative fixed operating expense per installed capacity" - - PipeSimple: - consumption_rate: "Rate at which the resource is consumed, as a ratio of the volume of the resource going into the inlet" + TransmissionMode: trans_cap: "Capacity of the transmission mode" trans_loss: "Relative loss of the transported resource during transmission" opex_var: "Relative variable operating expense per energy unit transported" opex_fixed: "Relative fixed operating expense per installed capacity" - - PipeLinepackSimple: consumption_rate: "Rate at which the resource is consumed, as a ratio of the volume of the resource going into the inlet" - trans_cap: "Capacity of the transmission mode" - trans_loss: "Relative loss of the transported resource during transmission" - opex_var: "Relative variable operating expense per energy unit transported" - opex_fixed: "Relative fixed operating expense per installed capacity" # EnergyModelsInvestment ## investment_data.jl - NoStartInvData: - capex: "Capital costs for investing in a capacity" - max_inst: "Maximum installed capacity in a strategic period" - - StartInvData: + AbstractInvData: capex: "Capital costs for investing in a capacity" max_inst: "Maximum installed capacity in a strategic period" initial: "Initial capacity in the first strategic period" ## investment_mode.jl + Investment: + min_add: "Minimum added capacity in a strategic period" + max_add: "Maximum added capacity in a strategic period" + increment: "Used increment for discrete investments" + capex_offset: "Offset for the CAPEX in a strategic period" FixedInvestment: cap: "Capacity used for the fixed investments" BinaryInvestment: cap: "Capacity used for the binary investments" - DiscreteInvestment: - increment: "Used increment for discrete investments" - - ContinuousInvestment: - min_add: "Minimum added capacity in a strategic period" - max_add: "Maximum added capacity in a strategic period" - - SemiContinuousInvestment: - min_add: "Minimum added capacity in a strategic period" - max_add: "Maximum added capacity in a strategic period" - - SemiContinuousOffsetInvestment: - max_add: "Maximum added capacity in a strategic period" - min_add: "Minimum added capacity in a strategic period" - capex_offset: "Offset for the CAPEX in a strategic period" - ## lifetime_mode.jl - StudyLife: - lifetime: "Chosen lifetime of the technology" - - PeriodLife: - lifetime: "Chosen lifetime of the technology" - - RollingLife: + LifetimeMode: lifetime: "Chosen lifetime of the technology" # EnergyModelsRenewableProducers ## datastructures.jl - NonDisRES: - cap: "Installed capacity" + AbstractNonDisRES: profile: "Power production profile as a ratio of installed capacity" - opex_var: "Relative variable operating expense per energy unit produced" - opex_fixed: "Relative fixed operating expense per installed capacity" - - HydroStor: - level_init: "Initial stored energy in the dam" - level_inflow: "Inflow of power per operational period" - level_min: "Minimum fraction of the reservoir capacity required" - PumpedHydroStor: + HydroStorage: level_init: "Initial stored energy in the dam" level_inflow: "Inflow of power per operational period" level_min: "Minimum fraction of the reservoir capacity required" @@ -141,17 +74,17 @@ structures: opex_var: "Variable operational costs per water flow" opex_fixed: "Fixed operational costs" - HydroGenerator: - cap: "Installed discharge or power capacity" + HydroUnit: opex_var: "Variable operational costs per energy unit produced" opex_fixed: "Fixed operational costs" + HydroGenerator: + cap: "Installed discharge or power capacity" + HydroPump: cap: "Installed pumping capacity" - opex_var: "Variable operational costs per energy unit produced" - opex_fixed: "Fixed operational costs" - CycleLife: + AbstractBatteryLife: stack_cost: "Relative cost for replacing a battery stack" # EnergyModelsHeat @@ -162,22 +95,9 @@ structures: ## node.jl HeatPump: - cap: "Installed capacity" t_source: "Heat source temperature" t_sink: "Heat sink temperature" eff_carnot: "Carnot Efficiency" - opex_var: "Variable operating expense per energy unit produced" - opex_fixed: "Fixed operating expense per installed capacity" - - HeatExchanger: - cap: "Installed capacity" - opex_var: "Variable operating expense per energy unit produced" - opex_fixed: "Fixed operating expense per installed capacity" - - DirectHeatUpgrade: - cap: "Installed capacity" - opex_var: "Variable operating expense per energy unit produced" - opex_fixed: "Fixed operating expense per installed capacity" ## resource.jl ResourceHeat: @@ -186,52 +106,27 @@ structures: # EnergyModelsHydrogen ## node.jl - Electrolyzer: - cap: "Installed capacity" - opex_var: "Variable operating expense per capacity used" - opex_fixed: "Fixed operating expense per installed capacity" - stack_replacement_cost: "Replacement cost of electrolyzer stacks" - - SimpleElectrolyzer: - cap: "Installed capacity" + AbstractElectrolyzer: opex_var: "Variable operating expense per capacity used" - opex_fixed: "Fixed operating expense per installed capacity" stack_replacement_cost: "Replacement cost of electrolyzer stacks" CommitParameters: opex: "Operating cost per installed capacity and operational duration" time: "Minimum time node must remain in a state before transitioning" - RampBi: - up: "Maximum positive rate of change of a node" - down: "Maximum negative rate of change of a node" - - RampUp: + AbstractRampParameters: up: "Maximum positive rate of change of a node" - - RampDown: down: "Maximum negative rate of change of a node" - Reformer: - cap: "Installed capacity" - opex_var: "Variable operating expense per capacity usage" - opex_fixed: "Fixed operating expense per installed capacity" - # EnergyModelsCO2 CO2Source: - cap: "Installed capacity" opex_var: "Variable operating expense per energy unit produced" - opex_fixed: "Fixed operating expense" RefNetworkNodeRetrofit: - cap: "Installed capacity" opex_var: "Variable operating expense per energy unit produced" - opex_fixed: "Fixed operating expense" CCSRetroFit: - cap: "Installed capacity" opex_var: "Variable operating expense per unit of CO₂ captured" - opex_fixed: "Fixed operating expense" variables: diff --git a/src/utils_GUI/GUI_utils.jl b/src/utils_GUI/GUI_utils.jl index 679b670..047785d 100644 --- a/src/utils_GUI/GUI_utils.jl +++ b/src/utils_GUI/GUI_utils.jl @@ -625,16 +625,19 @@ function update_descriptive_names!(gui::GUI) joinpath(@__DIR__, "..", "descriptive_names.yml"); dicttype = Dict{Symbol,Any}, ) - # Get a dictionary of installed packages - installed_packages = installed() + # Get a dictionary of loaded packages + loaded_packages = loaded() # Filter packages with names matching the pattern "EnergyModels*" - emx_packages = filter(pkg -> occursin(r"EnergyModels", pkg), keys(installed_packages)) - - # Search through EMX packages if icons are available there + emx_packages = filter(pkg -> occursin(r"EnergyModels", pkg), loaded_packages) + # apply inheritances for fetching descriptive names + # create a dictionary were the keys are all the types defined in emx_packages and the values are the types they inherit from + emx_supertypes_dict = get_supertypes(emx_packages) + inherit_descriptive_names_from_supertypes!(descriptive_names, emx_supertypes_dict) for package ∈ emx_packages package_path::Union{String,Nothing} = dirname(dirname(Base.find_package(package))) if !isnothing(package_path) + # check for presence of file to extend descriptive names path_to_descriptive_names_ext = joinpath( package_path, "ext", "EMGUIExt", "descriptive_names.yml", ) diff --git a/src/utils_gen/structures_utils.jl b/src/utils_gen/structures_utils.jl index 1dc6a84..baa45f4 100644 --- a/src/utils_gen/structures_utils.jl +++ b/src/utils_gen/structures_utils.jl @@ -14,6 +14,13 @@ function installed() return installs end +""" + function loaded() + +Get a list of loaded packages. +""" +loaded() = [String(n) for n ∈ names(Main, imported = true) if getfield(Main, n) isa Module] + """ place_nodes_in_circle(total_nodes::Int64, current_node::Int64, r::Float32, xₒ::Float32, yₒ::Float32) diff --git a/src/utils_gen/utils.jl b/src/utils_gen/utils.jl index 80557b3..f6c1705 100644 --- a/src/utils_gen/utils.jl +++ b/src/utils_gen/utils.jl @@ -270,3 +270,256 @@ function save_results(model::Model; directory = joinpath(pwd(), "csv_files")) YAML.write(io, metadata) end end + +""" + get_types(input) -> Vector{Symbol} + +Retrieves the names of all defined types from modules or packages. + +# Method Overloads + +- `get_types(modul::Module)`: + Returns a vector of type names defined in the given module. + +- `get_types(moduls::Vector{Module})`: + Returns a combined vector of type names from multiple modules. + +- `get_types(pkg::Union{String, Symbol})`: + Converts the package name to a module (via `Main`) and returns its defined types. + +- `get_types(pkgs::Union{Vector{<:Union{String, Symbol}}, Set{<:Union{String, Symbol}}})`: + Returns a combined vector of type names from multiple packages. + +# Arguments +- `input`: Can be a single module, a vector of modules, a single package name (as `String` or `Symbol`), or a collection of package names. + +# Returns +- `Vector{Symbol}`: A list of names corresponding to types defined in the given module(s) or package(s). + +# Description +This set of functions helps extract type definitions from Julia modules or packages. It filters out non-type bindings and collects only those that are instances of `DataType`. + +# Example +```julia +get_types(Base) # returns type names defined in Base + +get_types(["Base", "Core"]) # returns type names from both packages +``` +""" +function get_types(modul::Module) + types = [] + for name ∈ names(modul) + if isdefined(modul, name) && ( + getfield(modul, name) isa DataType || ( + getfield(modul, name) isa UnionAll && + Base.unwrap_unionall(getfield(modul, name)) isa DataType + ) + ) + push!(types, name) + end + end + return types +end + +get_types(modules::Vector{Module}) = [get_types(modul) for modul ∈ modules] +get_types(pkg::Union{String,Symbol}) = get_types(getfield(Main, Symbol(pkg))) +get_types(pkgs::Union{Vector{<:Union{String,Symbol}},Set{<:Union{String,Symbol}}}) = + get_types.(pkgs) + +""" + get_supertypes(input) -> Dict{Symbol, Vector{Type}} + +Retrieves the supertypes of all defined types from modules or packages. + +# Method Overloads + +- `get_supertypes(modul::Module)`: + Returns a dictionary mapping type names to their supertypes from the given module. + +- `get_supertypes(moduls::Vector{Module})`: + Merges and returns supertypes from multiple modules. + +- `get_supertypes(pkg::Union{String, Symbol})`: + Converts the package name to a module (via `Main`) and returns its type supertypes. + +- `get_supertypes(pkgs::Union{Vector{<:Union{String, Symbol}}, Set{<:Union{String, Symbol}}})`: + Merges and returns supertypes from multiple packages via their names. + +# Arguments +- `input`: Can be a single module, a vector of modules, a single package name (as `String` or `Symbol`), or a collection of package names. + +# Returns +- `Dict{Symbol, Vector{Type}}`: A dictionary where each key is a type name and the value is a vector of its supertypes. + +# Description +This set of functions helps extract the inheritance hierarchy of types defined in Julia modules or packages. It filters out non-type bindings and collects supertypes using `supertypes`. + +""" +function get_supertypes(modul::Module) + types=Dict() + for name ∈ names(modul) + if isdefined(modul, name) && ( + getfield(modul, name) isa DataType || ( + getfield(modul, name) isa UnionAll && + Base.unwrap_unionall(getfield(modul, name)) isa DataType + ) + ) + types[name] = supertypes(getfield(modul, name)) + end + end + return types +end + +get_supertypes(moduls::Vector{Module}) = + merge!(Dict(), (get_supertypes(m) for m ∈ moduls)...) +get_supertypes(pkg::Union{String,Symbol}) = get_supertypes(getfield(Main, Symbol(pkg))) +get_supertypes(pkgs::Union{Vector{<:Union{String,Symbol}},Set{<:Union{String,Symbol}}}) = + merge!(Dict(), (get_supertypes(pkg) for pkg ∈ pkgs)...) + +""" + has_fields(type::Type) -> Bool + +Checks whether a given type is a concrete struct with at least one field. + +# Arguments +- `type::Type`: The type to be inspected. + +# Returns +- `Bool`: Returns `true` if the type is a concrete struct and has one or more fields; otherwise, returns `false`. + +# Description +This function combines three checks: +- `isstructtype(type)`: Ensures the type is a struct. +- `nfields(type) > 0`: Ensures the struct has at least one field. + +# Example +```julia +struct MyStruct + x::Int +end + +has_fields(MyStruct) # returns true + +abstract type AbstractType end +has_fields(AbstractType) # returns false +``` +""" +function has_fields(type) + t = type isa UnionAll ? Base.unwrap_unionall(type) : type + return (t isa DataType && isstructtype(t) && nfields(t) > 0) +end + +""" + update_tree!(current_lvl::Dict{Type, Union{Dict, Nothing}}, tmp_type::Type) -> Nothing + +Ensures that a given type exists as a key in the current level of a nested type dependency dictionary. + +# Arguments +- `current_lvl::Dict{Type, Union{Dict, Nothing}}`: The current level of the nested dictionary structure representing type dependencies. +- `tmp_type::Type`: The type to be added as a key in the current level if it does not already exist. + +# Behavior +If `tmp_type` is not already a key in `current_lvl`, it adds it with an empty dictionary as its value, preparing for further nesting. + +# Returns +- `Nothing`: This function modifies `current_lvl` in-place and does not return a value. + +""" +function update_tree!(current_lvl, tmp_type::Type) + if !haskey(current_lvl, tmp_type) + current_lvl[tmp_type] = Dict{Type,Union{Dict,Nothing}}() + end +end + +""" + get_types_structure(emx_supertypes_dict::Dict{Any, Vector{Type}}) -> Dict{Type, Union{Dict, Nothing}} + +Constructs a nested dictionary representing type dependencies from a dictionary of supertypes. + +# Arguments +- `emx_supertypes_dict::Dict{Any, Vector{Type}}`: A dictionary where each key corresponds to a type identifier, and the value is a vector of supertypes ordered from the most general to the most specific. + +# Returns +- `Dict{Type, Union{Dict, Nothing}}`: A nested dictionary structure where each type is a key pointing to its subtype hierarchy. Leaf nodes point to `nothing`. + +# Description +This function builds a tree-like structure of type dependencies by iterating through each type's supertypes and nesting them accordingly. It uses the helper function `update_tree!` to insert types into the correct level of the hierarchy. +``` +""" +function get_types_structure(emx_supertypes_dict) + # make a visualization of the type dependencies by building a nested dictionary of types + emx_type_dependencies = Dict{Type,Union{Dict,Nothing}}() + for (emx_type_id, emx_supertypes) ∈ emx_supertypes_dict + i = 0 + current_lvl = emx_type_dependencies + while i < length(emx_supertypes) + tmp_type = emx_supertypes[end-i] + update_tree!(current_lvl, tmp_type) + current_lvl = current_lvl[tmp_type] + i+=1 + end + end + return emx_type_dependencies +end + +""" + inherit_descriptive_names_from_supertypes!(descriptive_names, emx_supertypes_dict) + +Copies descriptive field names from supertypes to subtypes in the `descriptive_names` dictionary. + +# Arguments +- `descriptive_names::Dict`: A dictionary containing descriptive names for structure fields, + organized by type. +- `emx_supertypes_dict::Dict`: A dictionary mapping type identifiers to arrays of types, + where the first element is the type itself and the remaining elements are its supertypes. + +# Description +For each type in `emx_supertypes_dict`, this function checks if the type has fields. +For each field, it looks for descriptive names in the supertypes. +If a descriptive name exists for a field in a supertype but not in the subtype, + it copies the descriptive name from the supertype to the subtype. + +# Modifies +- Updates `descriptive_names` in-place by inheriting missing descriptive names from supertypes. +""" +function inherit_descriptive_names_from_supertypes!(descriptive_names, emx_supertypes_dict) + for (emx_type_id, emx_supertypes) ∈ emx_supertypes_dict + emx_type = emx_supertypes[1] + # check if emx_type has field names and if so retrieve them, otherwise continue + if !has_fields(emx_type) + continue + end + emx_type_fieldnames = fieldnames(emx_type) + for fname ∈ emx_type_fieldnames + for emx_supertype ∈ emx_supertypes[2:end] # skip first element as it is the type itself + #check if the supertype has an entry in descriptive names for fname + # Extract only what is after the dot in emx_supertype, if any + supertype_str = string(emx_supertype) + supertype_key = + occursin(r"\.", supertype_str) ? + match(r"\.([^.]+)$", supertype_str).captures[1] : supertype_str + if haskey(descriptive_names[:structures], Symbol(supertype_key)) && + haskey( + descriptive_names[:structures][Symbol(supertype_key)], + Symbol(fname), + ) + # if so, and if the emx_type does not have an entry for fname, copy it + if !haskey(descriptive_names[:structures], Symbol(emx_type)) || + !haskey( + descriptive_names[:structures][Symbol(emx_type)], + Symbol(fname), + ) + if !haskey(descriptive_names[:structures], Symbol(emx_type)) + descriptive_names[:structures][Symbol(emx_type)] = + Dict{Symbol,Any}() + end + descriptive_names[:structures][Symbol(emx_type)][Symbol(fname)] = + descriptive_names[:structures][Symbol(supertype_key)][Symbol( + fname, + )] + end + end + end + end + end +end diff --git a/test/runtests.jl b/test/runtests.jl index 1fa0808..1249485 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -41,6 +41,9 @@ global_logger(logger_new) # Test specific GUI functionalities related to interactivity include("test_interactivity.jl") + + # Test descriptive names functionalities + include("test_descriptive_names.jl") end end global_logger(logger_org) diff --git a/test/test_descriptive_names.jl b/test/test_descriptive_names.jl new file mode 100644 index 0000000..d8ac05d --- /dev/null +++ b/test/test_descriptive_names.jl @@ -0,0 +1,66 @@ +case, model, m, gui = run_case() + +# Test specific miscellaneous descriptive names +@testset "Test descriptive names" verbose = true begin + @testset "Test customizing descriptive names" begin + path_to_descriptive_names = joinpath(pkgdir(EMGUI), "src", "descriptive_names.yml") + str1 = "" + str2 = "" + str3 = "" + str4 = "" + str5 = "" + str6 = "" + descriptive_names_dict = Dict( + :structures => Dict( # Input parameter from the case Dict + :RefStatic => Dict(:trans_cap => str1, :opex_fixed => str2), + :RefDynamic => Dict(:opex_var => str3, :directions => str4), + ), + :variables => Dict( # variables from the JuMP model + :stor_discharge_use => str5, + :trans_cap_rem => str6, + ), + ) + gui2 = GUI( + case; + path_to_descriptive_names = path_to_descriptive_names, + descriptive_names_dict = descriptive_names_dict, + ) + descriptive_names = EMGUI.get_var(gui2, :descriptive_names) + @test descriptive_names[:structures][:RefStatic][:trans_cap] == str1 + @test descriptive_names[:structures][:RefStatic][:opex_fixed] == str2 + @test descriptive_names[:structures][:RefDynamic][:opex_var] == str3 + @test descriptive_names[:structures][:RefDynamic][:directions] == str4 + @test descriptive_names[:variables][:stor_discharge_use] == str5 + @test descriptive_names[:variables][:trans_cap_rem] == str6 + EMGUI.close(gui2) + end + + @testset "Test inheritance of descriptive names" begin + path_to_descriptive_names = joinpath(pkgdir(EMGUI), "src", "descriptive_names.yml") + descriptive_names_raw = + YAML.load_file(path_to_descriptive_names; dicttype = Dict{Symbol,Any}) + str1 = "Relative fixed operating expense per installed capacity" + str2 = "Initial stored energy in the dam" + gui3 = GUI( + case; + path_to_descriptive_names = path_to_descriptive_names, + ) + + @test descriptive_names_raw[:structures][:Node][:opex_fixed] == str1 + @test :StorCapOpexFixed ∉ keys(descriptive_names_raw[:structures]) + @test :RefNetworkNode ∉ keys(descriptive_names_raw[:structures]) + + @test descriptive_names_raw[:structures][:HydroStorage][:level_init] == str2 + @test :HydroStor ∉ keys(descriptive_names_raw[:structures]) + @test :PumpedHydroStor ∉ keys(descriptive_names_raw[:structures]) + + descriptive_names = EMGUI.get_var(gui3, :descriptive_names) + @test descriptive_names[:structures][:StorCapOpexFixed][:opex_fixed] == str1 + @test descriptive_names[:structures][:RefNetworkNode][:opex_fixed] == str1 + + @test descriptive_names[:structures][:HydroStor][:level_init] == str2 + @test descriptive_names[:structures][:PumpedHydroStor][:level_init] == str2 + EMGUI.close(gui3) + end +end +EMGUI.close(gui) diff --git a/test/test_interactivity.jl b/test/test_interactivity.jl index 7d1b38b..5c2846f 100644 --- a/test/test_interactivity.jl +++ b/test/test_interactivity.jl @@ -59,39 +59,6 @@ pin_plot_button = get_button(gui, :pin_plot) system = EMGUI.parse_case(case) @test Base.show(system) == dump(system; maxdepth = 1) end - - @testset "Test customizing descriptive names" begin - path_to_descriptive_names = joinpath(pkgdir(EMGUI), "src", "descriptive_names.yml") - str1 = "" - str2 = "" - str3 = "" - str4 = "" - str5 = "" - str6 = "" - descriptive_names_dict = Dict( - :structures => Dict( # Input parameter from the case Dict - :RefStatic => Dict(:trans_cap => str1, :opex_fixed => str2), - :RefDynamic => Dict(:opex_var => str3, :directions => str4), - ), - :variables => Dict( # variables from the JuMP model - :stor_discharge_use => str5, - :trans_cap_rem => str6, - ), - ) - gui2 = GUI( - case; - path_to_descriptive_names = path_to_descriptive_names, - descriptive_names_dict = descriptive_names_dict, - ) - descriptive_names = EMGUI.get_var(gui2, :descriptive_names) - @test descriptive_names[:structures][:RefStatic][:trans_cap] == str1 - @test descriptive_names[:structures][:RefStatic][:opex_fixed] == str2 - @test descriptive_names[:structures][:RefDynamic][:opex_var] == str3 - @test descriptive_names[:structures][:RefDynamic][:directions] == str4 - @test descriptive_names[:variables][:stor_discharge_use] == str5 - @test descriptive_names[:variables][:trans_cap_rem] == str6 - EMGUI.close(gui2) - end end # Test specific GUI functionalities