Skip to content

Commit 20d5e69

Browse files
authored
Merge pull request #4: Preserve domain information when writing GDX files
Fix: Preserve domain information when writing GDX files
2 parents 0fd6ae4 + a7cb3b8 commit 20d5e69

3 files changed

Lines changed: 138 additions & 2 deletions

File tree

src/GDXFile.jl

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,12 @@ function write_gdx(filepath::String, symbols::Pair{String, DataFrames.DataFrame}
539539
return filepath
540540
end
541541

542+
function _set_domain_x(gdx::GDXHandle, name::String, domain::Vector{String}, dim::Int)
543+
length(domain) == dim && dim > 0 || return
544+
found, sym_nr = gdx_find_symbol(gdx, name)
545+
found && gdx_symbol_set_domain_x(gdx, sym_nr, domain)
546+
end
547+
542548
# Type dispatch for writing symbols
543549
_write_symbol(gdx::GDXHandle, sym::GDXSet) = _write_set(gdx, sym)
544550
_write_symbol(gdx::GDXHandle, sym::GDXParameter) = _write_parameter(gdx, sym)
@@ -572,13 +578,14 @@ function _write_set(gdx::GDXHandle, sym::GDXSet)
572578
end
573579

574580
gdx_data_write_done(gdx)
581+
_set_domain_x(gdx, sym.name, sym.domain, dim)
575582
end
576583

577584
function _write_parameter(gdx::GDXHandle, sym::GDXParameter)
578-
_write_parameter_df(gdx, sym.name, sym.records, sym.description)
585+
_write_parameter_df(gdx, sym.name, sym.records, sym.description, sym.domain)
579586
end
580587

581-
function _write_parameter_df(gdx::GDXHandle, name::String, df::DataFrames.DataFrame, description::String="")
588+
function _write_parameter_df(gdx::GDXHandle, name::String, df::DataFrames.DataFrame, description::String="", domain::Vector{String}=String[])
582589
dim_cols = [n for n in names(df) if n != "value"]
583590
dim = length(dim_cols)
584591

@@ -596,6 +603,7 @@ function _write_parameter_df(gdx::GDXHandle, name::String, df::DataFrames.DataFr
596603
end
597604

598605
gdx_data_write_done(gdx)
606+
_set_domain_x(gdx, name, domain, dim)
599607
end
600608

601609
const _VAR_EQU_COLS = Set(["level", "marginal", "lower", "upper", "scale"])
@@ -623,6 +631,7 @@ function _write_variable(gdx::GDXHandle, sym::GDXVariable)
623631
end
624632

625633
gdx_data_write_done(gdx)
634+
_set_domain_x(gdx, sym.name, sym.domain, dim)
626635
end
627636

628637
function _write_equation(gdx::GDXHandle, sym::GDXEquation)
@@ -648,6 +657,7 @@ function _write_equation(gdx::GDXHandle, sym::GDXEquation)
648657
end
649658

650659
gdx_data_write_done(gdx)
660+
_set_domain_x(gdx, sym.name, sym.domain, dim)
651661
end
652662

653663
# =============================================================================

src/gdx_c_api.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,22 @@ function gdx_symbol_get_domain_x(gdx::GDXHandle, sym_nr::Int, dim::Int)
281281
return domains
282282
end
283283

284+
function gdx_symbol_set_domain_x(gdx_ptr::Ptr{Cvoid}, sym_nr::Int, domain_ids::Vector{String})
285+
return ccall(
286+
(@cpfx(:gdxsymbolsetdomainx), LIBGDX),
287+
Cint,
288+
(Ptr{Cvoid}, Cint, Ptr{Cstring}),
289+
gdx_ptr,
290+
sym_nr,
291+
domain_ids,
292+
)
293+
end
294+
295+
function gdx_symbol_set_domain_x(gdx::GDXHandle, sym_nr::Int, domain_ids::Vector{String})
296+
rc = gdx_symbol_set_domain_x(gdx.cptr[], sym_nr, domain_ids)
297+
return rc
298+
end
299+
284300
# =============================================================================
285301
# Alias
286302
# =============================================================================

test/test_gdxfile.jl

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,4 +412,114 @@ execute_unload "gams_gdx_test.gdx", i, p, x, y;
412412
show(io, a)
413413
@test occursin("j", String(take!(io)))
414414
end
415+
416+
@testset "Domain preservation on round-trip (issue #3)" begin
417+
set_df = DataFrame(i = ["a", "b", "c"])
418+
s = GDXSet("i", "index set", ["*"], set_df)
419+
par_df = DataFrame(i = ["a", "b", "c"], value = [10.0, 20.0, 30.0])
420+
p = GDXParameter("x", "A parameter over i", ["i"], par_df)
421+
gdxfile = GDXFile("", Dict{Symbol,GDXSymbol}(:i => s, :x => p))
422+
423+
outfile = joinpath(tempdir(), "gdx_jl_domain_test.gdx")
424+
write_gdx(outfile, gdxfile)
425+
426+
gdx2 = read_gdx(outfile)
427+
x2 = get_symbol(gdx2, :x)
428+
@test x2.domain == ["i"]
429+
@test names(gdx2[:x])[1] == "i"
430+
431+
rm(outfile, force=true)
432+
end
433+
434+
@testset "Domain preservation for variables (issue #3)" begin
435+
set_df = DataFrame(i = ["a", "b", "c"])
436+
s = GDXSet("i", "index set", ["*"], set_df)
437+
var_df = DataFrame(
438+
i = ["a", "b", "c"],
439+
level = [1.0, 2.0, 3.0],
440+
marginal = [0.0, 0.0, 0.0],
441+
lower = [-Inf, -Inf, -Inf],
442+
upper = [Inf, Inf, Inf],
443+
scale = [1.0, 1.0, 1.0]
444+
)
445+
v = GDXVariable("y", "A variable over i", ["i"], VarFree, var_df)
446+
gdxfile = GDXFile("", Dict{Symbol,GDXSymbol}(:i => s, :y => v))
447+
448+
outfile = joinpath(tempdir(), "gdx_jl_domain_var_test.gdx")
449+
write_gdx(outfile, gdxfile)
450+
451+
gdx2 = read_gdx(outfile)
452+
y2 = get_symbol(gdx2, :y)
453+
@test y2.domain == ["i"]
454+
@test names(gdx2[:y])[1] == "i"
455+
456+
rm(outfile, force=true)
457+
end
458+
459+
@testset "Domain preservation for equations (issue #3)" begin
460+
set_df = DataFrame(i = ["a", "b"])
461+
s = GDXSet("i", "index set", ["*"], set_df)
462+
eq_df = DataFrame(
463+
i = ["a", "b"],
464+
level = [1.0, 2.0],
465+
marginal = [0.5, 0.6],
466+
lower = [-Inf, -Inf],
467+
upper = [Inf, Inf],
468+
scale = [1.0, 1.0]
469+
)
470+
eq = GDXEquation("myeq", "test eq", ["i"], EqE, eq_df)
471+
gdxfile = GDXFile("", Dict{Symbol,GDXSymbol}(:i => s, :myeq => eq))
472+
473+
outfile = joinpath(tempdir(), "gdx_jl_domain_eq_test.gdx")
474+
write_gdx(outfile, gdxfile)
475+
476+
gdx2 = read_gdx(outfile)
477+
eq2 = get_symbol(gdx2, :myeq)
478+
@test eq2.domain == ["i"]
479+
@test names(gdx2[:myeq])[1] == "i"
480+
481+
rm(outfile, force=true)
482+
end
483+
484+
@testset "Multi-dimensional domain preservation (issue #3)" begin
485+
si = GDXSet("i", "rows", ["*"], DataFrame(i = ["a", "b"]))
486+
sj = GDXSet("j", "cols", ["*"], DataFrame(j = ["x", "y"]))
487+
par_df = DataFrame(
488+
i = ["a", "a", "b", "b"],
489+
j = ["x", "y", "x", "y"],
490+
value = [1.0, 2.0, 3.0, 4.0]
491+
)
492+
p = GDXParameter("cost", "transport cost", ["i", "j"], par_df)
493+
gdxfile = GDXFile("", Dict{Symbol,GDXSymbol}(:i => si, :j => sj, :cost => p))
494+
495+
outfile = joinpath(tempdir(), "gdx_jl_domain_2d_test.gdx")
496+
write_gdx(outfile, gdxfile)
497+
498+
gdx2 = read_gdx(outfile)
499+
cost2 = get_symbol(gdx2, :cost)
500+
@test cost2.domain == ["i", "j"]
501+
@test names(gdx2[:cost])[1:2] == ["i", "j"]
502+
503+
rm(outfile, force=true)
504+
end
505+
506+
@testset "Domain preservation with GAMS-generated file (issue #3)" begin
507+
gdx1 = read_gdx(test_gdx)
508+
509+
p1 = get_symbol(gdx1, :p)
510+
original_domain = p1.domain
511+
512+
outfile = joinpath(tempdir(), "gdx_jl_gams_domain_rt.gdx")
513+
write_gdx(outfile, gdx1)
514+
515+
gdx2 = read_gdx(outfile)
516+
p2 = get_symbol(gdx2, :p)
517+
@test p2.domain == original_domain
518+
519+
x1 = get_symbol(gdx1, :x)
520+
x2 = get_symbol(gdx2, :x)
521+
@test x2.domain == x1.domain
522+
523+
rm(outfile, force=true)
524+
end
415525
end

0 commit comments

Comments
 (0)