diff --git a/.JuliaFormatter.toml b/.JuliaFormatter.toml new file mode 100644 index 0000000..f196f10 --- /dev/null +++ b/.JuliaFormatter.toml @@ -0,0 +1,9 @@ +# Configuration file for JuliaFormatter.jl +# For more information, see: https://domluna.github.io/JuliaFormatter.jl/stable/config/ + +always_for_in = true +always_use_return = true +margin = 80 +remove_extra_newlines = true +separate_kwargs_with_semicolon = true +short_to_long_function_def = true diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..700707c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ba9a9b5..0d039c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,9 +1,7 @@ name: CI on: push: - branches: - - main - - release-* + branches: [main] pull_request: types: [opened, synchronize, reopened] # needed to allow julia-actions/cache to delete old caches that it has created @@ -17,29 +15,12 @@ jobs: strategy: fail-fast: false matrix: - # Only test on a subset of possible platforms. - include: - - version: '1' # The latest point-release (Linux) - os: ubuntu-latest - arch: x64 - - version: '1' # The latest point-release (Windows) - os: windows-latest - arch: x64 - - version: '1.10' # 1.10 LTS (64-bit Linux) - os: ubuntu-latest - arch: x64 - - version: '1.10' # 1.10 LTS (64-bit Windows) - os: windows-latest - arch: x64 - - version: '1.10' # 1.10 LTS (64-bit Mac Intel) - os: macos-15-intel - arch: x64 - - version: '1.10' # 1.10 LTS (64-bit Mac Arm) - os: macOS-latest - arch: arm64 + version: ['1.10', '1'] + os: [ubuntu-latest, macOS-latest, windows-latest] + arch: [default] steps: - uses: actions/checkout@v6 - - uses: julia-actions/setup-julia@v2 + - uses: julia-actions/setup-julia@v3 with: version: ${{ matrix.version }} arch: ${{ matrix.arch }} diff --git a/.github/workflows/format_check.yml b/.github/workflows/format_check.yml new file mode 100644 index 0000000..710b1ca --- /dev/null +++ b/.github/workflows/format_check.yml @@ -0,0 +1,29 @@ +name: format-check +on: + push: + branches: + - main + pull_request: + types: [opened, synchronize, reopened] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: julia-actions/setup-julia@latest + with: + version: '1' + - uses: actions/checkout@v6 + - name: Format check + shell: julia --color=yes {0} + run: | + using Pkg + Pkg.add(PackageSpec(name="JuliaFormatter", version="2")) + using JuliaFormatter + format(".", verbose=true) + out = String(read(Cmd(`git diff`))) + if isempty(out) + exit(0) + end + @error "Some files have not been formatted !!!" + write(stdout, out) + exit(1) diff --git a/README.md b/README.md index 1ad45ca..a0dad48 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,45 @@ # GDXInterface.jl -High-level Julia interface for reading and writing -[GDX files](https://gams-dev.github.io/gdx/index.html) -(GAMS Data Exchange). +[![Build Status](https://github.com/jd-foster/GDXInterface.jl/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/jd-foster/GDXInterface.jl/actions?query=workflow%3ACI) +[![codecov](https://codecov.io/gh/jd-foster/GDXInterface.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/jd-foster/GDXInterface.jl) -Uses the [`gdx_jll`](https://github.com/JuliaBinaryWrappers/gdx_jll.jl.git) package to -provide the GDX C library independently of GAMS. - -**No GAMS installation required.** +[GDXInterface.jl](https://github.com/jd-foster/GDXInterface.jl) is an unofficial +wrapper for [gams-dev/gdx](http://github.com/gams-dev/gdx), which provides +support for reading and writing [GDX (GAMS Data Exchange) files](https://gams-dev.github.io/gdx/index.html). For more information on the GDX file format, see the blog post -['GDX source code published on GitHub'](https://www.gams.com/blog/2023/12/gdx-source-code-published-on-github/). +[GDX source code published on GitHub](https://www.gams.com/blog/2023/12/gdx-source-code-published-on-github/). -## Installation +## Affiliation -```julia -using Pkg -Pkg.add(url="https://github.com/jd-foster/GDXInterface.jl.git") -``` +This package is an unofficial Julia wrapper of [gams-dev/gdx](https://github.com/gams-dev/gdx). +It is developed and maintained by the JuMP community. It is not an official +product by [GAMS](https://gams.com). -Or in the Pkg REPL: +## Getting help -``` -pkg> add https://github.com/jd-foster/GDXInterface.jl.git -``` +If you need help, please ask a question on the [JuMP community forum](https://jump.dev/forum). -Run tests with: +If you have a reproducible example of a bug, please [open a GitHub issue](https://github.com/jd-foster/GDXInterface.jl/issues/new). -``` -pkg> test GDXInterface +## License + +`GDXInterface.jl` is licensed under the [MIT License](https://github.com/jd-foster/GDXInterface.jl/blob/main/LICENSE.md). + +`GDXInterface.jl` wraps the [official GAMS GDX project](https://github.com/GAMS-dev/gdx), +which is also licensed under the [MIT License](https://github.com/GAMS-dev/gdx/blob/main/LICENSE). +You do not need a GAMS license to use `GDXInterface.jl`. + +## Installation + +Install `GDXInterface.jl` as follows: + +```julia +using Pkg +Pkg.add(; url = "https://github.com/jd-foster/GDXInterface.jl.git") ``` -These instructions will be updated if/when the package is registered. +You do not need a GAMS installation to use `GDXInterface.jl`. ## Quick Start @@ -141,13 +149,13 @@ for (k, v) in gdx ... # iterate over symbols GAMS special values are mapped to Julia equivalents when reading: -| GAMS | Julia | Notes | -|------|-------|-------| -| `UNDEF` | `NaN` | Undefined value | -| `NA` | `NaN` | Not available | -| `+INF` | `Inf` | Positive infinity | -| `-INF` | `-Inf` | Negative infinity | -| `EPS` | `-0.0` | "Explicitly zero" in sparse data | +| GAMS | Julia | Notes | +| :------ | :----- | :------------------------------- | +| `UNDEF` | `NaN` | Undefined value | +| `NA` | `NaN` | Not available | +| `+INF` | `Inf` | Positive infinity | +| `-INF` | `-Inf` | Negative infinity | +| `EPS` | `-0.0` | "Explicitly zero" in sparse data | When writing, `NaN` maps to GAMS `NA`, `Inf`/`-Inf` map to `+INF`/`-INF`, and `-0.0` maps back to GAMS `EPS`. This preserves EPS semantics through diff --git a/src/GDXFile.jl b/src/GDXFile.jl index 4dbbc27..37fe65f 100644 --- a/src/GDXFile.jl +++ b/src/GDXFile.jl @@ -71,8 +71,15 @@ struct GDXVariable{T} <: GDXSymbol{T} records::T end -GDXVariable(name::String, desc::String, domain::Vector{String}, vartype::Integer, records) = - GDXVariable(name, desc, domain, VariableType(vartype), records) +function GDXVariable( + name::String, + desc::String, + domain::Vector{String}, + vartype::Integer, + records, +) + return GDXVariable(name, desc, domain, VariableType(vartype), records) +end """ GDXEquation{T} @@ -87,8 +94,15 @@ struct GDXEquation{T} <: GDXSymbol{T} records::T end -GDXEquation(name::String, desc::String, domain::Vector{String}, equtype::Integer, records) = - GDXEquation(name, desc, domain, EquationType(equtype), records) +function GDXEquation( + name::String, + desc::String, + domain::Vector{String}, + equtype::Integer, + records, +) + return GDXEquation(name, desc, domain, EquationType(equtype), records) +end """ GDXAlias @@ -101,13 +115,15 @@ struct GDXAlias <: GDXSymbol{Nothing} alias_for::String end -Base.show(io::IO, a::GDXAlias) = print(io, "GDXAlias: $(a.name) -> $(a.alias_for)") +function Base.show(io::IO, a::GDXAlias) + return print(io, "GDXAlias: $(a.name) -> $(a.alias_for)") +end # ============================================================================= # Tables.jl interface for GDXSymbol types # ============================================================================= -const GDXRecordSymbol = Union{GDXSet, GDXParameter, GDXVariable, GDXEquation} +const GDXRecordSymbol = Union{GDXSet,GDXParameter,GDXVariable,GDXEquation} Tables.istable(::Type{<:GDXRecordSymbol}) = true Tables.columnaccess(::Type{<:GDXRecordSymbol}) = true @@ -142,12 +158,12 @@ list_parameters(gdx) # List all parameters """ struct GDXFile path::String - _symbols::Dict{Symbol, GDXSymbol} + _symbols::Dict{Symbol,GDXSymbol} _order::Vector{Symbol} end -function GDXFile(path::String, symbols::Dict{Symbol, <:GDXSymbol}) - gdx = GDXFile(path, Dict{Symbol, GDXSymbol}(), Symbol[]) +function GDXFile(path::String, symbols::Dict{Symbol,<:GDXSymbol}) + gdx = GDXFile(path, Dict{Symbol,GDXSymbol}(), Symbol[]) for (k, v) in symbols _insert!(gdx, k, v) end @@ -155,7 +171,7 @@ function GDXFile(path::String, symbols::Dict{Symbol, <:GDXSymbol}) end function GDXFile(path::String) - return GDXFile(path, Dict{Symbol, GDXSymbol}(), Symbol[]) + return GDXFile(path, Dict{Symbol,GDXSymbol}(), Symbol[]) end function _insert!(gdx::GDXFile, key::Symbol, sym::GDXSymbol) @@ -163,7 +179,7 @@ function _insert!(gdx::GDXFile, key::Symbol, sym::GDXSymbol) if !haskey(gdx._symbols, lk) push!(gdx._order, lk) end - gdx._symbols[lk] = sym + return gdx._symbols[lk] = sym end function Base.show(io::IO, gdx::GDXFile) @@ -174,19 +190,50 @@ function Base.show(io::IO, gdx::GDXFile) vars = list_variables(gdx) eqns = list_equations(gdx) isempty(sets) || println(io, " Sets ($(length(sets))): ", join(sets, ", ")) - isempty(aliases) || println(io, " Aliases ($(length(aliases))): ", join(aliases, ", ")) - isempty(params) || println(io, " Parameters ($(length(params))): ", join(params, ", ")) - isempty(vars) || println(io, " Variables ($(length(vars))): ", join(vars, ", ")) - isempty(eqns) || println(io, " Equations ($(length(eqns))): ", join(eqns, ", ")) + isempty(aliases) || + println(io, " Aliases ($(length(aliases))): ", join(aliases, ", ")) + isempty(params) || + println(io, " Parameters ($(length(params))): ", join(params, ", ")) + isempty(vars) || + println(io, " Variables ($(length(vars))): ", join(vars, ", ")) + return isempty(eqns) || + println(io, " Equations ($(length(eqns))): ", join(eqns, ", ")) end # Symbol listing (returns original-case names) -list_sets(gdx::GDXFile) = Symbol[Symbol(gdx._symbols[k].name) for k in gdx._order if gdx._symbols[k] isa GDXSet] -list_aliases(gdx::GDXFile) = Symbol[Symbol(gdx._symbols[k].name) for k in gdx._order if gdx._symbols[k] isa GDXAlias] -list_parameters(gdx::GDXFile) = Symbol[Symbol(gdx._symbols[k].name) for k in gdx._order if gdx._symbols[k] isa GDXParameter] -list_variables(gdx::GDXFile) = Symbol[Symbol(gdx._symbols[k].name) for k in gdx._order if gdx._symbols[k] isa GDXVariable] -list_equations(gdx::GDXFile) = Symbol[Symbol(gdx._symbols[k].name) for k in gdx._order if gdx._symbols[k] isa GDXEquation] -list_symbols(gdx::GDXFile) = Symbol[Symbol(gdx._symbols[k].name) for k in gdx._order] +function list_sets(gdx::GDXFile) + return Symbol[ + Symbol(gdx._symbols[k].name) for + k in gdx._order if gdx._symbols[k] isa GDXSet + ] +end +function list_aliases(gdx::GDXFile) + return Symbol[ + Symbol(gdx._symbols[k].name) for + k in gdx._order if gdx._symbols[k] isa GDXAlias + ] +end +function list_parameters(gdx::GDXFile) + return Symbol[ + Symbol(gdx._symbols[k].name) for + k in gdx._order if gdx._symbols[k] isa GDXParameter + ] +end +function list_variables(gdx::GDXFile) + return Symbol[ + Symbol(gdx._symbols[k].name) for + k in gdx._order if gdx._symbols[k] isa GDXVariable + ] +end +function list_equations(gdx::GDXFile) + return Symbol[ + Symbol(gdx._symbols[k].name) for + k in gdx._order if gdx._symbols[k] isa GDXEquation + ] +end +function list_symbols(gdx::GDXFile) + return Symbol[Symbol(gdx._symbols[k].name) for k in gdx._order] +end """ get_symbol(gdx::GDXFile, sym) -> GDXSymbol @@ -198,7 +245,11 @@ get_symbol(gdx::GDXFile, sym::Symbol) = gdx._symbols[_symkey(sym)] get_symbol(gdx::GDXFile, sym::String) = gdx._symbols[_symkey(sym)] # Resolve alias chains to get the underlying records -function _get_records(gdx::GDXFile, sym::GDXSymbol, seen::Set{Symbol}=Set{Symbol}()) +function _get_records( + gdx::GDXFile, + sym::GDXSymbol, + seen::Set{Symbol} = Set{Symbol}(), +) sym isa GDXAlias || return sym.records key = _symkey(sym.alias_for) key in seen && error("Cyclic alias chain detected involving '$(sym.name)'") @@ -207,15 +258,23 @@ function _get_records(gdx::GDXFile, sym::GDXSymbol, seen::Set{Symbol}=Set{Symbol end # Dictionary-like access (returns records, resolving aliases) -Base.getindex(gdx::GDXFile, sym::Symbol) = _get_records(gdx, gdx._symbols[_symkey(sym)]) +function Base.getindex(gdx::GDXFile, sym::Symbol) + return _get_records(gdx, gdx._symbols[_symkey(sym)]) +end Base.getindex(gdx::GDXFile, sym::String) = gdx[Symbol(sym)] Base.haskey(gdx::GDXFile, sym::Symbol) = haskey(gdx._symbols, _symkey(sym)) -Base.keys(gdx::GDXFile) = Symbol[Symbol(gdx._symbols[k].name) for k in gdx._order] +function Base.keys(gdx::GDXFile) + return Symbol[Symbol(gdx._symbols[k].name) for k in gdx._order] +end Base.length(gdx::GDXFile) = length(gdx._order) # Dictionary-like setting (inserts or updates symbols) -Base.setindex!(gdx::GDXFile, sym::GDXSymbol, key::Symbol) = _insert!(gdx, key, sym) -Base.setindex!(gdx::GDXFile, sym::GDXSymbol, key::String) = _insert!(gdx, Symbol(key), sym) +function Base.setindex!(gdx::GDXFile, sym::GDXSymbol, key::Symbol) + return _insert!(gdx, key, sym) +end +function Base.setindex!(gdx::GDXFile, sym::GDXSymbol, key::String) + return _insert!(gdx, Symbol(key), sym) +end function Base.iterate(gdx::GDXFile) isempty(gdx._order) && return nothing @@ -232,8 +291,11 @@ function Base.iterate(gdx::GDXFile, state::Int) end # Property access for tab completion -function Base.propertynames(gdx::GDXFile, private::Bool=false) - (fieldnames(GDXFile)..., (Symbol(gdx._symbols[k].name) for k in gdx._order)...) +function Base.propertynames(gdx::GDXFile, private::Bool = false) + return ( + fieldnames(GDXFile)..., + (Symbol(gdx._symbols[k].name) for k in gdx._order)..., + ) end function Base.getproperty(gdx::GDXFile, sym::Symbol) @@ -277,14 +339,19 @@ demand = gdx[:demand] # Get parameter as DataFrame gdx = read_gdx("big_model.gdx", only=[:x, :demand]) ``` """ -function read_gdx(filepath::String, sink=Tables.columntable; parse_integers::Bool=true, only=nothing) +function read_gdx( + filepath::String, + sink = Tables.columntable; + parse_integers::Bool = true, + only = nothing, +) gdx = GDXHandle() gdx_create(gdx) only_filter = only === nothing ? nothing : Set{Symbol}(_symkey.(only)) try gdx_open_read(gdx, filepath) - gdxfile = GDXFile(filepath, Dict{Symbol, GDXSymbol}(), Symbol[]) + gdxfile = GDXFile(filepath, Dict{Symbol,GDXSymbol}(), Symbol[]) n_syms, n_uels = gdx_system_info(gdx) @@ -296,19 +363,75 @@ function read_gdx(filepath::String, sink=Tables.columntable; parse_integers::Boo continue end - sym_count, sym_user_info, sym_description = gdx_symbol_info_x(gdx, sym_nr) + sym_count, sym_user_info, sym_description = + gdx_symbol_info_x(gdx, sym_nr) if sym_type == GMS_DT_SET - _insert!(gdxfile, sym_key, _read_set(gdx, sym_nr, sym_name, sym_dim, sym_description, sink)) + _insert!( + gdxfile, + sym_key, + _read_set( + gdx, + sym_nr, + sym_name, + sym_dim, + sym_description, + sink, + ), + ) elseif sym_type == GMS_DT_PAR - _insert!(gdxfile, sym_key, _read_parameter(gdx, sym_nr, sym_name, sym_dim, sym_description, parse_integers, sink)) + _insert!( + gdxfile, + sym_key, + _read_parameter( + gdx, + sym_nr, + sym_name, + sym_dim, + sym_description, + parse_integers, + sink, + ), + ) elseif sym_type == GMS_DT_VAR - _insert!(gdxfile, sym_key, _read_variable(gdx, sym_nr, sym_name, sym_dim, sym_description, sym_user_info, parse_integers, sink)) + _insert!( + gdxfile, + sym_key, + _read_variable( + gdx, + sym_nr, + sym_name, + sym_dim, + sym_description, + sym_user_info, + parse_integers, + sink, + ), + ) elseif sym_type == GMS_DT_EQU - _insert!(gdxfile, sym_key, _read_equation(gdx, sym_nr, sym_name, sym_dim, sym_description, sym_user_info, parse_integers, sink)) + _insert!( + gdxfile, + sym_key, + _read_equation( + gdx, + sym_nr, + sym_name, + sym_dim, + sym_description, + sym_user_info, + parse_integers, + sink, + ), + ) elseif sym_type == GMS_DT_ALIAS - aliased_name = sym_user_info > 0 ? gdx_symbol_info(gdx, sym_user_info)[1] : "*" - _insert!(gdxfile, sym_key, GDXAlias(sym_name, sym_description, aliased_name)) + aliased_name = + sym_user_info > 0 ? gdx_symbol_info(gdx, sym_user_info)[1] : + "*" + _insert!( + gdxfile, + sym_key, + GDXAlias(sym_name, sym_description, aliased_name), + ) end end @@ -319,7 +442,14 @@ function read_gdx(filepath::String, sink=Tables.columntable; parse_integers::Boo end end -function _read_set(gdx::GDXHandle, sym_nr::Int, name::String, dim::Int, description::String, sink) +function _read_set( + gdx::GDXHandle, + sym_nr::Int, + name::String, + dim::Int, + description::String, + sink, +) domains = dim > 0 ? gdx_symbol_get_domain_x(gdx, sym_nr, dim) : String[] n_recs = gdx_data_read_str_start(gdx, sym_nr) @@ -338,7 +468,10 @@ function _read_set(gdx::GDXHandle, sym_nr::Int, name::String, dim::Int, descript end gdx_data_read_done(gdx) - col_names = Symbol[Symbol(domain == "*" ? "dim$d" : domain) for (d, domain) in enumerate(domains)] + col_names = Symbol[ + Symbol(domain == "*" ? "dim$d" : domain) for + (d, domain) in enumerate(domains) + ] has_text = any(>(0), text_nrs) if has_text @@ -351,7 +484,10 @@ function _read_set(gdx::GDXHandle, sym_nr::Int, name::String, dim::Int, descript element_text[i] = "" end end - nt = NamedTuple{(col_names..., :element_text)}((columns..., element_text)) + nt = NamedTuple{(col_names..., :element_text)}(( + columns..., + element_text, + )) else nt = NamedTuple{Tuple(col_names)}(Tuple(columns)) end @@ -359,7 +495,15 @@ function _read_set(gdx::GDXHandle, sym_nr::Int, name::String, dim::Int, descript return GDXSet(name, description, domains, sink(nt)) end -function _read_parameter(gdx::GDXHandle, sym_nr::Int, name::String, dim::Int, description::String, parse_integers::Bool, sink) +function _read_parameter( + gdx::GDXHandle, + sym_nr::Int, + name::String, + dim::Int, + description::String, + parse_integers::Bool, + sink, +) domains = dim > 0 ? gdx_symbol_get_domain_x(gdx, sym_nr, dim) : String[] n_recs = gdx_data_read_str_start(gdx, sym_nr) @@ -378,14 +522,26 @@ function _read_parameter(gdx::GDXHandle, sym_nr::Int, name::String, dim::Int, de end gdx_data_read_done(gdx) - col_names = Symbol[Symbol(domain == "*" ? "dim$d" : domain) for (d, domain) in enumerate(domains)] + col_names = Symbol[ + Symbol(domain == "*" ? "dim$d" : domain) for + (d, domain) in enumerate(domains) + ] col_data = Any[parse_integers ? _try_parse_integers(c) : c for c in columns] nt = NamedTuple{(col_names..., :value)}((col_data..., values)) return GDXParameter(name, description, domains, sink(nt)) end -function _read_variable(gdx::GDXHandle, sym_nr::Int, name::String, dim::Int, description::String, user_info::Int, parse_integers::Bool, sink) +function _read_variable( + gdx::GDXHandle, + sym_nr::Int, + name::String, + dim::Int, + description::String, + user_info::Int, + parse_integers::Bool, + sink, +) domains = dim > 0 ? gdx_symbol_get_domain_x(gdx, sym_nr, dim) : String[] n_recs = gdx_data_read_str_start(gdx, sym_nr) @@ -412,14 +568,39 @@ function _read_variable(gdx::GDXHandle, sym_nr::Int, name::String, dim::Int, des end gdx_data_read_done(gdx) - col_names = Symbol[Symbol(domain == "*" ? "dim$d" : domain) for (d, domain) in enumerate(domains)] + col_names = Symbol[ + Symbol(domain == "*" ? "dim$d" : domain) for + (d, domain) in enumerate(domains) + ] col_data = Any[parse_integers ? _try_parse_integers(c) : c for c in columns] - nt = NamedTuple{(col_names..., :level, :marginal, :lower, :upper, :scale)}((col_data..., level, marginal, lower, upper, scale)) - return GDXVariable(name, description, domains, VariableType(user_info), sink(nt)) -end - -function _read_equation(gdx::GDXHandle, sym_nr::Int, name::String, dim::Int, description::String, user_info::Int, parse_integers::Bool, sink) + nt = NamedTuple{(col_names..., :level, :marginal, :lower, :upper, :scale)}(( + col_data..., + level, + marginal, + lower, + upper, + scale, + )) + return GDXVariable( + name, + description, + domains, + VariableType(user_info), + sink(nt), + ) +end + +function _read_equation( + gdx::GDXHandle, + sym_nr::Int, + name::String, + dim::Int, + description::String, + user_info::Int, + parse_integers::Bool, + sink, +) domains = dim > 0 ? gdx_symbol_get_domain_x(gdx, sym_nr, dim) : String[] n_recs = gdx_data_read_str_start(gdx, sym_nr) @@ -446,11 +627,27 @@ function _read_equation(gdx::GDXHandle, sym_nr::Int, name::String, dim::Int, des end gdx_data_read_done(gdx) - col_names = Symbol[Symbol(domain == "*" ? "dim$d" : domain) for (d, domain) in enumerate(domains)] + col_names = Symbol[ + Symbol(domain == "*" ? "dim$d" : domain) for + (d, domain) in enumerate(domains) + ] col_data = Any[parse_integers ? _try_parse_integers(c) : c for c in columns] - nt = NamedTuple{(col_names..., :level, :marginal, :lower, :upper, :scale)}((col_data..., level, marginal, lower, upper, scale)) - return GDXEquation(name, description, domains, EquationType(user_info), sink(nt)) + nt = NamedTuple{(col_names..., :level, :marginal, :lower, :upper, :scale)}(( + col_data..., + level, + marginal, + lower, + upper, + scale, + )) + return GDXEquation( + name, + description, + domains, + EquationType(user_info), + sink(nt), + ) end # ============================================================================= @@ -469,7 +666,11 @@ gdx = read_gdx("input.gdx") write_gdx("output.gdx", gdx) ``` """ -function write_gdx(filepath::String, gdxfile::GDXFile; producer::String="GDXInterface.jl") +function write_gdx( + filepath::String, + gdxfile::GDXFile; + producer::String = "GDXInterface.jl", +) gdx = GDXHandle() gdx_create(gdx) @@ -513,7 +714,11 @@ nt = (; i=["a", "b", "c"], value=[1.0, 2.0, 3.0]) write_gdx("output.gdx", "demand" => nt) ``` """ -function write_gdx(filepath::String, symbols::Pair{String}...; producer::String="GDXInterface.jl") +function write_gdx( + filepath::String, + symbols::Pair{String}...; + producer::String = "GDXInterface.jl", +) gdx = GDXHandle() gdx_create(gdx) @@ -531,10 +736,15 @@ function write_gdx(filepath::String, symbols::Pair{String}...; producer::String= return filepath end -function _set_domain_x(gdx::GDXHandle, name::String, domain::Vector{String}, dim::Int) +function _set_domain_x( + gdx::GDXHandle, + name::String, + domain::Vector{String}, + dim::Int, +) length(domain) == dim && dim > 0 || return found, sym_nr = gdx_find_symbol(gdx, name) - found && gdx_symbol_set_domain_x(gdx, sym_nr, domain) + return found && gdx_symbol_set_domain_x(gdx, sym_nr, domain) end function _table_description(tbl) @@ -552,7 +762,8 @@ function _write_set(gdx::GDXHandle, sym::GDXSet) tbl = Tables.columns(sym.records) col_names = collect(Tables.columnnames(tbl)) has_text = :element_text in col_names - dim_cols = has_text ? filter(!=(Symbol("element_text")), col_names) : col_names + dim_cols = + has_text ? filter(!=(Symbol("element_text")), col_names) : col_names dim = length(dim_cols) gdx_data_write_str_start(gdx, sym.name, sym.description, dim, GMS_DT_SET) @@ -567,20 +778,33 @@ function _write_set(gdx::GDXHandle, sym::GDXSet) end if has_text text = string(Tables.getcolumn(tbl, :element_text)[i]) - vals[GAMS_VALUE_LEVEL] = isempty(text) ? 0.0 : Float64(gdx_add_set_text(gdx, text)) + vals[GAMS_VALUE_LEVEL] = + isempty(text) ? 0.0 : Float64(gdx_add_set_text(gdx, text)) end gdx_data_write_str(gdx, keys, vals) end gdx_data_write_done(gdx) - _set_domain_x(gdx, sym.name, sym.domain, dim) + return _set_domain_x(gdx, sym.name, sym.domain, dim) end function _write_parameter(gdx::GDXHandle, sym::GDXParameter) - _write_parameter_table(gdx, sym.name, sym.records, sym.description, sym.domain) -end - -function _write_parameter_table(gdx::GDXHandle, name::String, tbl, description::String="", domain::Vector{String}=String[]) + return _write_parameter_table( + gdx, + sym.name, + sym.records, + sym.description, + sym.domain, + ) +end + +function _write_parameter_table( + gdx::GDXHandle, + name::String, + tbl, + description::String = "", + domain::Vector{String} = String[], +) cols = Tables.columns(tbl) col_names = collect(Tables.columnnames(cols)) dim_cols = filter(!=(:value), col_names) @@ -601,7 +825,7 @@ function _write_parameter_table(gdx::GDXHandle, name::String, tbl, description:: end gdx_data_write_done(gdx) - _set_domain_x(gdx, name, domain, dim) + return _set_domain_x(gdx, name, domain, dim) end const _VAR_EQU_COLS = Set([:level, :marginal, :lower, :upper, :scale]) @@ -612,7 +836,14 @@ function _write_variable(gdx::GDXHandle, sym::GDXVariable) dim_cols = filter(c -> !(c in _VAR_EQU_COLS), col_names) dim = length(dim_cols) - gdx_data_write_str_start(gdx, sym.name, sym.description, dim, GMS_DT_VAR, Int(sym.vartype)) + gdx_data_write_str_start( + gdx, + sym.name, + sym.description, + dim, + GMS_DT_VAR, + Int(sym.vartype), + ) keys = Vector{String}(undef, dim) vals = zeros(Float64, GMS_VAL_MAX) @@ -636,7 +867,7 @@ function _write_variable(gdx::GDXHandle, sym::GDXVariable) end gdx_data_write_done(gdx) - _set_domain_x(gdx, sym.name, sym.domain, dim) + return _set_domain_x(gdx, sym.name, sym.domain, dim) end function _write_equation(gdx::GDXHandle, sym::GDXEquation) @@ -645,7 +876,14 @@ function _write_equation(gdx::GDXHandle, sym::GDXEquation) dim_cols = filter(c -> !(c in _VAR_EQU_COLS), col_names) dim = length(dim_cols) - gdx_data_write_str_start(gdx, sym.name, sym.description, dim, GMS_DT_EQU, Int(sym.equtype)) + gdx_data_write_str_start( + gdx, + sym.name, + sym.description, + dim, + GMS_DT_EQU, + Int(sym.equtype), + ) keys = Vector{String}(undef, dim) vals = zeros(Float64, GMS_VAL_MAX) @@ -669,7 +907,7 @@ function _write_equation(gdx::GDXHandle, sym::GDXEquation) end gdx_data_write_done(gdx) - _set_domain_x(gdx, sym.name, sym.domain, dim) + return _set_domain_x(gdx, sym.name, sym.domain, dim) end # ============================================================================= diff --git a/src/GDXInterface.jl b/src/GDXInterface.jl index 4ed8e39..d083cbf 100644 --- a/src/GDXInterface.jl +++ b/src/GDXInterface.jl @@ -9,12 +9,24 @@ include("gdx_c_api.jl") include("GDXFile.jl") # GDX file access exports -export GDXFile, GDXSymbol, GDXSet, GDXParameter, GDXVariable, GDXEquation, GDXAlias +export GDXFile, + GDXSymbol, GDXSet, GDXParameter, GDXVariable, GDXEquation, GDXAlias export GDXException -export VariableType, VarUnknown, VarBinary, VarInteger, VarPositive, VarNegative, VarFree, VarSOS1, VarSOS2, VarSemiCont, VarSemiInt +export VariableType, + VarUnknown, + VarBinary, + VarInteger, + VarPositive, + VarNegative, + VarFree, + VarSOS1, + VarSOS2, + VarSemiCont, + VarSemiInt export EquationType, EqE, EqG, EqL, EqN, EqX, EqC, EqB export read_gdx, write_gdx -export list_sets, list_aliases, list_parameters, list_variables, list_equations, list_symbols +export list_sets, + list_aliases, list_parameters, list_variables, list_equations, list_symbols export get_symbol end # module GDXInterface diff --git a/src/gdx_c_api.jl b/src/gdx_c_api.jl index 8e12311..21250ab 100644 --- a/src/gdx_c_api.jl +++ b/src/gdx_c_api.jl @@ -4,7 +4,7 @@ # libgdx C library prefix for ccall: const GDX_C_PREFIX = "c__" macro cpfx(x) - s = strip(string(x),':') + s = strip(string(x), ':') return Expr(:quote, Symbol(GDX_C_PREFIX, s)) end @@ -40,7 +40,6 @@ const GAMS_SV_EPS = 5.0e300 # epsilon const GAMS_SV_ACR = 10.0e300 # potential / real acronym const GAMS_SV_NAINT = 2100000000 # not available / applicable for integers - function parse_gdx_value(val::Float64) if val == GAMS_SV_UNDEF || val == GAMS_SV_NA return NaN @@ -81,7 +80,16 @@ mutable struct GDXHandle cbuf = pointer.(buf) crvec = Vector{Cdouble}(undef, GMS_VAL_MAX) civec = Vector{Cint}(undef, GMS_MAX_INDEX_DIM) - return new(cptr, Ref{Cint}(-1), Ref{Cint}(-1), Ref{Cint}(-1), civec, crvec, buf, cbuf) + return new( + cptr, + Ref{Cint}(-1), + Ref{Cint}(-1), + Ref{Cint}(-1), + civec, + crvec, + buf, + cbuf, + ) end end @@ -90,7 +98,9 @@ struct GDXException <: Exception n_err::Int end -Base.showerror(io::IO, e::GDXException) = print(io, "GDX failed: $(e.msg) ($(e.n_err))") +function Base.showerror(io::IO, e::GDXException) + return print(io, "GDX failed: $(e.msg) ($(e.n_err))") +end # ============================================================================= # Core handle management @@ -136,7 +146,12 @@ function gdx_open_read(gdx::GDXHandle, file::String) return rc end -function gdx_open_write(gdx_ptr::Ptr{Cvoid}, file::String, producer::String, n_err::Ref{Cint}) +function gdx_open_write( + gdx_ptr::Ptr{Cvoid}, + file::String, + producer::String, + n_err::Ref{Cint}, +) return ccall( (@cpfx(:gdxopenwrite), LIBGDX), Cint, @@ -148,7 +163,11 @@ function gdx_open_write(gdx_ptr::Ptr{Cvoid}, file::String, producer::String, n_e ) end -function gdx_open_write(gdx::GDXHandle, file::String, producer::String="GAMS.jl") +function gdx_open_write( + gdx::GDXHandle, + file::String, + producer::String = "GAMS.jl", +) rc = gdx_open_write(gdx.cptr[], file, producer, gdx.cival) if gdx.cival[] != 0 || rc != 1 throw(GDXException("Can't open file '$file' for writing", gdx.cival[])) @@ -156,7 +175,9 @@ function gdx_open_write(gdx::GDXHandle, file::String, producer::String="GAMS.jl" return end -gdx_close(gdx_ptr::Ptr{Cvoid}) = ccall((@cpfx(:gdxclose), LIBGDX), Cint, (Ptr{Cvoid},), gdx_ptr) +function gdx_close(gdx_ptr::Ptr{Cvoid}) + return ccall((@cpfx(:gdxclose), LIBGDX), Cint, (Ptr{Cvoid},), gdx_ptr) +end function gdx_close(gdx::GDXHandle) gdx_close(gdx.cptr[]) @@ -167,7 +188,11 @@ end # System and symbol information # ============================================================================= -function gdx_system_info(gdx_ptr::Ptr{Cvoid}, sym_count::Ref{Cint}, uel_count::Ref{Cint}) +function gdx_system_info( + gdx_ptr::Ptr{Cvoid}, + sym_count::Ref{Cint}, + uel_count::Ref{Cint}, +) return ccall( (@cpfx(:gdxsysteminfo), LIBGDX), Cint, @@ -235,7 +260,13 @@ end function gdx_symbol_info_x(gdx::GDXHandle, sym_id::Int) gdx.buf[1][1] = UInt8('\0') - rc = gdx_symbol_info_x(gdx.cptr[], sym_id, gdx.cival, gdx.cival2, gdx.cbuf[1]) + rc = gdx_symbol_info_x( + gdx.cptr[], + sym_id, + gdx.cival, + gdx.cival2, + gdx.cbuf[1], + ) if rc != 1 throw(GDXException("Can't read extended symbol info", 0)) end @@ -258,7 +289,11 @@ function gdx_find_symbol(gdx::GDXHandle, name::String) return rc == 1, Int(gdx.cival[]) end -function gdx_symbol_get_domain_x(gdx_ptr::Ptr{Cvoid}, sym_nr::Int, domains::Vector{Ptr{UInt8}}) +function gdx_symbol_get_domain_x( + gdx_ptr::Ptr{Cvoid}, + sym_nr::Int, + domains::Vector{Ptr{UInt8}}, +) return ccall( (@cpfx(:gdxsymbolgetdomainx), LIBGDX), Cint, @@ -281,7 +316,11 @@ function gdx_symbol_get_domain_x(gdx::GDXHandle, sym_nr::Int, dim::Int) return domains end -function gdx_symbol_set_domain_x(gdx_ptr::Ptr{Cvoid}, sym_nr::Int, domain_ids::Vector{String}) +function gdx_symbol_set_domain_x( + gdx_ptr::Ptr{Cvoid}, + sym_nr::Int, + domain_ids::Vector{String}, +) return ccall( (@cpfx(:gdxsymbolsetdomainx), LIBGDX), Cint, @@ -292,7 +331,11 @@ function gdx_symbol_set_domain_x(gdx_ptr::Ptr{Cvoid}, sym_nr::Int, domain_ids::V ) end -function gdx_symbol_set_domain_x(gdx::GDXHandle, sym_nr::Int, domain_ids::Vector{String}) +function gdx_symbol_set_domain_x( + gdx::GDXHandle, + sym_nr::Int, + domain_ids::Vector{String}, +) rc = gdx_symbol_set_domain_x(gdx.cptr[], sym_nr, domain_ids) return rc end @@ -324,7 +367,12 @@ end # Set element text # ============================================================================= -function gdx_get_elem_text(gdx_ptr::Ptr{Cvoid}, text_nr::Int, text::Ptr{UInt8}, node::Ref{Cint}) +function gdx_get_elem_text( + gdx_ptr::Ptr{Cvoid}, + text_nr::Int, + text::Ptr{UInt8}, + node::Ref{Cint}, +) return ccall( (@cpfx(:gdxgetelemtext), LIBGDX), Cint, @@ -365,7 +413,12 @@ end # UEL (Unique Element List) operations # ============================================================================= -function gdx_um_uel_get(gdx_ptr::Ptr{Cvoid}, uel_nr::Int, uel::Ptr{UInt8}, uel_map::Ref{Cint}) +function gdx_um_uel_get( + gdx_ptr::Ptr{Cvoid}, + uel_nr::Int, + uel::Ptr{UInt8}, + uel_map::Ref{Cint}, +) return ccall( (@cpfx(:gdxumuelget), LIBGDX), Cint, @@ -387,7 +440,12 @@ function gdx_um_uel_get(gdx::GDXHandle, uel_nr::Int) end function gdx_uel_register_str_start(gdx_ptr::Ptr{Cvoid}) - return ccall((@cpfx(:gdxuelregisterstrstart), LIBGDX), Cint, (Ptr{Cvoid},), gdx_ptr) + return ccall( + (@cpfx(:gdxuelregisterstrstart), LIBGDX), + Cint, + (Ptr{Cvoid},), + gdx_ptr, + ) end function gdx_uel_register_str_start(gdx::GDXHandle) @@ -398,7 +456,11 @@ function gdx_uel_register_str_start(gdx::GDXHandle) return end -function gdx_uel_register_str(gdx_ptr::Ptr{Cvoid}, uel::String, uel_nr::Ref{Cint}) +function gdx_uel_register_str( + gdx_ptr::Ptr{Cvoid}, + uel::String, + uel_nr::Ref{Cint}, +) return ccall( (@cpfx(:gdxuelregisterstr), LIBGDX), Cint, @@ -415,7 +477,12 @@ function gdx_uel_register_str(gdx::GDXHandle, uel::String) end function gdx_uel_register_done(gdx_ptr::Ptr{Cvoid}) - return ccall((@cpfx(:gdxuelregisterdone), LIBGDX), Cint, (Ptr{Cvoid},), gdx_ptr) + return ccall( + (@cpfx(:gdxuelregisterdone), LIBGDX), + Cint, + (Ptr{Cvoid},), + gdx_ptr, + ) end function gdx_uel_register_done(gdx::GDXHandle) @@ -430,7 +497,11 @@ end # Reading data (raw integer interface) # ============================================================================= -function gdx_data_read_raw_start(gdx_ptr::Ptr{Cvoid}, start::Int, n_rec::Ref{Cint}) +function gdx_data_read_raw_start( + gdx_ptr::Ptr{Cvoid}, + start::Int, + n_rec::Ref{Cint}, +) return ccall( (@cpfx(:gdxdatareadrawstart), LIBGDX), Cint, @@ -466,7 +537,11 @@ function gdx_data_read_raw( ) end -function gdx_data_read_raw(gdx::GDXHandle, idx::Vector{Int}, vals::Vector{Float64}) +function gdx_data_read_raw( + gdx::GDXHandle, + idx::Vector{Int}, + vals::Vector{Float64}, +) @assert(length(idx) <= length(gdx.civec)) @assert(length(vals) <= length(gdx.crvec)) @@ -488,7 +563,11 @@ end # Reading data (string interface) # ============================================================================= -function gdx_data_read_str_start(gdx_ptr::Ptr{Cvoid}, start::Int, n_err::Ref{Cint}) +function gdx_data_read_str_start( + gdx_ptr::Ptr{Cvoid}, + start::Int, + n_err::Ref{Cint}, +) return ccall( (@cpfx(:gdxdatareadstrstart), LIBGDX), Cint, @@ -524,7 +603,11 @@ function gdx_data_read_str( ) end -function gdx_data_read_str(gdx::GDXHandle, keystr::Vector{String}, vals::Vector{Float64}) +function gdx_data_read_str( + gdx::GDXHandle, + keystr::Vector{String}, + vals::Vector{Float64}, +) @assert(length(keystr) <= length(gdx.cbuf)) @assert(length(vals) <= length(gdx.crvec)) @@ -547,7 +630,12 @@ function gdx_data_read_str(gdx::GDXHandle, keystr::Vector{String}, vals::Vector{ end function gdx_data_read_done(gdx_ptr::Ptr{Cvoid}) - return ccall((@cpfx(:gdxdatareaddone), LIBGDX), Cint, (Ptr{Cvoid},), gdx_ptr) + return ccall( + (@cpfx(:gdxdatareaddone), LIBGDX), + Cint, + (Ptr{Cvoid},), + gdx_ptr, + ) end function gdx_data_read_done(gdx::GDXHandle) @@ -580,7 +668,14 @@ function gdx_data_write_str_start( ) end -function gdx_data_write_str_start(gdx::GDXHandle, name::String, text::String, dim::Int, typ::Int, user_info::Int=0) +function gdx_data_write_str_start( + gdx::GDXHandle, + name::String, + text::String, + dim::Int, + typ::Int, + user_info::Int = 0, +) rc = gdx_data_write_str_start(gdx.cptr[], name, text, dim, typ, user_info) if rc != 1 throw(GDXException("Can't start writing symbol '$name'", 0)) @@ -588,7 +683,11 @@ function gdx_data_write_str_start(gdx::GDXHandle, name::String, text::String, di return end -function gdx_data_write_str(gdx_ptr::Ptr{Cvoid}, keys::Vector{String}, vals::Vector{Float64}) +function gdx_data_write_str( + gdx_ptr::Ptr{Cvoid}, + keys::Vector{String}, + vals::Vector{Float64}, +) return ccall( (@cpfx(:gdxdatawritestr), LIBGDX), Cint, @@ -599,7 +698,11 @@ function gdx_data_write_str(gdx_ptr::Ptr{Cvoid}, keys::Vector{String}, vals::Vec ) end -function gdx_data_write_str(gdx::GDXHandle, keys::Vector{String}, vals::Vector{Float64}) +function gdx_data_write_str( + gdx::GDXHandle, + keys::Vector{String}, + vals::Vector{Float64}, +) rc = gdx_data_write_str(gdx.cptr[], keys, vals) if rc != 1 throw(GDXException("Can't write record", 0)) @@ -608,7 +711,12 @@ function gdx_data_write_str(gdx::GDXHandle, keys::Vector{String}, vals::Vector{F end function gdx_data_write_done(gdx_ptr::Ptr{Cvoid}) - return ccall((@cpfx(:gdxdatawritedone), LIBGDX), Cint, (Ptr{Cvoid},), gdx_ptr) + return ccall( + (@cpfx(:gdxdatawritedone), LIBGDX), + Cint, + (Ptr{Cvoid},), + gdx_ptr, + ) end function gdx_data_write_done(gdx::GDXHandle) diff --git a/test/test_gdxfile.jl b/test/test_gdxfile.jl index a6d100a..b5f3852 100644 --- a/test/test_gdxfile.jl +++ b/test/test_gdxfile.jl @@ -53,7 +53,10 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; @testset "Write and read round-trip" begin supply = (; i = ["seattle", "san-diego"], value = [350.0, 600.0]) - demand = (; j = ["new-york", "chicago", "topeka"], value = [325.0, 300.0, 275.0]) + demand = (; + j = ["new-york", "chicago", "topeka"], + value = [325.0, 300.0, 275.0], + ) outfile = joinpath(tempdir(), "gdx_jl_write_test.gdx") write_gdx(outfile, "supply" => supply, "demand" => demand) @@ -62,20 +65,22 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; @test :supply in list_parameters(gdxfile) @test :demand in list_parameters(gdxfile) - @test collect(Tables.getcolumn(gdxfile[:supply], :value)) == [350.0, 600.0] - @test collect(Tables.getcolumn(gdxfile[:demand], :value)) == [325.0, 300.0, 275.0] + @test collect(Tables.getcolumn(gdxfile[:supply], :value)) == + [350.0, 600.0] + @test collect(Tables.getcolumn(gdxfile[:demand], :value)) == + [325.0, 300.0, 275.0] @test gdxfile.supply == gdxfile[:supply] @test gdxfile.demand == gdxfile[:demand] - rm(outfile, force=true) + rm(outfile, force = true) end @testset "Multi-dimensional parameters" begin cost = (; i = ["seattle", "seattle", "san-diego", "san-diego"], j = ["new-york", "chicago", "new-york", "chicago"], - value = [2.5, 1.7, 2.5, 1.8] + value = [2.5, 1.7, 2.5, 1.8], ) outfile = joinpath(tempdir(), "gdx_jl_2d_test.gdx") @@ -89,7 +94,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; @test length(col_names) == 3 @test :value in col_names - rm(outfile, force=true) + rm(outfile, force = true) end @testset "Integer parsing" begin @@ -98,13 +103,13 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; outfile = joinpath(tempdir(), "gdx_jl_int_test.gdx") write_gdx(outfile, "data" => df) - gdxfile = read_gdx(outfile, parse_integers=true) + gdxfile = read_gdx(outfile, parse_integers = true) @test eltype(Tables.getcolumn(gdxfile[:data], :dim1)) == Int - gdxfile = read_gdx(outfile, parse_integers=false) + gdxfile = read_gdx(outfile, parse_integers = false) @test eltype(Tables.getcolumn(gdxfile[:data], :dim1)) == String - rm(outfile, force=true) + rm(outfile, force = true) end @testset "GDXFile show and propertynames" begin @@ -123,7 +128,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; props = propertynames(gdxfile) @test :param in props - rm(outfile, force=true) + rm(outfile, force = true) end @testset "Symbol listing" begin @@ -143,7 +148,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; syms = list_symbols(gdxfile) @test length(syms) == 2 - rm(outfile, force=true) + rm(outfile, force = true) end @testset "GDXFile full round-trip (sets, params, variables)" begin @@ -155,22 +160,34 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; @test sort(list_symbols(gdx1)) == sort(list_symbols(gdx2)) - @test collect(Tables.getcolumn(gdx1[:p], :value)) == collect(Tables.getcolumn(gdx2[:p], :value)) + @test collect(Tables.getcolumn(gdx1[:p], :value)) == + collect(Tables.getcolumn(gdx2[:p], :value)) - @test collect(Tables.getcolumn(gdx1[:x], :level)) == collect(Tables.getcolumn(gdx2[:x], :level)) - @test collect(Tables.getcolumn(gdx1[:x], :marginal)) ≈ collect(Tables.getcolumn(gdx2[:x], :marginal)) - @test collect(Tables.getcolumn(gdx1[:y], :level)) == collect(Tables.getcolumn(gdx2[:y], :level)) - @test collect(Tables.getcolumn(gdx1[:y], :upper)) == collect(Tables.getcolumn(gdx2[:y], :upper)) + @test collect(Tables.getcolumn(gdx1[:x], :level)) == + collect(Tables.getcolumn(gdx2[:x], :level)) + @test collect(Tables.getcolumn(gdx1[:x], :marginal)) ≈ + collect(Tables.getcolumn(gdx2[:x], :marginal)) + @test collect(Tables.getcolumn(gdx1[:y], :level)) == + collect(Tables.getcolumn(gdx2[:y], :level)) + @test collect(Tables.getcolumn(gdx1[:y], :upper)) == + collect(Tables.getcolumn(gdx2[:y], :upper)) - i1_col = collect(Tables.getcolumn(gdx1[:i], first(Tables.columnnames(gdx1[:i])))) - i2_col = collect(Tables.getcolumn(gdx2[:i], first(Tables.columnnames(gdx2[:i])))) + i1_col = collect( + Tables.getcolumn(gdx1[:i], first(Tables.columnnames(gdx1[:i]))), + ) + i2_col = collect( + Tables.getcolumn(gdx2[:i], first(Tables.columnnames(gdx2[:i]))), + ) @test sort(i1_col) == sort(i2_col) - rm(outfile, force=true) + rm(outfile, force = true) end @testset "Special values round-trip" begin - tbl = (; i = ["a", "b", "c", "d", "e"], value = [NaN, Inf, -Inf, 42.0, -0.0]) + tbl = (; + i = ["a", "b", "c", "d", "e"], + value = [NaN, Inf, -Inf, 42.0, -0.0], + ) outfile = joinpath(tempdir(), "gdx_jl_special.gdx") write_gdx(outfile, "special" => tbl) @@ -182,7 +199,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; @test result[4] == 42.0 @test result[5] === -0.0 - rm(outfile, force=true) + rm(outfile, force = true) end @testset "Scalar (0-dim) parameters" begin @@ -192,24 +209,26 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; gdxfile = read_gdx(outfile) @test :scalar_param in list_parameters(gdxfile) - @test collect(Tables.getcolumn(gdxfile[:scalar_param], :value)) == [42.0] + @test collect(Tables.getcolumn(gdxfile[:scalar_param], :value)) == + [42.0] @test length(collect(Tables.columnnames(gdxfile[:scalar_param]))) == 1 - rm(outfile, force=true) + rm(outfile, force = true) end @testset "Selective reading (only keyword)" begin gdx_full = read_gdx(test_gdx) - gdx_partial = read_gdx(test_gdx, only=[:p, :x]) + gdx_partial = read_gdx(test_gdx, only = [:p, :x]) @test length(gdx_partial) == 2 @test :p in list_parameters(gdx_partial) @test :x in list_variables(gdx_partial) @test !haskey(gdx_partial, :i) @test !haskey(gdx_partial, :y) - @test collect(Tables.getcolumn(gdx_partial[:p], :value)) == collect(Tables.getcolumn(gdx_full[:p], :value)) + @test collect(Tables.getcolumn(gdx_partial[:p], :value)) == + collect(Tables.getcolumn(gdx_full[:p], :value)) - gdx_str = read_gdx(test_gdx, only=["i"]) + gdx_str = read_gdx(test_gdx, only = ["i"]) @test length(gdx_str) == 1 @test :i in list_sets(gdx_str) end @@ -253,7 +272,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; marginal = [0.5, 0.6], lower = [-Inf, -Inf], upper = [Inf, Inf], - scale = [1.0, 1.0] + scale = [1.0, 1.0], ) eq = GDXEquation("myeq", "test equation", ["i"], 0, eq_tbl) gdxfile = GDXFile("", Dict{Symbol,GDXSymbol}(:myeq => eq)) @@ -266,7 +285,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; @test collect(Tables.getcolumn(gdx2[:myeq], :level)) == [1.0, 2.0] @test collect(Tables.getcolumn(gdx2[:myeq], :marginal)) == [0.5, 0.6] - rm(outfile, force=true) + rm(outfile, force = true) end @testset "Writing sets standalone" begin @@ -279,10 +298,15 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; gdx2 = read_gdx(outfile) @test :myset in list_sets(gdx2) - col = collect(Tables.getcolumn(gdx2[:myset], first(Tables.columnnames(gdx2[:myset])))) + col = collect( + Tables.getcolumn( + gdx2[:myset], + first(Tables.columnnames(gdx2[:myset])), + ), + ) @test sort(col) == ["x", "y", "z"] - rm(outfile, force=true) + rm(outfile, force = true) end @testset "Variable/Equation type enums" begin @@ -295,10 +319,34 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; sym_y = get_symbol(gdxfile, :y) @test sym_y.vartype == VarPositive - v = GDXVariable("test", "", String[], 3, (; level=[0.0], marginal=[0.0], lower=[0.0], upper=[0.0], scale=[1.0])) + v = GDXVariable( + "test", + "", + String[], + 3, + (; + level = [0.0], + marginal = [0.0], + lower = [0.0], + upper = [0.0], + scale = [1.0], + ), + ) @test v.vartype == VarPositive - e = GDXEquation("test", "", String[], 0, (; level=[0.0], marginal=[0.0], lower=[0.0], upper=[0.0], scale=[1.0])) + e = GDXEquation( + "test", + "", + String[], + 0, + (; + level = [0.0], + marginal = [0.0], + lower = [0.0], + upper = [0.0], + scale = [1.0], + ), + ) @test e.equtype == EqE end @@ -316,7 +364,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; sym = get_symbol(gdxfile, :p) @test sym.name == "p" - gdx2 = read_gdx(test_gdx, only=[:P, :X]) + gdx2 = read_gdx(test_gdx, only = [:P, :X]) @test length(gdx2) == 2 @test :p in list_parameters(gdx2) @test :x in list_variables(gdx2) @@ -334,13 +382,13 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; gdx2 = read_gdx(outfile) @test list_symbols(gdx2) == syms - rm(outfile, force=true) + rm(outfile, force = true) end @testset "Set element text round-trip" begin set_tbl = (; dim1 = ["seattle", "san-diego", "topeka"], - element_text = ["rainy city", "sunny city", ""] + element_text = ["rainy city", "sunny city", ""], ) s = GDXSet("cities", "transport cities", ["*"], set_tbl) gdxfile = GDXFile("", Dict{Symbol,GDXSymbol}(:cities => s)) @@ -356,7 +404,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; @test et[2] == "sunny city" @test et[3] == "" - rm(outfile, force=true) + rm(outfile, force = true) end @testset "Set without element text has no extra column" begin @@ -370,7 +418,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; gdx2 = read_gdx(outfile) @test !(:element_text in Tables.columnnames(gdx2[:simple])) - rm(outfile, force=true) + rm(outfile, force = true) end @testset "Alias round-trip" begin @@ -392,7 +440,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; @test gdx2[:j] == gdx2[:i] - rm(outfile, force=true) + rm(outfile, force = true) end @testset "GDXAlias show" begin @@ -417,7 +465,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; @test x2.domain == ["i"] @test first(Tables.columnnames(gdx2[:x])) == :i - rm(outfile, force=true) + rm(outfile, force = true) end @testset "Domain preservation for variables (issue #3)" begin @@ -429,7 +477,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; marginal = [0.0, 0.0, 0.0], lower = [-Inf, -Inf, -Inf], upper = [Inf, Inf, Inf], - scale = [1.0, 1.0, 1.0] + scale = [1.0, 1.0, 1.0], ) v = GDXVariable("y", "A variable over i", ["i"], VarFree, var_tbl) gdxfile = GDXFile("", Dict{Symbol,GDXSymbol}(:i => s, :y => v)) @@ -442,7 +490,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; @test y2.domain == ["i"] @test first(Tables.columnnames(gdx2[:y])) == :i - rm(outfile, force=true) + rm(outfile, force = true) end @testset "Domain preservation for equations (issue #3)" begin @@ -454,7 +502,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; marginal = [0.5, 0.6], lower = [-Inf, -Inf], upper = [Inf, Inf], - scale = [1.0, 1.0] + scale = [1.0, 1.0], ) eq = GDXEquation("myeq", "test eq", ["i"], EqE, eq_tbl) gdxfile = GDXFile("", Dict{Symbol,GDXSymbol}(:i => s, :myeq => eq)) @@ -467,7 +515,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; @test eq2.domain == ["i"] @test first(Tables.columnnames(gdx2[:myeq])) == :i - rm(outfile, force=true) + rm(outfile, force = true) end @testset "Multi-dimensional domain preservation (issue #3)" begin @@ -476,10 +524,11 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; par_tbl = (; i = ["a", "a", "b", "b"], j = ["x", "y", "x", "y"], - value = [1.0, 2.0, 3.0, 4.0] + value = [1.0, 2.0, 3.0, 4.0], ) p = GDXParameter("cost", "transport cost", ["i", "j"], par_tbl) - gdxfile = GDXFile("", Dict{Symbol,GDXSymbol}(:i => si, :j => sj, :cost => p)) + gdxfile = + GDXFile("", Dict{Symbol,GDXSymbol}(:i => si, :j => sj, :cost => p)) outfile = joinpath(tempdir(), "gdx_jl_domain_2d_test.gdx") write_gdx(outfile, gdxfile) @@ -490,7 +539,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; cnames = collect(Tables.columnnames(gdx2[:cost])) @test cnames[1:2] == [:i, :j] - rm(outfile, force=true) + rm(outfile, force = true) end @testset "Domain preservation with GAMS-generated file (issue #3)" begin @@ -510,7 +559,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; x2 = get_symbol(gdx2, :x) @test x2.domain == x1.domain - rm(outfile, force=true) + rm(outfile, force = true) end @testset "Setting symbols via indexing" begin @@ -546,7 +595,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; @testset "DataFrame metadata description" begin df = DataFrame(i = ["a", "b"], value = [1.0, 2.0]) - metadata!(df, "description", "from metadata", style=:default) + metadata!(df, "description", "from metadata", style = :default) outfile = joinpath(tempdir(), "gdx_jl_metadata_desc.gdx") write_gdx(outfile, "meta_param" => df) @@ -554,7 +603,7 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y; gdxfile = read_gdx(outfile) @test get_symbol(gdxfile, :meta_param).description == "from metadata" - rm(outfile, force=true) + rm(outfile, force = true) end @testset "Tables.jl interface on GDXSymbol" begin