mirror of
https://github.com/ANL-CEEESA/UnitCommitment.jl.git
synced 2025-12-06 08:18:51 -06:00
Compare commits
3 Commits
hotfix/0.4
...
akazachk/f
| Author | SHA1 | Date | |
|---|---|---|---|
| 675143967f | |||
| ba9e086bea | |||
|
|
3baddf158a |
@@ -12,6 +12,7 @@ JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
|
||||
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
||||
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
|
||||
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
|
||||
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
|
||||
PackageCompiler = "9b87118b-4619-50d2-8e1e-99f35a4d4d9d"
|
||||
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
|
||||
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
|
||||
|
||||
@@ -6,6 +6,9 @@ SHELL := /bin/bash
|
||||
JULIA := julia --project=. --sysimage ../build/sysimage.so
|
||||
TIMESTAMP := $(shell date "+%Y-%m-%d %H:%M")
|
||||
SRC_FILES := $(wildcard ../src/*.jl)
|
||||
DEST := .
|
||||
FORMULATION := tight
|
||||
results_dir := results_$(FORMULATION)
|
||||
|
||||
INSTANCES_PGLIB := \
|
||||
pglib-uc/ca/2014-09-01_reserves_0 \
|
||||
@@ -38,6 +41,206 @@ INSTANCES_MATPOWER := \
|
||||
matpower/case6468rte/2017-08-01 \
|
||||
matpower/case6515rte/2017-08-01
|
||||
|
||||
INSTANCES_INFORMS1 := \
|
||||
matpower/case1888rte/2017-01-01 \
|
||||
matpower/case1888rte/2017-01-02 \
|
||||
matpower/case1888rte/2017-01-03 \
|
||||
matpower/case1888rte/2017-01-04 \
|
||||
matpower/case1888rte/2017-01-05 \
|
||||
matpower/case1888rte/2017-01-06 \
|
||||
matpower/case1888rte/2017-01-07 \
|
||||
matpower/case1888rte/2017-01-08 \
|
||||
matpower/case1888rte/2017-01-09 \
|
||||
matpower/case1888rte/2017-01-10 \
|
||||
matpower/case1888rte/2017-01-11 \
|
||||
matpower/case1888rte/2017-01-12 \
|
||||
matpower/case1888rte/2017-01-13 \
|
||||
matpower/case1888rte/2017-01-14 \
|
||||
matpower/case1888rte/2017-01-15 \
|
||||
matpower/case1888rte/2017-01-16 \
|
||||
matpower/case1888rte/2017-01-17 \
|
||||
matpower/case1888rte/2017-01-18 \
|
||||
matpower/case1888rte/2017-01-19 \
|
||||
matpower/case1888rte/2017-01-20 \
|
||||
matpower/case1888rte/2017-01-21 \
|
||||
matpower/case1888rte/2017-01-22 \
|
||||
matpower/case1888rte/2017-01-23 \
|
||||
matpower/case1888rte/2017-01-24 \
|
||||
matpower/case1888rte/2017-01-25 \
|
||||
matpower/case1888rte/2017-01-26 \
|
||||
matpower/case1888rte/2017-01-27 \
|
||||
matpower/case1888rte/2017-01-28 \
|
||||
matpower/case1888rte/2017-01-29 \
|
||||
matpower/case1888rte/2017-01-30 \
|
||||
matpower/case1888rte/2017-01-31 \
|
||||
matpower/case1888rte/2017-02-01 \
|
||||
matpower/case1888rte/2017-02-02 \
|
||||
matpower/case1888rte/2017-02-03 \
|
||||
matpower/case1888rte/2017-02-04 \
|
||||
matpower/case1888rte/2017-02-05 \
|
||||
matpower/case1888rte/2017-02-06 \
|
||||
matpower/case1888rte/2017-02-07 \
|
||||
matpower/case1888rte/2017-02-08 \
|
||||
matpower/case1888rte/2017-02-09 \
|
||||
matpower/case1888rte/2017-02-10 \
|
||||
matpower/case1888rte/2017-02-11 \
|
||||
matpower/case1888rte/2017-02-12 \
|
||||
matpower/case1888rte/2017-02-13 \
|
||||
matpower/case1888rte/2017-02-14 \
|
||||
matpower/case1888rte/2017-02-15 \
|
||||
matpower/case1888rte/2017-02-16 \
|
||||
matpower/case1888rte/2017-02-17 \
|
||||
matpower/case1888rte/2017-02-18 \
|
||||
matpower/case1888rte/2017-02-19 \
|
||||
matpower/case1888rte/2017-02-20 \
|
||||
matpower/case1888rte/2017-02-21 \
|
||||
matpower/case1888rte/2017-02-22 \
|
||||
matpower/case1888rte/2017-02-23 \
|
||||
matpower/case1888rte/2017-02-24 \
|
||||
matpower/case1888rte/2017-02-25 \
|
||||
matpower/case1888rte/2017-02-26 \
|
||||
matpower/case1888rte/2017-02-27 \
|
||||
matpower/case1888rte/2017-02-28 \
|
||||
matpower/case1888rte/2017-03-01
|
||||
|
||||
INSTANCES_INFORMS2 := \
|
||||
matpower/case3375wp/2017-01-01 \
|
||||
matpower/case3375wp/2017-01-02 \
|
||||
matpower/case3375wp/2017-01-03 \
|
||||
matpower/case3375wp/2017-01-04 \
|
||||
matpower/case3375wp/2017-01-05 \
|
||||
matpower/case3375wp/2017-01-06 \
|
||||
matpower/case3375wp/2017-01-07 \
|
||||
matpower/case3375wp/2017-01-08 \
|
||||
matpower/case3375wp/2017-01-09 \
|
||||
matpower/case3375wp/2017-01-10 \
|
||||
matpower/case3375wp/2017-01-11 \
|
||||
matpower/case3375wp/2017-01-12 \
|
||||
matpower/case3375wp/2017-01-13 \
|
||||
matpower/case3375wp/2017-01-14 \
|
||||
matpower/case3375wp/2017-01-15 \
|
||||
matpower/case3375wp/2017-01-16 \
|
||||
matpower/case3375wp/2017-01-17 \
|
||||
matpower/case3375wp/2017-01-18 \
|
||||
matpower/case3375wp/2017-01-19 \
|
||||
matpower/case3375wp/2017-01-20 \
|
||||
matpower/case3375wp/2017-01-21 \
|
||||
matpower/case3375wp/2017-01-22 \
|
||||
matpower/case3375wp/2017-01-23 \
|
||||
matpower/case3375wp/2017-01-24 \
|
||||
matpower/case3375wp/2017-01-25 \
|
||||
matpower/case3375wp/2017-01-26 \
|
||||
matpower/case3375wp/2017-01-27 \
|
||||
matpower/case3375wp/2017-01-28 \
|
||||
matpower/case3375wp/2017-01-29 \
|
||||
matpower/case3375wp/2017-01-30 \
|
||||
matpower/case3375wp/2017-01-31 \
|
||||
matpower/case3375wp/2017-02-01 \
|
||||
matpower/case3375wp/2017-02-02 \
|
||||
matpower/case3375wp/2017-02-03 \
|
||||
matpower/case3375wp/2017-02-04 \
|
||||
matpower/case3375wp/2017-02-05 \
|
||||
matpower/case3375wp/2017-02-06 \
|
||||
matpower/case3375wp/2017-02-07 \
|
||||
matpower/case3375wp/2017-02-08 \
|
||||
matpower/case3375wp/2017-02-09 \
|
||||
matpower/case3375wp/2017-02-10 \
|
||||
matpower/case3375wp/2017-02-11 \
|
||||
matpower/case3375wp/2017-02-12 \
|
||||
matpower/case3375wp/2017-02-13 \
|
||||
matpower/case3375wp/2017-02-14 \
|
||||
matpower/case3375wp/2017-02-15 \
|
||||
matpower/case3375wp/2017-02-16 \
|
||||
matpower/case3375wp/2017-02-17 \
|
||||
matpower/case3375wp/2017-02-18 \
|
||||
matpower/case3375wp/2017-02-19 \
|
||||
matpower/case3375wp/2017-02-20 \
|
||||
matpower/case3375wp/2017-02-21 \
|
||||
matpower/case3375wp/2017-02-22 \
|
||||
matpower/case3375wp/2017-02-23 \
|
||||
matpower/case3375wp/2017-02-24 \
|
||||
matpower/case3375wp/2017-02-25 \
|
||||
matpower/case3375wp/2017-02-26 \
|
||||
matpower/case3375wp/2017-02-27 \
|
||||
matpower/case3375wp/2017-02-28 \
|
||||
matpower/case3375wp/2017-03-01
|
||||
|
||||
INSTANCES_INFORMS3 := \
|
||||
matpower/case6468rte/2017-01-01 \
|
||||
matpower/case6468rte/2017-01-02 \
|
||||
matpower/case6468rte/2017-01-03 \
|
||||
matpower/case6468rte/2017-01-04 \
|
||||
matpower/case6468rte/2017-01-05 \
|
||||
matpower/case6468rte/2017-01-06 \
|
||||
matpower/case6468rte/2017-01-07 \
|
||||
matpower/case6468rte/2017-01-08 \
|
||||
matpower/case6468rte/2017-01-09 \
|
||||
matpower/case6468rte/2017-01-10 \
|
||||
matpower/case6468rte/2017-01-11 \
|
||||
matpower/case6468rte/2017-01-12 \
|
||||
matpower/case6468rte/2017-01-13 \
|
||||
matpower/case6468rte/2017-01-14 \
|
||||
matpower/case6468rte/2017-01-15 \
|
||||
matpower/case6468rte/2017-01-16 \
|
||||
matpower/case6468rte/2017-01-17 \
|
||||
matpower/case6468rte/2017-01-18 \
|
||||
matpower/case6468rte/2017-01-19 \
|
||||
matpower/case6468rte/2017-01-20 \
|
||||
matpower/case6468rte/2017-01-21 \
|
||||
matpower/case6468rte/2017-01-22 \
|
||||
matpower/case6468rte/2017-01-23 \
|
||||
matpower/case6468rte/2017-01-24 \
|
||||
matpower/case6468rte/2017-01-25 \
|
||||
matpower/case6468rte/2017-01-26 \
|
||||
matpower/case6468rte/2017-01-27 \
|
||||
matpower/case6468rte/2017-01-28 \
|
||||
matpower/case6468rte/2017-01-29 \
|
||||
matpower/case6468rte/2017-01-30 \
|
||||
matpower/case6468rte/2017-01-31 \
|
||||
matpower/case6468rte/2017-02-01 \
|
||||
matpower/case6468rte/2017-02-02 \
|
||||
matpower/case6468rte/2017-02-03 \
|
||||
matpower/case6468rte/2017-02-04 \
|
||||
matpower/case6468rte/2017-02-05 \
|
||||
matpower/case6468rte/2017-02-06 \
|
||||
matpower/case6468rte/2017-02-07 \
|
||||
matpower/case6468rte/2017-02-08 \
|
||||
matpower/case6468rte/2017-02-09 \
|
||||
matpower/case6468rte/2017-02-10 \
|
||||
matpower/case6468rte/2017-02-11 \
|
||||
matpower/case6468rte/2017-02-12 \
|
||||
matpower/case6468rte/2017-02-13 \
|
||||
matpower/case6468rte/2017-02-14 \
|
||||
matpower/case6468rte/2017-02-15 \
|
||||
matpower/case6468rte/2017-02-16 \
|
||||
matpower/case6468rte/2017-02-17 \
|
||||
matpower/case6468rte/2017-02-18 \
|
||||
matpower/case6468rte/2017-02-19 \
|
||||
matpower/case6468rte/2017-02-20 \
|
||||
matpower/case6468rte/2017-02-21 \
|
||||
matpower/case6468rte/2017-02-22 \
|
||||
matpower/case6468rte/2017-02-23 \
|
||||
matpower/case6468rte/2017-02-24 \
|
||||
matpower/case6468rte/2017-02-25 \
|
||||
matpower/case6468rte/2017-02-26 \
|
||||
matpower/case6468rte/2017-02-27 \
|
||||
matpower/case6468rte/2017-02-28 \
|
||||
matpower/case6468rte/2017-03-01
|
||||
|
||||
INSTANCES_TEST := \
|
||||
test/case14
|
||||
|
||||
#SAMPLES := 1 2 3
|
||||
SAMPLES := 1
|
||||
SOLUTIONS_MATPOWER := $(foreach s,$(SAMPLES),$(addprefix $(results_dir)/,$(addsuffix .$(s).sol.json,$(INSTANCES_MATPOWER))))
|
||||
SOLUTIONS_PGLIB := $(foreach s,$(SAMPLES),$(addprefix $(results_dir)/,$(addsuffix .$(s).sol.json,$(INSTANCES_PGLIB))))
|
||||
SOLUTIONS_INFORMS1 := $(foreach s,$(SAMPLES),$(addprefix $(results_dir)/,$(addsuffix .$(s).sol.json,$(INSTANCES_INFORMS1))))
|
||||
SOLUTIONS_INFORMS2 := $(foreach s,$(SAMPLES),$(addprefix $(results_dir)/,$(addsuffix .$(s).sol.json,$(INSTANCES_INFORMS2))))
|
||||
SOLUTIONS_INFORMS3 := $(foreach s,$(SAMPLES),$(addprefix $(results_dir)/,$(addsuffix .$(s).sol.json,$(INSTANCES_INFORMS3))))
|
||||
SOLUTIONS_TEST := $(foreach s,$(SAMPLES),$(addprefix $(results_dir)/,$(addsuffix .$(s).sol.json,$(INSTANCES_TEST))))
|
||||
|
||||
.PHONY: tables save small large clean-mps matpower pglib informs1 informs2 informs3 test pglib orlib
|
||||
|
||||
INSTANCES_ORLIB := \
|
||||
or-lib/20_0_1_w \
|
||||
or-lib/20_0_5_w \
|
||||
@@ -62,13 +265,8 @@ INSTANCES_TEJADA19 := \
|
||||
tejada19/UC_168h_131g \
|
||||
tejada19/UC_168h_199g
|
||||
|
||||
SAMPLES := 1 2 3 4 5
|
||||
SOLUTIONS_MATPOWER := $(foreach s,$(SAMPLES),$(addprefix results/,$(addsuffix .$(s).sol.json,$(INSTANCES_MATPOWER))))
|
||||
SOLUTIONS_PGLIB := $(foreach s,$(SAMPLES),$(addprefix results/,$(addsuffix .$(s).sol.json,$(INSTANCES_PGLIB))))
|
||||
SOLUTIONS_ORLIB := $(foreach s,$(SAMPLES),$(addprefix results/,$(addsuffix .$(s).sol.json,$(INSTANCES_ORLIB))))
|
||||
SOLUTIONS_TEJADA19 := $(foreach s,$(SAMPLES),$(addprefix results/,$(addsuffix .$(s).sol.json,$(INSTANCES_TEJADA19))))
|
||||
|
||||
.PHONY: tables save small large clean-mps matpower pglib orlib
|
||||
SOLUTIONS_ORLIB := $(foreach s,$(SAMPLES),$(addprefix $(results_dir)/,$(addsuffix .$(s).sol.json,$(INSTANCES_ORLIB))))
|
||||
SOLUTIONS_TEJADA19 := $(foreach s,$(SAMPLES),$(addprefix $(results_dir)/,$(addsuffix .$(s).sol.json,$(INSTANCES_TEJADA19))))
|
||||
|
||||
all: matpower pglib orlib tejada19
|
||||
|
||||
@@ -76,27 +274,51 @@ matpower: $(SOLUTIONS_MATPOWER)
|
||||
|
||||
pglib: $(SOLUTIONS_PGLIB)
|
||||
|
||||
informs1: $(SOLUTIONS_INFORMS1)
|
||||
informs2: $(SOLUTIONS_INFORMS2)
|
||||
informs3: $(SOLUTIONS_INFORMS3)
|
||||
|
||||
test: $(SOLUTIONS_TEST)
|
||||
|
||||
orlib: $(SOLUTIONS_ORLIB)
|
||||
|
||||
tejada19: $(SOLUTIONS_TEJADA19)
|
||||
|
||||
clean:
|
||||
@rm -rf tables/benchmark* tables/compare* results
|
||||
@rm -rf tables/benchmark* tables/compare* $(results_dir)
|
||||
|
||||
clean-mps:
|
||||
@rm -fv results/*/*.mps.gz results/*/*/*.mps.gz
|
||||
@rm -fv $(results_dir)/*/*.mps.gz results/*/*/*.mps.gz
|
||||
|
||||
clean-sol:
|
||||
@rm -rf results/*/*.sol.* results/*/*/*.sol.*
|
||||
@rm -rf $(results_dir)/*/*.sol.* results/*/*/*.sol.*
|
||||
|
||||
save:
|
||||
mkdir -p "runs/$(TIMESTAMP)"
|
||||
rsync -avP results tables "runs/$(TIMESTAMP)/"
|
||||
rsync -avP $(results_dir) tables "runs/$(TIMESTAMP)/"
|
||||
|
||||
results/%.sol.json: run.jl
|
||||
@echo "run $*"
|
||||
@mkdir -p $(dir results/$*)
|
||||
@$(JULIA) run.jl $* 2>&1 | cat > results/$*.log
|
||||
@mkdir -p $(dir $(DEST)/$(results_dir)/$*)
|
||||
@$(JULIA) run.jl $* default $(DEST)/$(results_dir) 2>&1 | cat > $(DEST)/$(results_dir)/$*.log
|
||||
@echo "run $* [done]"
|
||||
|
||||
results_tight/%.sol.json: run.jl
|
||||
@echo "run $*"
|
||||
@mkdir -p $(dir $(DEST)/$(results_dir)/$*)
|
||||
@$(JULIA) run.jl $* tight $(DEST)/$(results_dir) 2>&1 | cat > $(DEST)/$(results_dir)/$*.log
|
||||
@echo "run $* [done]"
|
||||
|
||||
results_default/%.sol.json: run.jl
|
||||
@echo "run $*"
|
||||
@mkdir -p $(dir $(DEST)/$(results_dir)/$*)
|
||||
@$(JULIA) run.jl $* default $(DEST)/$(results_dir) 2>&1 | cat > $(DEST)/$(results_dir)/$*.log
|
||||
@echo "run $* [done]"
|
||||
|
||||
results_sparse/%.sol.json: run.jl
|
||||
@echo "run $*"
|
||||
@mkdir -p $(dir $(DEST)/$(results_dir)/$*)
|
||||
@$(JULIA) run.jl $* sparse $(DEST)/$(results_dir) 2>&1 | cat > $(DEST)/$(results_dir)/$*.log
|
||||
@echo "run $* [done]"
|
||||
|
||||
tables:
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# This file is machine-generated - editing it directly is not advised
|
||||
|
||||
[[Artifacts]]
|
||||
deps = ["Pkg"]
|
||||
git-tree-sha1 = "c30985d8821e0cd73870b17b0ed0ce6dc44cb744"
|
||||
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
|
||||
version = "1.3.0"
|
||||
|
||||
[[Base64]]
|
||||
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
|
||||
|
||||
@@ -16,15 +22,15 @@ uuid = "b99e7846-7c00-51b0-8f62-c81ae34c0232"
|
||||
version = "0.5.10"
|
||||
|
||||
[[Bzip2_jll]]
|
||||
deps = ["Libdl", "Pkg"]
|
||||
git-tree-sha1 = "3663bfffede2ef41358b6fc2e1d8a6d50b3c3904"
|
||||
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
|
||||
git-tree-sha1 = "c3598e525718abcc440f69cc6d5f60dda0a1b61e"
|
||||
uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0"
|
||||
version = "1.0.6+2"
|
||||
version = "1.0.6+5"
|
||||
|
||||
[[CEnum]]
|
||||
git-tree-sha1 = "1b77a77c3b28e0b3f413f7567c9bb8dd9bdccd14"
|
||||
git-tree-sha1 = "215a9aa4a1f23fbd05b92769fdd62559488d70e9"
|
||||
uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82"
|
||||
version = "0.3.0"
|
||||
version = "0.4.1"
|
||||
|
||||
[[Calculus]]
|
||||
deps = ["LinearAlgebra"]
|
||||
@@ -34,9 +40,9 @@ version = "0.5.1"
|
||||
|
||||
[[Cbc]]
|
||||
deps = ["BinaryProvider", "CEnum", "Cbc_jll", "Libdl", "MathOptInterface", "SparseArrays"]
|
||||
git-tree-sha1 = "72e4299de0995a60a6230079adc7e47580870815"
|
||||
git-tree-sha1 = "929d0500c50387e7ac7ae9956ca7d7ce5312c90d"
|
||||
uuid = "9961bab8-2fa3-5c5a-9d89-47fab24efd76"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
|
||||
[[Cbc_jll]]
|
||||
deps = ["Cgl_jll", "Clp_jll", "CoinUtils_jll", "CompilerSupportLibraries_jll", "Libdl", "OpenBLAS32_jll", "Osi_jll", "Pkg"]
|
||||
@@ -52,15 +58,9 @@ version = "0.60.2+5"
|
||||
|
||||
[[Clp_jll]]
|
||||
deps = ["CoinUtils_jll", "CompilerSupportLibraries_jll", "Libdl", "OpenBLAS32_jll", "Osi_jll", "Pkg"]
|
||||
git-tree-sha1 = "70fe9e52fd95fa37f645e3d30f08f436cc5b1457"
|
||||
git-tree-sha1 = "79263d9383ca89b35f31c33ab5b880536a8413a4"
|
||||
uuid = "06985876-5285-5a41-9fcb-8948a742cc53"
|
||||
version = "1.17.6+5"
|
||||
|
||||
[[CodeTracking]]
|
||||
deps = ["InteractiveUtils", "UUIDs"]
|
||||
git-tree-sha1 = "cab4da992adc0a64f63fa30d2db2fd8bec40cab4"
|
||||
uuid = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
|
||||
version = "0.5.11"
|
||||
version = "1.17.6+6"
|
||||
|
||||
[[CodecBzip2]]
|
||||
deps = ["Bzip2_jll", "Libdl", "TranscodingStreams"]
|
||||
@@ -87,16 +87,16 @@ uuid = "bbf7d656-a473-5ed7-a52c-81e309532950"
|
||||
version = "0.3.0"
|
||||
|
||||
[[CompilerSupportLibraries_jll]]
|
||||
deps = ["Libdl", "Pkg"]
|
||||
git-tree-sha1 = "7c4f882c41faa72118841185afc58a2eb00ef612"
|
||||
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
|
||||
git-tree-sha1 = "8e695f735fca77e9708e795eda62afdb869cbb70"
|
||||
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
|
||||
version = "0.3.3+0"
|
||||
version = "0.3.4+0"
|
||||
|
||||
[[DataStructures]]
|
||||
deps = ["InteractiveUtils", "OrderedCollections"]
|
||||
git-tree-sha1 = "edad9434967fdc0a2631a65d902228400642120c"
|
||||
git-tree-sha1 = "88d48e133e6d3dd68183309877eac74393daa7eb"
|
||||
uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
|
||||
version = "0.17.19"
|
||||
version = "0.17.20"
|
||||
|
||||
[[Dates]]
|
||||
deps = ["Printf"]
|
||||
@@ -120,18 +120,15 @@ uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"
|
||||
|
||||
[[DocStringExtensions]]
|
||||
deps = ["LibGit2", "Markdown", "Pkg", "Test"]
|
||||
git-tree-sha1 = "c5714d9bcdba66389612dc4c47ed827c64112997"
|
||||
git-tree-sha1 = "50ddf44c53698f5e784bbebb3f4b21c5807401b1"
|
||||
uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
|
||||
[[Documenter]]
|
||||
deps = ["Base64", "Dates", "DocStringExtensions", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "REPL", "Test", "Unicode"]
|
||||
git-tree-sha1 = "1c593d1efa27437ed9dd365d1143c594b563e138"
|
||||
git-tree-sha1 = "fb1ff838470573adc15c71ba79f8d31328f035da"
|
||||
uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
|
||||
version = "0.25.1"
|
||||
|
||||
[[FileWatching]]
|
||||
uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"
|
||||
version = "0.25.2"
|
||||
|
||||
[[ForwardDiff]]
|
||||
deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "NaNMath", "Random", "SpecialFunctions", "StaticArrays"]
|
||||
@@ -140,10 +137,10 @@ uuid = "f6369f11-7733-5829-9624-2563aa707210"
|
||||
version = "0.10.12"
|
||||
|
||||
[[GLPK]]
|
||||
deps = ["BinaryProvider", "GLPK_jll", "Libdl", "MathOptInterface", "SparseArrays"]
|
||||
git-tree-sha1 = "86573ecb852e303b209212046af44871f1c0e49c"
|
||||
deps = ["BinaryProvider", "CEnum", "GLPK_jll", "Libdl", "MathOptInterface"]
|
||||
git-tree-sha1 = "0984f1669480cdecd465458b4abf81b238fbfe50"
|
||||
uuid = "60bf3e95-4087-53dc-ae20-288a0d20c6a6"
|
||||
version = "0.13.0"
|
||||
version = "0.14.2"
|
||||
|
||||
[[GLPK_jll]]
|
||||
deps = ["GMP_jll", "Libdl", "Pkg"]
|
||||
@@ -152,10 +149,10 @@ uuid = "e8aa6df9-e6ca-548a-97ff-1f85fc5b8b98"
|
||||
version = "4.64.0+0"
|
||||
|
||||
[[GMP_jll]]
|
||||
deps = ["Libdl", "Pkg"]
|
||||
git-tree-sha1 = "4dd9301d3a027c05ec403e756ee7a60e3c367e5d"
|
||||
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
|
||||
git-tree-sha1 = "15abc5f976569a1c9d651aff02f7222ef305eb2a"
|
||||
uuid = "781609d7-10c4-51f6-84f2-b8444358ff6d"
|
||||
version = "6.1.2+5"
|
||||
version = "6.1.2+6"
|
||||
|
||||
[[GZip]]
|
||||
deps = ["Libdl"]
|
||||
@@ -164,16 +161,16 @@ uuid = "92fee26a-97fe-5a0c-ad85-20a5f3185b63"
|
||||
version = "0.5.1"
|
||||
|
||||
[[Gurobi]]
|
||||
deps = ["Libdl", "LinearAlgebra", "MathOptInterface", "MathProgBase", "SparseArrays"]
|
||||
git-tree-sha1 = "f36a2fa62909675681aec582ccfc4a4a629406e4"
|
||||
deps = ["CEnum", "Libdl", "MathOptInterface"]
|
||||
git-tree-sha1 = "de2015da3bffcf005ef51b78163e81bfb7b2301d"
|
||||
uuid = "2e9cd046-0924-5485-92f1-d5272153d98b"
|
||||
version = "0.8.1"
|
||||
version = "0.9.2"
|
||||
|
||||
[[HTTP]]
|
||||
deps = ["Base64", "Dates", "IniFile", "MbedTLS", "Sockets"]
|
||||
git-tree-sha1 = "2ac03263ce44be4222342bca1c51c36ce7566161"
|
||||
git-tree-sha1 = "c7ec02c4c6a039a98a15f955462cd7aea5df4508"
|
||||
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
|
||||
version = "0.8.17"
|
||||
version = "0.8.19"
|
||||
|
||||
[[IniFile]]
|
||||
deps = ["Test"]
|
||||
@@ -185,6 +182,11 @@ version = "0.5.0"
|
||||
deps = ["Markdown"]
|
||||
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
|
||||
|
||||
[[JLLWrappers]]
|
||||
git-tree-sha1 = "c70593677bbf2c3ccab4f7500d0f4dacfff7b75c"
|
||||
uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
|
||||
version = "1.1.3"
|
||||
|
||||
[[JSON]]
|
||||
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
|
||||
git-tree-sha1 = "b34d7cef7b337321e97d22242c3c2b91f476748e"
|
||||
@@ -193,9 +195,9 @@ version = "0.21.0"
|
||||
|
||||
[[JSONSchema]]
|
||||
deps = ["HTTP", "JSON", "ZipFile"]
|
||||
git-tree-sha1 = "832a4d327d9dafdae55a6ecae04f9997c83615cc"
|
||||
git-tree-sha1 = "a9ecdbc90be216912a2e3e8a8e38dc4c93f0d065"
|
||||
uuid = "7d188eb4-7ad8-530c-ae41-71a32a6d4692"
|
||||
version = "0.3.0"
|
||||
version = "0.3.2"
|
||||
|
||||
[[JuMP]]
|
||||
deps = ["Calculus", "DataStructures", "ForwardDiff", "LinearAlgebra", "MathOptInterface", "MutableArithmetics", "NaNMath", "Random", "SparseArrays", "Statistics"]
|
||||
@@ -203,12 +205,6 @@ git-tree-sha1 = "cbab42e2e912109d27046aa88f02a283a9abac7c"
|
||||
uuid = "4076af6c-e467-56ae-b986-b466b2749572"
|
||||
version = "0.21.3"
|
||||
|
||||
[[JuliaInterpreter]]
|
||||
deps = ["CodeTracking", "InteractiveUtils", "Random", "UUIDs"]
|
||||
git-tree-sha1 = "79e4496b79e8af45198f8c291f26d4514d6b06d6"
|
||||
uuid = "aa1ae85d-cabe-5617-a682-6adf51b2e16a"
|
||||
version = "0.7.24"
|
||||
|
||||
[[LibGit2]]
|
||||
deps = ["Printf"]
|
||||
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"
|
||||
@@ -223,51 +219,33 @@ uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
||||
[[Logging]]
|
||||
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
|
||||
|
||||
[[LoweredCodeUtils]]
|
||||
deps = ["JuliaInterpreter"]
|
||||
git-tree-sha1 = "1b632dc108106101a9909db7be8f8b32ed8d02f7"
|
||||
uuid = "6f1432cf-f94c-5a45-995e-cdbf5db27b0b"
|
||||
version = "0.4.6"
|
||||
|
||||
[[MacroTools]]
|
||||
deps = ["Markdown", "Random"]
|
||||
git-tree-sha1 = "f7d2e3f654af75f01ec49be82c231c382214223a"
|
||||
git-tree-sha1 = "6a8a2a625ab0dea913aba95c11370589e0239ff0"
|
||||
uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
|
||||
version = "0.5.5"
|
||||
version = "0.5.6"
|
||||
|
||||
[[Markdown]]
|
||||
deps = ["Base64"]
|
||||
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
|
||||
|
||||
[[MathOptFormat]]
|
||||
deps = ["CodecZlib", "DataStructures", "HTTP", "JSON", "JSONSchema", "MathOptInterface"]
|
||||
git-tree-sha1 = "0206edd9310b863c222af23f71fde5d09e95c20c"
|
||||
uuid = "f4570300-c277-12e8-125c-4912f86ce65d"
|
||||
version = "0.2.2"
|
||||
|
||||
[[MathOptInterface]]
|
||||
deps = ["BenchmarkTools", "CodecBzip2", "CodecZlib", "JSON", "JSONSchema", "LinearAlgebra", "MutableArithmetics", "OrderedCollections", "SparseArrays", "Test", "Unicode"]
|
||||
git-tree-sha1 = "cd2049c055c7d192a235670d50faa375361624ba"
|
||||
git-tree-sha1 = "5a1d631e0a9087d425e024d66b9c71e92e78fda8"
|
||||
uuid = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
|
||||
version = "0.9.14"
|
||||
|
||||
[[MathProgBase]]
|
||||
deps = ["LinearAlgebra", "SparseArrays"]
|
||||
git-tree-sha1 = "9abbe463a1e9fc507f12a69e7f29346c2cdc472c"
|
||||
uuid = "fdba3010-5040-5b88-9595-932c9decdf73"
|
||||
version = "0.7.8"
|
||||
version = "0.9.17"
|
||||
|
||||
[[MbedTLS]]
|
||||
deps = ["Dates", "MbedTLS_jll", "Random", "Sockets"]
|
||||
git-tree-sha1 = "426a6978b03a97ceb7ead77775a1da066343ec6e"
|
||||
git-tree-sha1 = "1c38e51c3d08ef2278062ebceade0e46cefc96fe"
|
||||
uuid = "739be429-bea8-5141-9913-cc70e7f3736d"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
|
||||
[[MbedTLS_jll]]
|
||||
deps = ["Libdl", "Pkg"]
|
||||
git-tree-sha1 = "a0cb0d489819fa7ea5f9fa84c7e7eba19d8073af"
|
||||
git-tree-sha1 = "c0b1286883cac4e2b617539de41111e0776d02e8"
|
||||
uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"
|
||||
version = "2.16.6+1"
|
||||
version = "2.16.8+0"
|
||||
|
||||
[[Mmap]]
|
||||
uuid = "a63ad114-7e13-5084-954f-fe012c677804"
|
||||
@@ -284,21 +262,21 @@ uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3"
|
||||
version = "0.3.4"
|
||||
|
||||
[[OpenBLAS32_jll]]
|
||||
deps = ["CompilerSupportLibraries_jll", "Libdl", "Pkg"]
|
||||
git-tree-sha1 = "793b33911239d2651c356c823492b58d6490d36a"
|
||||
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"]
|
||||
git-tree-sha1 = "19c33675cdeb572c1b17f96c492459d4f4958036"
|
||||
uuid = "656ef2d0-ae68-5445-9ca0-591084a874a2"
|
||||
version = "0.3.9+4"
|
||||
version = "0.3.10+0"
|
||||
|
||||
[[OpenSpecFun_jll]]
|
||||
deps = ["CompilerSupportLibraries_jll", "Libdl", "Pkg"]
|
||||
git-tree-sha1 = "d51c416559217d974a1113522d5919235ae67a87"
|
||||
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"]
|
||||
git-tree-sha1 = "9db77584158d0ab52307f8c04f8e7c08ca76b5b3"
|
||||
uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e"
|
||||
version = "0.5.3+3"
|
||||
version = "0.5.3+4"
|
||||
|
||||
[[OrderedCollections]]
|
||||
git-tree-sha1 = "293b70ac1780f9584c89268a6e2a560d938a7065"
|
||||
git-tree-sha1 = "16c08bf5dba06609fe45e30860092d6fa41fde7b"
|
||||
uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
|
||||
version = "1.3.0"
|
||||
version = "1.3.1"
|
||||
|
||||
[[Osi_jll]]
|
||||
deps = ["CoinUtils_jll", "CompilerSupportLibraries_jll", "Libdl", "OpenBLAS32_jll", "Pkg"]
|
||||
@@ -308,15 +286,15 @@ version = "0.108.5+3"
|
||||
|
||||
[[PackageCompiler]]
|
||||
deps = ["Libdl", "Pkg", "UUIDs"]
|
||||
git-tree-sha1 = "98aa9c653e1dc3473bb5050caf8501293db9eee1"
|
||||
git-tree-sha1 = "3eee77c94646163f15bd8626acf494360897f890"
|
||||
uuid = "9b87118b-4619-50d2-8e1e-99f35a4d4d9d"
|
||||
version = "1.2.1"
|
||||
version = "1.2.3"
|
||||
|
||||
[[Parsers]]
|
||||
deps = ["Dates", "Test"]
|
||||
git-tree-sha1 = "10134f2ee0b1978ae7752c41306e131a684e1f06"
|
||||
deps = ["Dates"]
|
||||
git-tree-sha1 = "6fa4202675c05ba0f8268a6ddf07606350eda3ce"
|
||||
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
|
||||
version = "1.0.7"
|
||||
version = "1.0.11"
|
||||
|
||||
[[Pkg]]
|
||||
deps = ["Dates", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"]
|
||||
@@ -336,15 +314,9 @@ uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
||||
|
||||
[[Requires]]
|
||||
deps = ["UUIDs"]
|
||||
git-tree-sha1 = "d37400976e98018ee840e0ca4f9d20baa231dc6b"
|
||||
git-tree-sha1 = "28faf1c963ca1dc3ec87f166d92982e3c4a1f66d"
|
||||
uuid = "ae029012-a4dd-5104-9daa-d747884805df"
|
||||
version = "1.0.1"
|
||||
|
||||
[[Revise]]
|
||||
deps = ["CodeTracking", "Distributed", "FileWatching", "JuliaInterpreter", "LibGit2", "LoweredCodeUtils", "OrderedCollections", "Pkg", "REPL", "UUIDs", "Unicode"]
|
||||
git-tree-sha1 = "0992d4643e27b2deb9f2e4ec7a56b7033813a027"
|
||||
uuid = "295af30f-e4ad-537b-8983-00126c2a3abe"
|
||||
version = "2.7.3"
|
||||
version = "1.1.0"
|
||||
|
||||
[[SHA]]
|
||||
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
|
||||
@@ -399,19 +371,19 @@ uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
|
||||
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
|
||||
|
||||
[[UnitCommitment]]
|
||||
deps = ["Cbc", "DataStructures", "Documenter", "GLPK", "GZip", "JSON", "JuMP", "LinearAlgebra", "Logging", "MathOptFormat", "MathOptInterface", "PackageCompiler", "Printf", "Requires", "Revise", "SparseArrays", "Test", "TimerOutputs"]
|
||||
deps = ["Cbc", "DataStructures", "Documenter", "GLPK", "GZip", "Gurobi", "JSON", "JuMP", "LinearAlgebra", "Logging", "MathOptInterface", "OrderedCollections", "PackageCompiler", "Printf", "Requires", "SparseArrays", "Test", "TimerOutputs"]
|
||||
path = ".."
|
||||
uuid = "64606440-39ea-11e9-0f29-3303a1d3d877"
|
||||
version = "2.1.0"
|
||||
|
||||
[[ZipFile]]
|
||||
deps = ["Libdl", "Printf", "Zlib_jll"]
|
||||
git-tree-sha1 = "254975fef2fc526583bb9b7c9420fe66ffe09f2f"
|
||||
git-tree-sha1 = "c3a5637e27e914a7a445b8d0ad063d701931e9f7"
|
||||
uuid = "a5390f91-8eb1-5f08-bee0-b1d1ffed6cea"
|
||||
version = "0.9.2"
|
||||
version = "0.9.3"
|
||||
|
||||
[[Zlib_jll]]
|
||||
deps = ["Libdl", "Pkg"]
|
||||
git-tree-sha1 = "622d8b6dc0c7e8029f17127703de9819134d1b71"
|
||||
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
|
||||
git-tree-sha1 = "320228915c8debb12cb434c59057290f0834dbf6"
|
||||
uuid = "83775a58-1f1d-513f-b197-d71354ab007a"
|
||||
version = "1.2.11+14"
|
||||
version = "1.2.11+18"
|
||||
|
||||
@@ -11,14 +11,47 @@ using Printf
|
||||
using LinearAlgebra
|
||||
|
||||
function main()
|
||||
basename, suffix = split(ARGS[1], ".")
|
||||
solution_filename = "results/$basename.$suffix.sol.json"
|
||||
model_filename = "results/$basename.$suffix.mps.gz"
|
||||
|
||||
NUM_THREADS = 4
|
||||
time_limit = 60 * 20
|
||||
BLAS.set_num_threads(NUM_THREADS)
|
||||
|
||||
BLAS.set_num_threads(4)
|
||||
global_logger(TimeLogger(initial_time = time()))
|
||||
if length(ARGS) >= 2
|
||||
mode = string("_", ARGS[2])
|
||||
else
|
||||
mode = "_default"
|
||||
end
|
||||
if length(ARGS) >= 3 && !isempty(strip(ARGS[3]))
|
||||
results_dir = ARGS[3]
|
||||
else
|
||||
results_dir = string("./","results$mode")
|
||||
end
|
||||
|
||||
# Validate mode and set formulation
|
||||
if mode == "_default"
|
||||
formulation = UnitCommitment.DefaultFormulation
|
||||
elseif mode == "_tight"
|
||||
formulation = UnitCommitment.TightFormulation
|
||||
elseif mode == "_sparse"
|
||||
formulation = UnitCommitment.SparseDefaultFormulation
|
||||
else
|
||||
error("Unknown formulation requested: ", ARGS[2])
|
||||
end
|
||||
|
||||
# Filename is instance_name.sample_number.sol.gz
|
||||
# Parse out the instance + sample parts to create output files
|
||||
basename, suffix = split(ARGS[1], ".") # will not work if suffix part is not present
|
||||
model_filename_stub = string(results_dir,"/$basename.$suffix")
|
||||
solution_filename = string("$model_filename_stub.sol.json")
|
||||
|
||||
# Choose logging options
|
||||
logname, logfile = nothing, nothing
|
||||
#logname = string("$model_filename_stub.out")
|
||||
if isa(logname, String) && !isempty(logname)
|
||||
logfile = open(logname, "w")
|
||||
global_logger(TimeLogger(initial_time = time(), file = logfile))
|
||||
else
|
||||
global_logger(TimeLogger(initial_time = time()))
|
||||
end
|
||||
|
||||
total_time = @elapsed begin
|
||||
@info "Reading: $basename"
|
||||
@@ -28,18 +61,26 @@ function main()
|
||||
@info @sprintf("Read problem in %.2f seconds", time_read)
|
||||
|
||||
time_model = @elapsed begin
|
||||
model = build_model(instance=instance,
|
||||
optimizer=optimizer_with_attributes(Gurobi.Optimizer,
|
||||
"Threads" => 4,
|
||||
"Seed" => rand(1:1000),
|
||||
))
|
||||
optimizer=optimizer_with_attributes(Gurobi.Optimizer,
|
||||
"Threads" => NUM_THREADS,
|
||||
"Seed" => rand(1:1000))
|
||||
model = build_model(instance=instance, optimizer=optimizer, formulation=formulation)
|
||||
end
|
||||
end
|
||||
|
||||
@info "Setting names..."
|
||||
UnitCommitment.set_variable_names!(model)
|
||||
|
||||
model_filename = string(model_filename_stub,".init",".mps.gz")
|
||||
@info string("Exporting initial model without transmission constraints to ", model_filename)
|
||||
JuMP.write_to_file(model.mip, model_filename)
|
||||
|
||||
total_time += @elapsed begin
|
||||
@info "Optimizing..."
|
||||
BLAS.set_num_threads(1)
|
||||
UnitCommitment.optimize!(model, time_limit=time_limit, gap_limit=1e-3)
|
||||
|
||||
end
|
||||
|
||||
@info @sprintf("Total time was %.2f seconds", total_time)
|
||||
|
||||
@info "Writing: $solution_filename"
|
||||
@@ -51,11 +92,13 @@ function main()
|
||||
@info "Verifying solution..."
|
||||
UnitCommitment.validate(instance, solution)
|
||||
|
||||
@info "Setting variable names..."
|
||||
UnitCommitment.set_variable_names!(model)
|
||||
|
||||
@info "Exporting model..."
|
||||
model_filename = string(model_filename_stub,".final",".mps.gz")
|
||||
@info string("Exporting final model to ", model_filename)
|
||||
JuMP.write_to_file(model.mip, model_filename)
|
||||
end
|
||||
|
||||
if !isnothing(logfile)
|
||||
close(logfile)
|
||||
end
|
||||
end # main
|
||||
|
||||
main()
|
||||
|
||||
182
scripts/instances.txt
Normal file
182
scripts/instances.txt
Normal file
@@ -0,0 +1,182 @@
|
||||
matpower/case1888rte/2017-01-01
|
||||
matpower/case1888rte/2017-01-02
|
||||
matpower/case1888rte/2017-01-03
|
||||
matpower/case1888rte/2017-01-04
|
||||
matpower/case1888rte/2017-01-05
|
||||
matpower/case1888rte/2017-01-06
|
||||
matpower/case1888rte/2017-01-07
|
||||
matpower/case1888rte/2017-01-08
|
||||
matpower/case1888rte/2017-01-09
|
||||
matpower/case1888rte/2017-01-10
|
||||
matpower/case1888rte/2017-01-11
|
||||
matpower/case1888rte/2017-01-12
|
||||
matpower/case1888rte/2017-01-13
|
||||
matpower/case1888rte/2017-01-14
|
||||
matpower/case1888rte/2017-01-15
|
||||
matpower/case1888rte/2017-01-16
|
||||
matpower/case1888rte/2017-01-17
|
||||
matpower/case1888rte/2017-01-18
|
||||
matpower/case1888rte/2017-01-19
|
||||
matpower/case1888rte/2017-01-20
|
||||
matpower/case1888rte/2017-01-21
|
||||
matpower/case1888rte/2017-01-22
|
||||
matpower/case1888rte/2017-01-23
|
||||
matpower/case1888rte/2017-01-24
|
||||
matpower/case1888rte/2017-01-25
|
||||
matpower/case1888rte/2017-01-26
|
||||
matpower/case1888rte/2017-01-27
|
||||
matpower/case1888rte/2017-01-28
|
||||
matpower/case1888rte/2017-01-29
|
||||
matpower/case1888rte/2017-01-30
|
||||
matpower/case1888rte/2017-01-31
|
||||
matpower/case1888rte/2017-02-01
|
||||
matpower/case1888rte/2017-02-02
|
||||
matpower/case1888rte/2017-02-03
|
||||
matpower/case1888rte/2017-02-04
|
||||
matpower/case1888rte/2017-02-05
|
||||
matpower/case1888rte/2017-02-06
|
||||
matpower/case1888rte/2017-02-07
|
||||
matpower/case1888rte/2017-02-08
|
||||
matpower/case1888rte/2017-02-09
|
||||
matpower/case1888rte/2017-02-10
|
||||
matpower/case1888rte/2017-02-11
|
||||
matpower/case1888rte/2017-02-12
|
||||
matpower/case1888rte/2017-02-13
|
||||
matpower/case1888rte/2017-02-14
|
||||
matpower/case1888rte/2017-02-15
|
||||
matpower/case1888rte/2017-02-16
|
||||
matpower/case1888rte/2017-02-17
|
||||
matpower/case1888rte/2017-02-18
|
||||
matpower/case1888rte/2017-02-19
|
||||
matpower/case1888rte/2017-02-20
|
||||
matpower/case1888rte/2017-02-21
|
||||
matpower/case1888rte/2017-02-22
|
||||
matpower/case1888rte/2017-02-23
|
||||
matpower/case1888rte/2017-02-24
|
||||
matpower/case1888rte/2017-02-25
|
||||
matpower/case1888rte/2017-02-26
|
||||
matpower/case1888rte/2017-02-27
|
||||
matpower/case1888rte/2017-02-28
|
||||
matpower/case1888rte/2017-03-01
|
||||
matpower/case3375wp/2017-01-01
|
||||
matpower/case3375wp/2017-01-02
|
||||
matpower/case3375wp/2017-01-03
|
||||
matpower/case3375wp/2017-01-04
|
||||
matpower/case3375wp/2017-01-05
|
||||
matpower/case3375wp/2017-01-06
|
||||
matpower/case3375wp/2017-01-07
|
||||
matpower/case3375wp/2017-01-08
|
||||
matpower/case3375wp/2017-01-09
|
||||
matpower/case3375wp/2017-01-10
|
||||
matpower/case3375wp/2017-01-11
|
||||
matpower/case3375wp/2017-01-12
|
||||
matpower/case3375wp/2017-01-13
|
||||
matpower/case3375wp/2017-01-14
|
||||
matpower/case3375wp/2017-01-15
|
||||
matpower/case3375wp/2017-01-16
|
||||
matpower/case3375wp/2017-01-17
|
||||
matpower/case3375wp/2017-01-18
|
||||
matpower/case3375wp/2017-01-19
|
||||
matpower/case3375wp/2017-01-20
|
||||
matpower/case3375wp/2017-01-21
|
||||
matpower/case3375wp/2017-01-22
|
||||
matpower/case3375wp/2017-01-23
|
||||
matpower/case3375wp/2017-01-24
|
||||
matpower/case3375wp/2017-01-25
|
||||
matpower/case3375wp/2017-01-26
|
||||
matpower/case3375wp/2017-01-27
|
||||
matpower/case3375wp/2017-01-28
|
||||
matpower/case3375wp/2017-01-29
|
||||
matpower/case3375wp/2017-01-30
|
||||
matpower/case3375wp/2017-01-31
|
||||
matpower/case3375wp/2017-02-01
|
||||
matpower/case3375wp/2017-02-02
|
||||
matpower/case3375wp/2017-02-03
|
||||
matpower/case3375wp/2017-02-04
|
||||
matpower/case3375wp/2017-02-05
|
||||
matpower/case3375wp/2017-02-06
|
||||
matpower/case3375wp/2017-02-07
|
||||
matpower/case3375wp/2017-02-08
|
||||
matpower/case3375wp/2017-02-09
|
||||
matpower/case3375wp/2017-02-10
|
||||
matpower/case3375wp/2017-02-11
|
||||
matpower/case3375wp/2017-02-12
|
||||
matpower/case3375wp/2017-02-13
|
||||
matpower/case3375wp/2017-02-14
|
||||
matpower/case3375wp/2017-02-15
|
||||
matpower/case3375wp/2017-02-16
|
||||
matpower/case3375wp/2017-02-17
|
||||
matpower/case3375wp/2017-02-18
|
||||
matpower/case3375wp/2017-02-19
|
||||
matpower/case3375wp/2017-02-20
|
||||
matpower/case3375wp/2017-02-21
|
||||
matpower/case3375wp/2017-02-22
|
||||
matpower/case3375wp/2017-02-23
|
||||
matpower/case3375wp/2017-02-24
|
||||
matpower/case3375wp/2017-02-25
|
||||
matpower/case3375wp/2017-02-26
|
||||
matpower/case3375wp/2017-02-27
|
||||
matpower/case3375wp/2017-02-28
|
||||
matpower/case3375wp/2017-03-01
|
||||
matpower/case6468rte/2017-01-01
|
||||
matpower/case6468rte/2017-01-02
|
||||
matpower/case6468rte/2017-01-03
|
||||
matpower/case6468rte/2017-01-04
|
||||
matpower/case6468rte/2017-01-05
|
||||
matpower/case6468rte/2017-01-06
|
||||
matpower/case6468rte/2017-01-07
|
||||
matpower/case6468rte/2017-01-08
|
||||
matpower/case6468rte/2017-01-09
|
||||
matpower/case6468rte/2017-01-10
|
||||
matpower/case6468rte/2017-01-11
|
||||
matpower/case6468rte/2017-01-12
|
||||
matpower/case6468rte/2017-01-13
|
||||
matpower/case6468rte/2017-01-14
|
||||
matpower/case6468rte/2017-01-15
|
||||
matpower/case6468rte/2017-01-16
|
||||
matpower/case6468rte/2017-01-17
|
||||
matpower/case6468rte/2017-01-18
|
||||
matpower/case6468rte/2017-01-19
|
||||
matpower/case6468rte/2017-01-20
|
||||
matpower/case6468rte/2017-01-21
|
||||
matpower/case6468rte/2017-01-22
|
||||
matpower/case6468rte/2017-01-23
|
||||
matpower/case6468rte/2017-01-24
|
||||
matpower/case6468rte/2017-01-25
|
||||
matpower/case6468rte/2017-01-26
|
||||
matpower/case6468rte/2017-01-27
|
||||
matpower/case6468rte/2017-01-28
|
||||
matpower/case6468rte/2017-01-29
|
||||
matpower/case6468rte/2017-01-30
|
||||
matpower/case6468rte/2017-01-31
|
||||
matpower/case6468rte/2017-02-01
|
||||
matpower/case6468rte/2017-02-02
|
||||
matpower/case6468rte/2017-02-03
|
||||
matpower/case6468rte/2017-02-04
|
||||
matpower/case6468rte/2017-02-05
|
||||
matpower/case6468rte/2017-02-06
|
||||
matpower/case6468rte/2017-02-07
|
||||
matpower/case6468rte/2017-02-08
|
||||
matpower/case6468rte/2017-02-09
|
||||
matpower/case6468rte/2017-02-10
|
||||
matpower/case6468rte/2017-02-11
|
||||
matpower/case6468rte/2017-02-12
|
||||
matpower/case6468rte/2017-02-13
|
||||
matpower/case6468rte/2017-02-14
|
||||
matpower/case6468rte/2017-02-15
|
||||
matpower/case6468rte/2017-02-16
|
||||
matpower/case6468rte/2017-02-17
|
||||
matpower/case6468rte/2017-02-18
|
||||
matpower/case6468rte/2017-02-19
|
||||
matpower/case6468rte/2017-02-20
|
||||
matpower/case6468rte/2017-02-21
|
||||
matpower/case6468rte/2017-02-22
|
||||
matpower/case6468rte/2017-02-23
|
||||
matpower/case6468rte/2017-02-24
|
||||
matpower/case6468rte/2017-02-25
|
||||
matpower/case6468rte/2017-02-26
|
||||
matpower/case6468rte/2017-02-27
|
||||
matpower/case6468rte/2017-02-28
|
||||
matpower/case6468rte/2017-03-01
|
||||
|
||||
test/case14
|
||||
49
scripts/run_batch.sh
Normal file
49
scripts/run_batch.sh
Normal file
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
#SBATCH --array=1-180
|
||||
#SBATCH --time=02:00:00
|
||||
#SBATCH --account=def-alodi
|
||||
#SBATCH --mem-per-cpu=1G
|
||||
#SBATCH --cpus-per-task=4
|
||||
#SBATCH --mail-user=aleksandr.kazachkov@polymtl.ca
|
||||
#SBATCH --mail-type=BEGIN
|
||||
#SBATCH --mail-type=END
|
||||
#SBATCH --mail-type=FAIL
|
||||
#SBATCH --array=182
|
||||
#SBATCH --time=00:00:30
|
||||
#SBATCH --mem-per-cpu=500M
|
||||
#SBATCH --cpus-per-task=1
|
||||
#SBATCH --time=01:00:00
|
||||
#SBATCH --mem-per-cpu=1G
|
||||
#SBATCH --cpus-per-task=4
|
||||
|
||||
MODE="tight"
|
||||
if [ ! -z $1 ]; then
|
||||
MODE=$1
|
||||
fi
|
||||
|
||||
#CASE_NUM=`printf %03d $SLURM_ARRAY_TASK_ID`
|
||||
PROJ_DIR="${REPOS_DIR}/UnitCommitment2.jl"
|
||||
INST=$(sed -n "${SLURM_ARRAY_TASK_ID}p" ${PROJ_DIR}/scripts/instances.txt)
|
||||
#DEST="${PROJ_DIR}/benchmark"
|
||||
DEST="${HOME}/scratch/uc"
|
||||
RESULTS_DIR="${DEST}/results_${MODE}"
|
||||
NUM_SAMPLES=1
|
||||
|
||||
if [ $MODE == "sparse" ] || [ $MODE == "default" ] || [ $MODE == "tight" ]
|
||||
then
|
||||
echo "Running task $SLURM_ARRAY_TASK_ID for instance $INST with results sent to ${RESULTS_DIR}"
|
||||
else
|
||||
echo "Unrecognized mode: $1. Exiting."
|
||||
exit
|
||||
fi
|
||||
|
||||
cd ${PROJ_DIR}/benchmark
|
||||
mkdir -p $(dirname ${RESULTS_DIR}/${INST})
|
||||
for i in $(seq ${NUM_SAMPLES}); do
|
||||
FILE=$INST.$i
|
||||
#echo "Running $FILE at `date` using command julia --project=${PROJ_DIR}/benchmark --sysimage=${PROJ_DIR}/build/sysimage.so ${PROJ_DIR}/benchmark/run.jl ${FILE} ${MODE} ${RESULTS_DIR} 2&>1 | cat > ${RESULTS_DIR}/${FILE}.log"
|
||||
#julia --project=${PROJ_DIR}/benchmark --sysimage=${PROJ_DIR}/build/sysimage.so ${PROJ_DIR}/benchmark/run.jl ${FILE} ${MODE} ${RESULTS_DIR} 2&>1 | cat > ${RESULTS_DIR}/${FILE}.log
|
||||
echo "Running $FILE at `date` using command julia --project=${PROJ_DIR}/benchmark --sysimage=${PROJ_DIR}/build/sysimage.so ${PROJ_DIR}/benchmark/run.jl ${FILE} ${MODE} ${RESULTS_DIR} &> ${RESULTS_DIR}/${FILE}.log"
|
||||
julia --project=${PROJ_DIR}/benchmark --sysimage=${PROJ_DIR}/build/sysimage.so ${PROJ_DIR}/benchmark/run.jl ${FILE} ${MODE} ${RESULTS_DIR} &> ${RESULTS_DIR}/${FILE}.log
|
||||
#julia --project=${PROJ_DIR}/benchmark --sysimage=${PROJ_DIR}/build/sysimage.so ${PROJ_DIR}/benchmark/run.jl ${FILE} ${MODE} ${RESULTS_DIR}
|
||||
done
|
||||
@@ -7,7 +7,12 @@ module UnitCommitment
|
||||
include("dotdict.jl")
|
||||
include("instance.jl")
|
||||
include("screening.jl")
|
||||
include("model.jl")
|
||||
include("components.jl")
|
||||
include("variables.jl")
|
||||
include("constraints.jl")
|
||||
include("formulation.jl")
|
||||
#include("model.jl")
|
||||
include("model2.jl")
|
||||
include("sensitivity.jl")
|
||||
include("validate.jl")
|
||||
include("convert.jl")
|
||||
|
||||
46
src/components.jl
Normal file
46
src/components.jl
Normal file
@@ -0,0 +1,46 @@
|
||||
##################################################
|
||||
# Component types
|
||||
abstract type UCComponentType end
|
||||
abstract type RequiredConstraints <: UCComponentType end
|
||||
abstract type SystemConstraints <: UCComponentType end
|
||||
abstract type GenerationLimits <: UCComponentType end
|
||||
abstract type PiecewiseProduction <: UCComponentType end
|
||||
abstract type UpDownTime <: UCComponentType end
|
||||
abstract type ReserveConstraints <: UCComponentType end
|
||||
abstract type RampLimits <: UCComponentType end
|
||||
abstract type StartupCosts <: UCComponentType end
|
||||
abstract type ShutdownCosts <: UCComponentType end
|
||||
|
||||
##################################################
|
||||
# Components
|
||||
"""
|
||||
Generic component of the unit commitment problem.
|
||||
|
||||
Elements
|
||||
===
|
||||
* `name`: name of the component
|
||||
* `description`: gives a brief summary of what the component adds
|
||||
* `type`: reference back to the UCComponentType being modeled
|
||||
* `vars`: required variables
|
||||
* `constrs`: constraints that are created by this function
|
||||
* `add_component`: function to add constraints and update the objective to capture this component
|
||||
* `params`: extra parameters the component might use
|
||||
"""
|
||||
mutable struct UCComponent
|
||||
"Name of the component."
|
||||
name::String
|
||||
"Description of what the component adds."
|
||||
description::String
|
||||
"Which part of the unit commitment problem is modeled by this component."
|
||||
type::Type{<:UCComponentType}
|
||||
"Variables that are needed for the component (subset of `var_list`)."
|
||||
vars::Union{Array{Symbol},Nothing}
|
||||
"Equations that are modified for the component (subset of `constr_list`)."
|
||||
constrs::Union{Array{Symbol},Nothing}
|
||||
"Function to add constraints and objective coefficients needed for this component to the model. Signature should be (component, mip, model)."
|
||||
add_component::Union{Function,Nothing}
|
||||
"Extra parameters for the component."
|
||||
params::Any
|
||||
end # struct UCComponent
|
||||
|
||||
export UCComponent
|
||||
31
src/constraints.jl
Normal file
31
src/constraints.jl
Normal file
@@ -0,0 +1,31 @@
|
||||
##################################################
|
||||
# Constraints
|
||||
"""
|
||||
List of constraints that the model will potentially have
|
||||
"""
|
||||
constr_list =
|
||||
[
|
||||
:startup_choose,
|
||||
:startup_restrict,
|
||||
:segprod_limit,
|
||||
:segprod_limita,
|
||||
:segprod_limitb,
|
||||
:prod_above_def,
|
||||
:prod_limit,
|
||||
:str_prod_limit,
|
||||
:binary_link,
|
||||
:switch_on_off,
|
||||
:ramp_up,
|
||||
:ramp_down,
|
||||
:str_ramp_up,
|
||||
:str_ramp_down,
|
||||
:startstop_limit,
|
||||
:startup_limit,
|
||||
:shutdown_limit,
|
||||
:min_uptime,
|
||||
:min_downtime,
|
||||
:power_balance,
|
||||
:net_injection_def,
|
||||
:min_reserve
|
||||
]
|
||||
|
||||
1711
src/formulation.jl
Normal file
1711
src/formulation.jl
Normal file
File diff suppressed because it is too large
Load Diff
@@ -8,8 +8,11 @@ using DataStructures
|
||||
import Base: getindex, time
|
||||
import GZip
|
||||
|
||||
abstract type UCElement end
|
||||
|
||||
mutable struct Bus
|
||||
abstract type Time <: UCElement end
|
||||
|
||||
mutable struct Bus <: UCElement
|
||||
name::String
|
||||
offset::Int
|
||||
load::Array{Float64}
|
||||
@@ -18,19 +21,19 @@ mutable struct Bus
|
||||
end
|
||||
|
||||
|
||||
mutable struct CostSegment
|
||||
mutable struct CostSegment <: UCElement
|
||||
mw::Array{Float64}
|
||||
cost::Array{Float64}
|
||||
end
|
||||
|
||||
|
||||
mutable struct StartupCategory
|
||||
mutable struct StartupCategory <: UCElement
|
||||
delay::Int
|
||||
cost::Float64
|
||||
end
|
||||
|
||||
|
||||
mutable struct Unit
|
||||
mutable struct Unit <: UCElement
|
||||
name::String
|
||||
bus::Bus
|
||||
max_power::Array{Float64}
|
||||
@@ -48,10 +51,10 @@ mutable struct Unit
|
||||
initial_power::Union{Float64,Nothing}
|
||||
provides_spinning_reserves::Array{Bool}
|
||||
startup_categories::Array{StartupCategory}
|
||||
end
|
||||
end # Unit
|
||||
|
||||
|
||||
mutable struct TransmissionLine
|
||||
mutable struct TransmissionLine <: UCElement
|
||||
name::String
|
||||
offset::Int
|
||||
source::Bus
|
||||
@@ -64,19 +67,19 @@ mutable struct TransmissionLine
|
||||
end
|
||||
|
||||
|
||||
mutable struct Reserves
|
||||
mutable struct Reserves <: UCElement
|
||||
spinning::Array{Float64}
|
||||
end
|
||||
|
||||
|
||||
mutable struct Contingency
|
||||
mutable struct Contingency <: UCElement
|
||||
name::String
|
||||
lines::Array{TransmissionLine}
|
||||
units::Array{Unit}
|
||||
end
|
||||
|
||||
|
||||
mutable struct PriceSensitiveLoad
|
||||
mutable struct PriceSensitiveLoad <: UCElement
|
||||
name::String
|
||||
bus::Bus
|
||||
demand::Array{Float64}
|
||||
@@ -87,6 +90,8 @@ end
|
||||
mutable struct UnitCommitmentInstance
|
||||
time::Int
|
||||
power_balance_penalty::Array{Float64}
|
||||
"Penalty for failing to meet reserve requirement."
|
||||
shortfall_penalty::Array{Float64}
|
||||
units::Array{Unit}
|
||||
buses::Array{Bus}
|
||||
lines::Array{TransmissionLine}
|
||||
@@ -151,6 +156,8 @@ function from_json(json; fix=true)
|
||||
# Read parameters
|
||||
power_balance_penalty = timeseries(json["Parameters"]["Power balance penalty (\$/MW)"],
|
||||
default=[1000.0 for t in 1:T])
|
||||
shortfall_penalty = timeseries(json["Parameters"]["Reserve shortfall penalty (\$/MW)"],
|
||||
default=[0. for t in 1:T])
|
||||
|
||||
# Read buses
|
||||
for (bus_name, dict) in json["Buses"]
|
||||
@@ -286,6 +293,7 @@ function from_json(json; fix=true)
|
||||
|
||||
instance = UnitCommitmentInstance(T,
|
||||
power_balance_penalty,
|
||||
shortfall_penalty,
|
||||
units,
|
||||
buses,
|
||||
lines,
|
||||
|
||||
475
src/model2.jl
Normal file
475
src/model2.jl
Normal file
@@ -0,0 +1,475 @@
|
||||
# UnitCommitment.jl: Optimization Package for Security-Constrained Unit Commitment
|
||||
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
# Writen by Alinson S. Xavier <axavier@anl.gov>
|
||||
|
||||
using JuMP, MathOptInterface, DataStructures
|
||||
import JuMP: value, fix, set_name
|
||||
|
||||
# Extend some JuMP functions so that decision variables can be safely replaced by
|
||||
# (constant) floating point numbers.
|
||||
function value(x::Float64)
|
||||
x
|
||||
end
|
||||
|
||||
function fix(x::Float64, v::Float64; force)
|
||||
abs(x - v) < 1e-6 || error("Value mismatch: $x != $v")
|
||||
end
|
||||
|
||||
function set_name(x::Float64, n::String)
|
||||
# nop
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
Create a JuMP model using the variables and constraints defined by
|
||||
the collection of `UCComponent`s in `formulation`.
|
||||
|
||||
Parameters
|
||||
===
|
||||
* `isf`: injection shift factors
|
||||
* `lodf`: line outage distribution factors
|
||||
"""
|
||||
function build_model(;
|
||||
filename::Union{String, Nothing}=nothing,
|
||||
instance::Union{UnitCommitmentInstance, Nothing}=nothing,
|
||||
isf::Union{Array{Float64,2}, Nothing}=nothing,
|
||||
lodf::Union{Array{Float64,2}, Nothing}=nothing,
|
||||
isf_cutoff::Float64=0.005,
|
||||
lodf_cutoff::Float64=0.001,
|
||||
optimizer=nothing,
|
||||
model=nothing,
|
||||
variable_names::Bool=false,
|
||||
formulation::Vector{UCComponent} = UnitCommitment.DefaultFormulation,
|
||||
) :: UnitCommitmentModel2
|
||||
|
||||
if (filename == nothing) && (instance == nothing)
|
||||
error("Either filename or instance must be specified")
|
||||
end
|
||||
|
||||
if filename != nothing
|
||||
@info "Reading: $(filename)"
|
||||
time_read = @elapsed begin
|
||||
instance = UnitCommitment.read(filename)
|
||||
end
|
||||
@info @sprintf("Read problem in %.2f seconds", time_read)
|
||||
end
|
||||
|
||||
if length(instance.buses) == 1
|
||||
isf = zeros(0, 0)
|
||||
lodf = zeros(0, 0)
|
||||
else
|
||||
if isf == nothing
|
||||
@info "Computing injection shift factors..."
|
||||
time_isf = @elapsed begin
|
||||
isf = UnitCommitment.injection_shift_factors(lines=instance.lines,
|
||||
buses=instance.buses)
|
||||
end
|
||||
@info @sprintf("Computed ISF in %.2f seconds", time_isf)
|
||||
|
||||
@info "Computing line outage factors..."
|
||||
time_lodf = @elapsed begin
|
||||
lodf = UnitCommitment.line_outage_factors(lines=instance.lines,
|
||||
buses=instance.buses,
|
||||
isf=isf)
|
||||
end
|
||||
@info @sprintf("Computed LODF in %.2f seconds", time_lodf)
|
||||
|
||||
@info @sprintf("Applying PTDF and LODF cutoffs (%.5f, %.5f)", isf_cutoff, lodf_cutoff)
|
||||
isf[abs.(isf) .< isf_cutoff] .= 0
|
||||
lodf[abs.(lodf) .< lodf_cutoff] .= 0
|
||||
end
|
||||
end
|
||||
|
||||
@info "Building model..."
|
||||
time_model = @elapsed begin
|
||||
if model == nothing
|
||||
if optimizer == nothing
|
||||
mip = Model()
|
||||
else
|
||||
mip = Model(optimizer)
|
||||
end
|
||||
else
|
||||
mip = model
|
||||
end
|
||||
@info "About to build model"
|
||||
model = UnitCommitmentModel2(mip, # JuMP.Model
|
||||
DotDict(), # vars
|
||||
DotDict(), # eqs
|
||||
DotDict(), # exprs
|
||||
instance, # UnitCommitmentInstance
|
||||
isf, # injection shift factors
|
||||
lodf, # line outage distribution factors
|
||||
AffExpr(), # obj
|
||||
formulation, # formulation
|
||||
)
|
||||
|
||||
# Prepare variables
|
||||
for var in get_required_variables(formulation)
|
||||
add_variable(mip, model, instance, UnitCommitment.var_list[var])
|
||||
end # prepare variables
|
||||
|
||||
# Prepare constraints
|
||||
for constr in get_required_constraints(formulation)
|
||||
add_constraint(mip, model, instance, constr)
|
||||
end # prepare constraints
|
||||
|
||||
# Prepare expressions (in this case, affine expressions that are later used as part of constraints or objective)
|
||||
# * :startup_cost => contribution to objective of startup costs
|
||||
for field in [:startup_cost] #[:net_injection]
|
||||
setproperty!(model.exprs, field, OrderedDict())
|
||||
end
|
||||
|
||||
# Add components to mip
|
||||
for c in formulation
|
||||
c.add_component(c, mip, model)
|
||||
end
|
||||
|
||||
# Add objective function
|
||||
build_obj_function!(model)
|
||||
end # end timing of building model
|
||||
@info @sprintf("Built model in %.2f seconds", time_model)
|
||||
|
||||
if variable_names
|
||||
set_variable_names!(model)
|
||||
end
|
||||
|
||||
return model
|
||||
end # build_model
|
||||
|
||||
|
||||
"""
|
||||
Add a particular variable to `model.vars`.
|
||||
"""
|
||||
function add_variable(mip::JuMP.Model,
|
||||
model::UnitCommitmentModel2,
|
||||
instance::UnitCommitmentInstance,
|
||||
var::UCVariable)
|
||||
setproperty!(model.vars, var.name, OrderedDict())
|
||||
x = getproperty(model.vars, var.name)
|
||||
if !isnothing(var.add_variable)
|
||||
var.add_variable(var, x, mip, instance)
|
||||
return
|
||||
end
|
||||
|
||||
# The following is a bit complex-looking, but the idea is ultimately straightforward
|
||||
# We want to loop over the possible index values for var,
|
||||
# for every dimension of var (e.g., looping over units and time)
|
||||
# The OrderedDict `ind_to_field` maps a UCElement to the corresponding field name within a UnitCommitmentInstance
|
||||
# NB: this can be an array of field names, such as [:x, :y], which means we want to access instance.x.y
|
||||
# Furthermore, `var` has an array `indices` of UCElement values, describing which index loops over
|
||||
# So all we want is to extract the _length_ of the corresponding field of `instance`
|
||||
# We create a Tuple so we can feed it to CartesianIndices
|
||||
fields = UnitCommitment.ind_to_field(var.indices)
|
||||
num_indices = UnitCommitment.num_indices(fields)
|
||||
|
||||
# There is some really complicated logic below that one day needs to be improved
|
||||
# (we need to handle nested indices, and this is one way that hopefully works, but it is definitely not intuitive)
|
||||
loop_primitive = UnitCommitment.loop_over_indices(UnitCommitment.get_indices_tuple(instance, fields))
|
||||
indices = UnitCommitment.get_indices(loop_primitive) # returns an array of tuples? or a unit range maybe.
|
||||
|
||||
for ind in indices
|
||||
# For each of the indices, check if the field corresponding to that index has a name
|
||||
# Then we will index the variable by that name instead of the integer
|
||||
curr_tuple = Tuple(ind)
|
||||
new_tuple = ()
|
||||
for i in 1:num_indices
|
||||
curr_field = UnitCommitment.get_nested_field(instance, fields, i, curr_tuple)
|
||||
if :name in propertynames(curr_field)
|
||||
new_tuple = (new_tuple..., curr_field.name)
|
||||
else
|
||||
new_tuple = (new_tuple..., curr_tuple[i])
|
||||
end
|
||||
end
|
||||
name = string(var.name, "[")
|
||||
for (i,val) in enumerate(new_tuple)
|
||||
name = string(name, val, i < num_indices ? "," : "")
|
||||
end
|
||||
name = string(name, "]")
|
||||
if num_indices == 1
|
||||
new_tuple = new_tuple[1]
|
||||
end
|
||||
x[new_tuple] = @variable(mip,
|
||||
lower_bound=var.lb,
|
||||
upper_bound=var.ub,
|
||||
integer=var.integer,
|
||||
base_name=name)
|
||||
end
|
||||
### DEBUG
|
||||
#if var.name == :reserve_shortfall
|
||||
# @show var.name, num_indices, loop_primitive, indices, x
|
||||
# #@show JuMP.all_variables(mip)
|
||||
#end
|
||||
### DEBUG
|
||||
end # add_variable
|
||||
|
||||
|
||||
"""
|
||||
Add constraint to `model.eqs` (set of affine expressions represent left-hand side of constraints).
|
||||
"""
|
||||
function add_constraint(mip::JuMP.Model,
|
||||
model::UnitCommitmentModel2,
|
||||
instance::UnitCommitmentInstance,
|
||||
constr::Symbol)
|
||||
setproperty!(model.eqs, constr, OrderedDict())
|
||||
end # add_constraint
|
||||
|
||||
|
||||
"""
|
||||
Components of the objective include, summed over time:
|
||||
* production cost above minimum
|
||||
* minimum production cost if generator is on
|
||||
* startup cost
|
||||
* shutdown cost
|
||||
* cost of not meeting shortfall
|
||||
* penalty for not meeting or exceeding load (using curtai variable)
|
||||
* shutdown cost
|
||||
"""
|
||||
function build_obj_function!(model::UnitCommitmentModel2)
|
||||
@objective(model.mip, Min, model.obj)
|
||||
end # build_obj_function
|
||||
|
||||
|
||||
function enforce_transmission(;
|
||||
model::UnitCommitmentModel2,
|
||||
violation::Violation,
|
||||
isf::Array{Float64,2},
|
||||
lodf::Array{Float64,2})::Nothing
|
||||
|
||||
instance, mip, vars = model.instance, model.mip, model.vars
|
||||
limit::Float64 = 0.0
|
||||
|
||||
if violation.outage_line == nothing
|
||||
limit = violation.monitored_line.normal_flow_limit[violation.time]
|
||||
@info @sprintf(" %8.3f MW overflow in %-5s time %3d (pre-contingency)",
|
||||
violation.amount,
|
||||
violation.monitored_line.name,
|
||||
violation.time)
|
||||
else
|
||||
limit = violation.monitored_line.emergency_flow_limit[violation.time]
|
||||
@info @sprintf(" %8.3f MW overflow in %-5s time %3d (outage: line %s)",
|
||||
violation.amount,
|
||||
violation.monitored_line.name,
|
||||
violation.time,
|
||||
violation.outage_line.name)
|
||||
end
|
||||
|
||||
fm = violation.monitored_line.name
|
||||
t = violation.time
|
||||
flow = @variable(mip, base_name="flow[$fm,$t]")
|
||||
|
||||
# |flow| <= limit + overflow
|
||||
overflow = vars.overflow[violation.monitored_line.name, violation.time]
|
||||
@constraint(mip, flow <= limit + overflow)
|
||||
@constraint(mip, -flow <= limit + overflow)
|
||||
|
||||
if violation.outage_line == nothing
|
||||
@constraint(mip, flow == sum(vars.net_injection[b.name, violation.time] *
|
||||
isf[violation.monitored_line.offset, b.offset]
|
||||
for b in instance.buses
|
||||
if b.offset > 0))
|
||||
else
|
||||
@constraint(mip, flow == sum(vars.net_injection[b.name, violation.time] * (
|
||||
isf[violation.monitored_line.offset, b.offset] + (
|
||||
lodf[violation.monitored_line.offset, violation.outage_line.offset] *
|
||||
isf[violation.outage_line.offset, b.offset]
|
||||
)
|
||||
)
|
||||
for b in instance.buses
|
||||
if b.offset > 0))
|
||||
end
|
||||
nothing
|
||||
end # enforce_transmission
|
||||
|
||||
|
||||
function set_variable_names!(model::UnitCommitmentModel2)
|
||||
@info "Setting variable and constraint names..."
|
||||
time_varnames = @elapsed begin
|
||||
#set_jump_names!(model.vars) # amk: already set
|
||||
set_jump_names!(model.eqs)
|
||||
end
|
||||
@info @sprintf("Set names in %.2f seconds", time_varnames)
|
||||
end # set_variable_names
|
||||
|
||||
|
||||
function set_jump_names!(dict)
|
||||
for name in keys(dict)
|
||||
for idx in keys(dict[name])
|
||||
idx_str = isa(idx, Tuple) ? join(map(string, idx), ",") : idx
|
||||
set_name(dict[name][idx], "$name[$idx_str]")
|
||||
end
|
||||
end
|
||||
end # set_jump_names
|
||||
|
||||
|
||||
function get_solution(model::UnitCommitmentModel2)
|
||||
instance, T = model.instance, model.instance.time
|
||||
function timeseries(vars, collection)
|
||||
return OrderedDict(b.name => [round(value(vars[b.name, t]), digits=5) for t in 1:T]
|
||||
for b in collection)
|
||||
end
|
||||
function production_cost(g)
|
||||
return [value(model.vars.is_on[g.name, t]) * g.min_power_cost[t] +
|
||||
sum(Float64[value(model.vars.segprod[g.name, k, t]) * g.cost_segments[k].cost[t]
|
||||
for k in 1:length(g.cost_segments)])
|
||||
for t in 1:T]
|
||||
end
|
||||
function production(g)
|
||||
return [value(model.vars.is_on[g.name, t]) * g.min_power[t] +
|
||||
sum(Float64[value(model.vars.segprod[g.name, k, t])
|
||||
for k in 1:length(g.cost_segments)])
|
||||
for t in 1:T]
|
||||
end
|
||||
function startup_cost(g)
|
||||
#S = length(g.startup_categories)
|
||||
#return [sum(g.startup_categories[s].cost * value(model.vars.startup[g.name, s, t])
|
||||
# for s in 1:S)
|
||||
# for t in 1:T]
|
||||
return [ value.(model.exprs.startup_cost[g.name, t]) for t in 1:T ]
|
||||
end
|
||||
sol = OrderedDict()
|
||||
sol["Production (MW)"] = OrderedDict(g.name => production(g) for g in instance.units)
|
||||
sol["Production cost (\$)"] = OrderedDict(g.name => production_cost(g) for g in instance.units)
|
||||
sol["Startup cost (\$)"] = OrderedDict(g.name => startup_cost(g) for g in instance.units)
|
||||
sol["Is on"] = timeseries(model.vars.is_on, instance.units)
|
||||
sol["Switch on"] = timeseries(model.vars.switch_on, instance.units)
|
||||
sol["Switch off"] = timeseries(model.vars.switch_off, instance.units)
|
||||
sol["Reserve (MW)"] = timeseries(model.vars.reserve, instance.units)
|
||||
sol["Net injection (MW)"] = timeseries(model.vars.net_injection, instance.buses)
|
||||
sol["Load curtail (MW)"] = timeseries(model.vars.curtail, instance.buses)
|
||||
if !isempty(instance.lines)
|
||||
sol["Line overflow (MW)"] = timeseries(model.vars.overflow, instance.lines)
|
||||
end
|
||||
if !isempty(instance.price_sensitive_loads)
|
||||
sol["Price-sensitive loads (MW)"] = timeseries(model.vars.loads, instance.price_sensitive_loads)
|
||||
end
|
||||
return sol
|
||||
end # get_solution
|
||||
|
||||
|
||||
function fix!(model::UnitCommitmentModel2, solution)::Nothing
|
||||
vars, instance, T = model.vars, model.instance, model.instance.time
|
||||
for g in instance.units
|
||||
for t in 1:T
|
||||
is_on = round(solution["Is on"][g.name][t])
|
||||
production = round(solution["Production (MW)"][g.name][t], digits=5)
|
||||
reserve = round(solution["Reserve (MW)"][g.name][t], digits=5)
|
||||
JuMP.fix(vars.is_on[g.name, t], is_on, force=true)
|
||||
JuMP.fix(vars.prod_above[g.name, t], production - is_on * g.min_power[t], force=true)
|
||||
JuMP.fix(vars.reserve[g.name, t], reserve, force=true)
|
||||
end
|
||||
end
|
||||
end # fix!
|
||||
|
||||
|
||||
function set_warm_start!(model::UnitCommitmentModel2, solution)::Nothing
|
||||
vars, instance, T = model.vars, model.instance, model.instance.time
|
||||
for g in instance.units
|
||||
for t in 1:T
|
||||
JuMP.set_start_value(vars.is_on[g.name, t], solution["Is on"][g.name][t])
|
||||
JuMP.set_start_value(vars.switch_on[g.name, t], solution["Switch on"][g.name][t])
|
||||
JuMP.set_start_value(vars.switch_off[g.name, t], solution["Switch off"][g.name][t])
|
||||
end
|
||||
end
|
||||
end # set_warm_start
|
||||
|
||||
|
||||
function optimize!(model::UnitCommitmentModel2;
|
||||
time_limit=3600,
|
||||
gap_limit=1e-4,
|
||||
two_phase_gap=true,
|
||||
)::Nothing
|
||||
|
||||
function set_gap(gap)
|
||||
try
|
||||
JuMP.set_optimizer_attribute(model.mip, "MIPGap", gap)
|
||||
@info @sprintf("MIP gap tolerance set to %f", gap)
|
||||
catch
|
||||
@warn "Could not change MIP gap tolerance"
|
||||
end
|
||||
end
|
||||
|
||||
instance = model.instance
|
||||
initial_time = time()
|
||||
|
||||
large_gap = false
|
||||
has_transmission = (length(model.isf) > 0)
|
||||
|
||||
if has_transmission && two_phase_gap
|
||||
set_gap(1e-2)
|
||||
large_gap = true
|
||||
else
|
||||
set_gap(gap_limit)
|
||||
end
|
||||
|
||||
while true
|
||||
time_elapsed = time() - initial_time
|
||||
time_remaining = time_limit - time_elapsed
|
||||
if time_remaining < 0
|
||||
@info "Time limit exceeded"
|
||||
break
|
||||
end
|
||||
|
||||
@info @sprintf("Setting MILP time limit to %.2f seconds", time_remaining)
|
||||
JuMP.set_time_limit_sec(model.mip, time_remaining)
|
||||
|
||||
@info "Solving MILP..."
|
||||
JuMP.optimize!(model.mip)
|
||||
|
||||
has_transmission || break
|
||||
|
||||
violations = find_violations(model)
|
||||
if isempty(violations)
|
||||
@info "No violations found"
|
||||
if large_gap
|
||||
large_gap = false
|
||||
set_gap(gap_limit)
|
||||
else
|
||||
break
|
||||
end
|
||||
else
|
||||
enforce_transmission(model, violations)
|
||||
end
|
||||
end
|
||||
|
||||
nothing
|
||||
end # optimize!
|
||||
|
||||
|
||||
"""
|
||||
Identify which transmission lines are violated.
|
||||
See find_violations description from screening.jl.
|
||||
"""
|
||||
function find_violations(model::UnitCommitmentModel2)
|
||||
instance, vars = model.instance, model.vars
|
||||
length(instance.buses) > 1 || return []
|
||||
violations = []
|
||||
@info "Verifying transmission limits..."
|
||||
time_screening = @elapsed begin
|
||||
non_slack_buses = [b for b in instance.buses if b.offset > 0]
|
||||
net_injections = [value(vars.net_injection[b.name, t])
|
||||
for b in non_slack_buses, t in 1:instance.time]
|
||||
overflow = [value(vars.overflow[lm.name, t])
|
||||
for lm in instance.lines, t in 1:instance.time]
|
||||
violations = UnitCommitment.find_violations(instance=instance,
|
||||
net_injections=net_injections,
|
||||
overflow=overflow,
|
||||
isf=model.isf,
|
||||
lodf=model.lodf)
|
||||
end
|
||||
@info @sprintf("Verified transmission limits in %.2f seconds", time_screening)
|
||||
return violations
|
||||
end # find_violations
|
||||
|
||||
|
||||
function enforce_transmission(model::UnitCommitmentModel2, violations::Array{Violation, 1})
|
||||
for v in violations
|
||||
enforce_transmission(model=model,
|
||||
violation=v,
|
||||
isf=model.isf,
|
||||
lodf=model.lodf)
|
||||
end
|
||||
end # enforce_transmission
|
||||
|
||||
|
||||
export UnitCommitmentModel2, build_model, get_solution, optimize!
|
||||
@@ -115,6 +115,7 @@ function validate_units(instance, solution; tol=0.01)
|
||||
actual_production_cost = solution["Production cost (\$)"][unit.name]
|
||||
actual_startup_cost = solution["Startup cost (\$)"][unit.name]
|
||||
is_on = bin(solution["Is on"][unit.name])
|
||||
switch_off = bin(solution["Switch off"][unit.name]) # some formulations may not use this
|
||||
|
||||
for t in 1:instance.time
|
||||
# Auxiliary variables
|
||||
@@ -127,7 +128,8 @@ function validate_units(instance, solution; tol=0.01)
|
||||
is_starting_up = !is_on[t-1] && is_on[t]
|
||||
is_shutting_down = is_on[t-1] && !is_on[t]
|
||||
ramp_up = max(0, production[t] + reserve[t] - production[t-1])
|
||||
ramp_down = max(0, production[t-1] - production[t])
|
||||
#ramp_down = max(0, production[t-1] - production[t])
|
||||
ramp_down = max(0, production[t-1] + reserve[t-1] - production[t])
|
||||
end
|
||||
|
||||
# Compute production costs
|
||||
@@ -193,8 +195,12 @@ function validate_units(instance, solution; tol=0.01)
|
||||
|
||||
# Shutdown limit
|
||||
if is_shutting_down && (ramp_down > unit.shutdown_limit + tol)
|
||||
@error @sprintf("Unit %s exceeds shutdown limit at time %d (%.2f > %.2f)",
|
||||
unit.name, t, ramp_down, unit.shutdown_limit)
|
||||
@error @sprintf("Unit %s exceeds shutdown limit at time %d (%.2f > %.2f)\n\tproduction[t-1] = %.2f\n\treserve[t-1] = %.2f\n\tproduction[t] = %.2f\n\treserve[t] = %.2f\n\tis_on[t-1] = %d\n\tis_on[t] = %d",
|
||||
unit.name, t, ramp_down, unit.shutdown_limit,
|
||||
(t == 1 ? unit.initial_power : production[t-1]), production[t],
|
||||
(t == 1 ? 0. : reserve[t-1]), reserve[t],
|
||||
(t == 1 ? unit.initial_status != nothing && unit.initial_status > 0 : is_on[t-1]), is_on[t]
|
||||
)
|
||||
err_count += 1
|
||||
end
|
||||
|
||||
@@ -207,8 +213,12 @@ function validate_units(instance, solution; tol=0.01)
|
||||
|
||||
# Ramp-down limit
|
||||
if !is_starting_up && !is_shutting_down && (ramp_down > unit.ramp_down_limit + tol)
|
||||
@error @sprintf("Unit %s exceeds ramp down limit at time %d (%.2f > %.2f)",
|
||||
unit.name, t, ramp_down, unit.ramp_down_limit)
|
||||
@error @sprintf("Unit %s exceeds ramp down limit at time %d (%.2f > %.2f)\n\tproduction[t-1] = %.2f\n\treserve[t-1] = %.2f\n\tproduction[t] = %.2f\n\treserve[t] = %.2f\n\tis_on[t-1] = %d\n\tis_on[t] = %d",
|
||||
unit.name, t, ramp_down, unit.ramp_down_limit,
|
||||
(t == 1 ? unit.initial_power : production[t-1]), production[t],
|
||||
(t == 1 ? 0. : reserve[t-1]), reserve[t],
|
||||
(t == 1 ? unit.initial_status != nothing && unit.initial_status > 0 : is_on[t-1]), is_on[t]
|
||||
)
|
||||
err_count += 1
|
||||
end
|
||||
|
||||
@@ -218,13 +228,16 @@ function validate_units(instance, solution; tol=0.01)
|
||||
# Calculate how much time the unit has been offline
|
||||
time_down = 0
|
||||
for k in 1:(t-1)
|
||||
if !is_on[t - k]
|
||||
if !is_on[t - k]
|
||||
time_down += 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
if t == time_down + 1
|
||||
if t == time_down + 1 && !switch_off[1]
|
||||
# If unit has always been off, then the correct startup cost depends on how long was it off before t = 1
|
||||
# Absent known initial conditions, we assume it was off for the minimum downtime
|
||||
# TODO: verify the formulations are making the same assumption...
|
||||
initial_down = unit.min_downtime
|
||||
if unit.initial_status < 0
|
||||
initial_down = -unit.initial_status
|
||||
|
||||
471
src/variables.jl
Normal file
471
src/variables.jl
Normal file
@@ -0,0 +1,471 @@
|
||||
using DataStructures # for OrderedDict
|
||||
using JuMP
|
||||
|
||||
##################################################
|
||||
# Variables
|
||||
#mutable struct UCVariable
|
||||
# "Name of the variable."
|
||||
# name::Symbol
|
||||
# "What does the variable represent?"
|
||||
# description::String
|
||||
# "Global lower bound for the variable (may be adjusted later)."
|
||||
# lb::Float64
|
||||
# "Global upper bound for the variable (may be adjusted later)."
|
||||
# ub::Float64
|
||||
# "Is the variable integer-restricted?"
|
||||
# integer::Bool
|
||||
# "What are we indexing over?"*
|
||||
# " Recursive structure, e.g., [X,Y] means Y is a field in X,"*
|
||||
# " and [X,[Y1,Z],Y2] means Y1 and Y2 are fields in X and Z is a field in Y1.\n"*
|
||||
# " [ X, [Y,A,B], [Y,A,A], [Z,[D,E],F], T ]\n"*
|
||||
# " => [x, y1, y1.a, y1.b, y2, y2.a1, y2.a2, z, z.d, z.d.e, z.f, t]."
|
||||
# indices::Vector
|
||||
# "Function to add the variable; if this is missing, we will attempt to add the variable automatically using the `indices`. Signature should be (variable, model.vars.familyname, mip, instance)."
|
||||
# add_variable::Union{Function,Nothing}
|
||||
#end # UCVariable
|
||||
|
||||
# TODO Above did not work for some reason
|
||||
mutable struct UCVariable
|
||||
name::Symbol
|
||||
description::String
|
||||
lb::Float64
|
||||
ub::Float64
|
||||
integer::Bool
|
||||
indices::Vector
|
||||
add_variable::Union{Function,Nothing}
|
||||
end
|
||||
|
||||
"""
|
||||
It holds that x(t,t') = 0 if t' does not belong to 𝒢 = [t+DT, t+TC-1].
|
||||
This is because DT is the minimum downtime, so there is no way x(t,t')=1 for t'<t+DT
|
||||
and TC is the "time until cold" => if the generator starts afterwards, always has max cost.
|
||||
"""
|
||||
function add_downtime_arcs(var::UCVariable,
|
||||
x::OrderedDict,
|
||||
mip::JuMP.Model,
|
||||
instance::UnitCommitmentInstance)
|
||||
T = instance.time
|
||||
for g in instance.units
|
||||
S = length(g.startup_categories)
|
||||
if S == 0
|
||||
continue
|
||||
end
|
||||
|
||||
DT = g.min_downtime # minimum time offline
|
||||
TC = g.startup_categories[S].delay # time offline until totally cold
|
||||
|
||||
for t1 = 1:T-1
|
||||
for t2 = t1+1:T
|
||||
# It holds that x(t,t') = 0 if t' does not belong to 𝒢 = [t+DT, t+TC-1]
|
||||
# This is because DT is the minimum downtime, so there is no way x(t,t')=1 for t'<t+DT
|
||||
# and TC is the "time until cold" => if the generator starts afterwards, always has max cost
|
||||
if (t2 < t1 + DT) || (t2 >= t1 + TC)
|
||||
continue
|
||||
end
|
||||
|
||||
name = string(var.name, "[", g.name, ",", t1, ",", t2, "]")
|
||||
x[g.name, t1, t2] = @variable(mip,
|
||||
lower_bound=var.lb,
|
||||
upper_bound=var.ub,
|
||||
integer=var.integer,
|
||||
base_name=name)
|
||||
end # loop over time 2
|
||||
end # loop over time 1
|
||||
end # loop over units
|
||||
end # add_downtime_arcs
|
||||
|
||||
|
||||
"""
|
||||
If there is a penalty specified for not meeting the reserve, then we add a reserve shortfall variable.
|
||||
"""
|
||||
function add_reserve_shortfall(var::UCVariable,
|
||||
x::OrderedDict,
|
||||
mip::JuMP.Model,
|
||||
instance::UnitCommitmentInstance)
|
||||
T = instance.time
|
||||
for t = 1:T
|
||||
if instance.shortfall_penalty[t] > 1e-7
|
||||
name = string(var.name, "[", t, "]")
|
||||
x[t] = @variable(mip,
|
||||
lower_bound=var.lb,
|
||||
upper_bound=var.ub,
|
||||
integer=var.integer,
|
||||
base_name=name)
|
||||
end
|
||||
end # loop over time
|
||||
end # add_reserve_shortfall
|
||||
|
||||
|
||||
"""
|
||||
Variables that the model may (or may not) use.
|
||||
|
||||
Note the relationship
|
||||
r_g(t) = bar{p}_g(t) - p_g(t)
|
||||
= bar{p}'_g(t) - p'_g(t)
|
||||
"""
|
||||
var_list = OrderedDict{Symbol,UCVariable}(
|
||||
:prod
|
||||
=> UCVariable(:prod,
|
||||
"[gen, t]; power from generator gen at time t; p_g(t) = p'_g(t) + g.min_power[t] * u_g(t)",
|
||||
0., Inf, false,
|
||||
[Unit, Time], nothing),
|
||||
:prod_above
|
||||
=> UCVariable(:prod_above,
|
||||
"[gen, t]; production above minimum required level; p'_g(t)",
|
||||
0., Inf, false,
|
||||
[Unit, Time], nothing ),
|
||||
:max_power_avail
|
||||
=> UCVariable(:max_power_avail,
|
||||
"[gen, t]; maximum power available from generator gen at time t; bar{p}_g(t) = p_g(t) + r_g(t)",
|
||||
0., Inf, false,
|
||||
[Unit, Time], nothing),
|
||||
:max_power_avail_above
|
||||
=> UCVariable(:max_power_avail_above,
|
||||
"[gen, t]; maximum power available above minimum from generator gen at time t; bar{p}'_g(t)",
|
||||
0., Inf, false,
|
||||
[Unit, Time], nothing),
|
||||
:segprod
|
||||
=> UCVariable(:segprod,
|
||||
"[gen, seg, t]; how much generator gen produces on segment seg in time t; p_g^l(t)",
|
||||
0., Inf, false,
|
||||
[ [Unit, CostSegment], Time], nothing),
|
||||
:reserve
|
||||
=> UCVariable(:reserve,
|
||||
"[gen, t]; reserves provided by gen at t; r_g(t)",
|
||||
0., Inf, false,
|
||||
[Unit, Time], nothing),
|
||||
:reserve_shortfall
|
||||
=> UCVariable(:reserve_shortfall,
|
||||
"[t]; reserve shortfall at gen at t; s_R(t)",
|
||||
0., Inf, false,
|
||||
[Time], add_reserve_shortfall),
|
||||
:is_on
|
||||
=> UCVariable(:is_on,
|
||||
"[gen, t]; is gen on at t; u_g(t)",
|
||||
0., 1., true,
|
||||
[Unit, Time], nothing),
|
||||
:switch_on
|
||||
=> UCVariable(:switch_on,
|
||||
"[gen, t]; indicator that gen will be turned on at t; v_g(t)",
|
||||
0., 1., true,
|
||||
[Unit, Time], nothing),
|
||||
:switch_off
|
||||
=> UCVariable(:switch_off,
|
||||
"[gen, t]; indicator that gen will be turned off at t; w_g(t)",
|
||||
0., 1., true,
|
||||
[Unit, Time], nothing),
|
||||
:net_injection
|
||||
=> UCVariable(:net_injection,
|
||||
"[bus.name, t]",
|
||||
-1e100, Inf, false,
|
||||
[Bus, Time], nothing),
|
||||
:curtail
|
||||
=> UCVariable(:curtail,
|
||||
"[bus.name, t]; upper bound is max load at the bus at time t",
|
||||
0., Inf, false,
|
||||
[Bus, Time], nothing),
|
||||
:flow
|
||||
=> UCVariable(:flow,
|
||||
"[violation.monitored_line.name, t]",
|
||||
-1e100, Inf, false,
|
||||
[Violation, Time], nothing),
|
||||
:overflow
|
||||
=> UCVariable(:overflow,
|
||||
"[transmission_line.name, t]; how much flow above the transmission limits (in MW) is allowed",
|
||||
0., Inf, false,
|
||||
[TransmissionLine, Time], nothing),
|
||||
:loads
|
||||
=> UCVariable(:loads,
|
||||
"[price_sensitive_load.name, t]; production to meet demand at a set price, if it is economically sensible, independent of the rest of the demand; upper bound is demand at this price at time t",
|
||||
0., Inf, false,
|
||||
[PriceSensitiveLoad, Time], nothing),
|
||||
:startup
|
||||
=> UCVariable(:startup,
|
||||
"[gen, startup_category, t]; indicator that generator g starts up in startup_category at time t; 𝛿_g^s(t)",
|
||||
0., 1., true,
|
||||
[ [Unit, StartupCategory], Time], nothing),
|
||||
:downtime_arc
|
||||
=> UCVariable(:downtime_arc,
|
||||
"[gen, t, t']; indicator for shutdown at t and starting at t'",
|
||||
0., 1., true,
|
||||
[Unit, Time, Time], add_downtime_arcs),
|
||||
) # var_list
|
||||
|
||||
#var_symbol_list =
|
||||
# [
|
||||
# :prod_above, # [gen, t], ≥ 0
|
||||
# :segprod, # [gen, t, segment], ≥ 0
|
||||
# :reserve, # [gen, t], ≥ 0
|
||||
# :is_on, # [gen, t], binary
|
||||
# :switch_on, # [gen, t], binary
|
||||
# :switch_off, # [gen, t], binary
|
||||
# :net_injection, # [bus.name, t], urs?
|
||||
# :curtail, # [bus.name, t], domain [0, b.load[t]]
|
||||
# :overflow, # [transmission_line.name, t], ≥ 0
|
||||
# :loads, # [price_sensitive_load.name, t], domain [0, ps.demand[t]]
|
||||
# :startup # [gen, t, startup_category], binary
|
||||
# ]
|
||||
|
||||
|
||||
"""
|
||||
For a particular UCElement, which is the field in UnitCommitmentInstance that this corresponds to?
|
||||
This is used to determine indexing and ranges, e.g., `is_on` is indexed over Unit and Time,
|
||||
so the variable `is_on` will range in the first index from 1 to length(instance.units)
|
||||
and on the second index from 1 to instance.time.
|
||||
"""
|
||||
ind_to_field_dict = OrderedDict{Type{<:UCElement},Symbol}(
|
||||
Time => :time,
|
||||
Bus => :buses,
|
||||
Unit => :units,
|
||||
TransmissionLine => :lines,
|
||||
PriceSensitiveLoad => :price_sensitive_loads,
|
||||
CostSegment => :cost_segments,
|
||||
StartupCategory => :startup_categories,
|
||||
) # ind_to_field_dict
|
||||
|
||||
"""
|
||||
Take indices and convert them to fields of UnitCommitmentInstance.
|
||||
"""
|
||||
function ind_to_field(index::Union{Vector,Type{<:UCElement}}) :: Union{Vector,Symbol}
|
||||
if isa(index, Type{<:UCElement})
|
||||
return ind_to_field_dict[index]
|
||||
else
|
||||
return [ ind_to_field(t) for t in index ]
|
||||
end
|
||||
end # ind_to_field
|
||||
|
||||
function num_indices(v) :: Int64
|
||||
if !isa(v, Array)
|
||||
return 1
|
||||
else
|
||||
return sum(num_indices(v[i]) for i in 1:length(v))
|
||||
end
|
||||
end # num_indices
|
||||
|
||||
|
||||
"""
|
||||
Can return
|
||||
* UnitRange -> iterate over this range
|
||||
* Array{UnitRange} -> cross product of the ranges in the array
|
||||
* Tuple(UnitRange, Array{UnitRange}) -> the array length should be the same as the range of the UnitRange
|
||||
"""
|
||||
function get_indices_tuple(obj::Any, fields::Union{Symbol,Vector,Nothing} = nothing)
|
||||
if isa(fields, Symbol)
|
||||
return get_indices_tuple(getfield(obj,fields))
|
||||
end
|
||||
if fields == nothing || (isa(fields,Array) && length(fields) == 0)
|
||||
if isa(obj, Array)
|
||||
return UnitRange(1,length(obj))
|
||||
elseif isa(obj, Int)
|
||||
return UnitRange(1,obj)
|
||||
else
|
||||
return UnitRange{Int64}(0:-1)
|
||||
#return UnitRange(1,1)
|
||||
end
|
||||
end
|
||||
|
||||
if isa(obj,Array)
|
||||
indices = (
|
||||
UnitRange(1,length(obj)),
|
||||
([
|
||||
isa(f,Array) ? get_indices_tuple(getfield(x, f[1]), f[2:end]) : get_indices_tuple(getfield(x, f))
|
||||
for x in obj
|
||||
] for f in fields)...
|
||||
)
|
||||
# more_indices = ([
|
||||
# isa(f,Array) ? get_indices_tuple(getfield(x, f[1]), f[2:end]) : get_indices_tuple(getfield(x, f))
|
||||
# for x in obj
|
||||
# ] for f in fields
|
||||
# )
|
||||
# indices = (UnitRange(1,length(obj)),more_indices...)
|
||||
else
|
||||
indices = ()
|
||||
for f in fields
|
||||
if isa(f,Array)
|
||||
indices = (indices..., get_indices_tuple(getfield(obj, f[1]), f[2:end]))
|
||||
else
|
||||
indices = (indices..., get_indices_tuple(obj,f))
|
||||
end
|
||||
end
|
||||
# indices = (
|
||||
# isa(f,Array) ? get_indices_tuple(getfield(obj, f[1]), f[2:end]) : get_indices_tuple(getfield(obj, f))
|
||||
# for f in fields
|
||||
# )
|
||||
# (
|
||||
# isa(f,Array) ? get_indices_tuple(getfield(obj, f[1]), f[2:end]) : get_indices_tuple(getfield(obj, f))
|
||||
# for f in fields
|
||||
# )
|
||||
# indices = (indices...,)
|
||||
end # check if obj is Array or not
|
||||
|
||||
return indices
|
||||
end # get_indices_tuple
|
||||
|
||||
function loop_over_indices(indices::Any)
|
||||
loop = nothing
|
||||
should_print = false
|
||||
|
||||
if isa(indices, UnitRange)
|
||||
loop = indices
|
||||
elseif isa(indices, Array{UnitRange{Int64}}) || isa(indices, Tuple{Int, UnitRange})
|
||||
loop = Base.product(Tuple(indices)...)
|
||||
elseif isa(indices, Tuple{UnitRange, Array})
|
||||
loop = ()
|
||||
for t in zip(indices...)
|
||||
loop = (loop..., loop_over_indices(t)...)
|
||||
end
|
||||
elseif isa(indices,Tuple)
|
||||
loop = ()
|
||||
for i in indices
|
||||
loop = (loop..., loop_over_indices(i))
|
||||
end
|
||||
loop = Base.product(loop...)
|
||||
else
|
||||
error("Why are we here?")
|
||||
#loop = Base.product(loop_over_indices(indices)...)
|
||||
end
|
||||
|
||||
if should_print
|
||||
for i in loop
|
||||
@show i
|
||||
end
|
||||
end
|
||||
return loop
|
||||
end # loop_over_indices
|
||||
|
||||
|
||||
function expand_tuple(x::Tuple)
|
||||
y = ()
|
||||
for i in x
|
||||
if isa(i, Tuple)
|
||||
y = (y..., expand_tuple(i)...)
|
||||
else
|
||||
y = (y..., i)
|
||||
end
|
||||
end
|
||||
return y
|
||||
end # expand_tuple
|
||||
|
||||
|
||||
function expand_tuple(X::Array{<:Tuple})
|
||||
return [ expand_tuple(x) for x in X ]
|
||||
end # expand_tuple
|
||||
|
||||
|
||||
function get_indices(x::Array)
|
||||
return expand_tuple(x)
|
||||
end
|
||||
function get_indices(x::Base.Iterators.ProductIterator)
|
||||
return get_indices(collect(x))
|
||||
end
|
||||
|
||||
|
||||
"""
|
||||
Access `t.f`, special terminal case of `get_nested_field`.
|
||||
"""
|
||||
function get_nested_field(t::Any, f::Symbol)
|
||||
return getfield(t,f)
|
||||
end # get_nested_field
|
||||
|
||||
|
||||
"""
|
||||
Access `t.f`, where `f` could be a subfield.
|
||||
"""
|
||||
function get_nested_field(t::Any, f::Vector{Symbol})
|
||||
if length(f) > 1
|
||||
return get_nested_field(getfield(t,f[1]), f[2:end])
|
||||
else
|
||||
return getfield(t,f[1])
|
||||
end
|
||||
end # get_nested_field
|
||||
|
||||
|
||||
"""
|
||||
Given a set of indices of UCVariable, e.g., [[X,Y],T],
|
||||
and a UnitCommitmentInstance instance,
|
||||
if we want to access the field corresponding to Y,
|
||||
then we call get_nested_field(instance, [[X,Y],T], 2, (4,3)),
|
||||
which will return instance.X[4].Y[3] if Y is a vector,
|
||||
and just instance.X[4].Y otherwise.
|
||||
|
||||
===
|
||||
Termination Conditions
|
||||
|
||||
If `i` <= 0, then we only care about instance, and not the field.
|
||||
If `field` is a Symbol and `i` >= 1, then we want to explore instance.field (index t[i] or t).
|
||||
Note that if `i` >= 1, then `field` must be a symbol.
|
||||
If `i` == 1, then `t` can be an Int.
|
||||
|
||||
===
|
||||
Parameters
|
||||
* instance::Any --> all fields will be from instance, or nested fields of fields of instance.
|
||||
* field::Union{Vector,Symbol,Nothing} --> either the field we want to access, or a vector of fields, and we will want field[i].
|
||||
* i::Int --> which field to access.
|
||||
* t::Tuple --> how to go through the fields of instance to get the right field, length needs to be at least `i`.
|
||||
"""
|
||||
function get_nested_field(instance::Any, field::Union{Vector,Symbol,Nothing}, i::Int, t::Union{Tuple, Int})
|
||||
# Check data
|
||||
if isa(field, Vector)
|
||||
if i >= 2 && (!isa(t,Tuple) || length(t) < i)
|
||||
error("Tuple of indices to get nested field needs to be at least the length of the index we want to get.")
|
||||
end
|
||||
end
|
||||
|
||||
if isa(field, Symbol) || i <= 0
|
||||
# i = 0 can happen in the recursive call
|
||||
# What it means is that we do not want a field of the instance, but the instance itself
|
||||
# TODO handle other iterable types and empty arrays
|
||||
f = (isa(field, Symbol) && i >= 1) ? getfield(instance, field) : instance
|
||||
if isa(f,Vector)
|
||||
if length(f) == 0
|
||||
error("Trying to iterate over empty field!")
|
||||
else
|
||||
return isa(t,Int) ? f[t] : f[t[i]]
|
||||
end
|
||||
else
|
||||
return f
|
||||
end
|
||||
end # check termination conditions (f is field or i <= 0)
|
||||
|
||||
# Loop over the fields until we find where index i is located
|
||||
# It may be nested inside an array, so that is why we recurse
|
||||
start_ind = 0
|
||||
for f in field
|
||||
curr_len = isa(f, Vector) ? length(f) : 1
|
||||
if start_ind + curr_len >= i
|
||||
if isa(f, Vector)
|
||||
new_field_is_iterable = isa(getfield(instance, f[1]), Vector)
|
||||
if new_field_is_iterable
|
||||
return get_nested_field(getfield(instance, f[1])[t[start_ind+1]], f[2:end], i - start_ind - 1, isa(t,Tuple) ? t[start_ind+2:end] : t)
|
||||
else
|
||||
return get_nested_field(getfield(instance, f[1]), f[2:end], i - start_ind - 1, isa(t,Tuple) ? t[start_ind+2:end] : t)
|
||||
end
|
||||
else
|
||||
# f is hopefully a symbol...
|
||||
return get_nested_field(instance, f, 1, isa(t,Tuple) ? t[start_ind+1] : t)
|
||||
end
|
||||
end
|
||||
start_ind += curr_len
|
||||
end
|
||||
|
||||
return nothing
|
||||
end # get_nested_field
|
||||
|
||||
|
||||
#"""
|
||||
#Get ranges for the indices of a UCVariable along dimension `i`,
|
||||
#making sure that the right fields ranges are calculated via `get_nested_field` and `ind_to_field`.
|
||||
#"""
|
||||
#function get_range(arr::UCVariable, instance::UnitCommitmentInstance, i::Int) :: UnitRange
|
||||
# arr = ind_to_field[var.indices[i]]
|
||||
# f = get_nested_field(instance, arr)
|
||||
# if isa(f, Array)
|
||||
# return 1:length(f)
|
||||
# elseif isa(f, Int)
|
||||
# return 1:f
|
||||
# else
|
||||
# error("Unknown type to generate UnitRange from: ", typeof(f))
|
||||
# end
|
||||
#end # get_range
|
||||
|
||||
export UCVariable
|
||||
@@ -2,31 +2,55 @@
|
||||
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
|
||||
using UnitCommitment, LinearAlgebra, Cbc, JuMP
|
||||
using UnitCommitment, LinearAlgebra, JuMP
|
||||
USE_GUROBI = (Base.find_package("Gurobi") != nothing)
|
||||
USE_CBC = !USE_GUROBI
|
||||
if USE_GUROBI
|
||||
using Gurobi
|
||||
else
|
||||
using Cbc
|
||||
end
|
||||
|
||||
NUM_THREADS = 4
|
||||
LOG_LEVEL = 1
|
||||
|
||||
@testset "Model" begin
|
||||
@testset "Run" begin
|
||||
instance = UnitCommitment.read_benchmark("test/case14")
|
||||
#instance = UnitCommitment.read_benchmark("matpower/case3375wp/2017-02-01")
|
||||
#instance = UnitCommitment.read_benchmark("matpower/case1888rte/2017-02-01")
|
||||
for line in instance.lines, t in 1:4
|
||||
line.normal_flow_limit[t] = 10.0
|
||||
end
|
||||
optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)
|
||||
model = build_model(instance=instance,
|
||||
optimizer=optimizer,
|
||||
variable_names=true)
|
||||
#for formulation in [UnitCommitment.DefaultFormulation, UnitCommitment.TightFormulation]
|
||||
for formulation in [UnitCommitment.TightFormulation]
|
||||
@info string("Running test of ", formulation)
|
||||
if USE_CBC
|
||||
optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => LOG_LEVEL)
|
||||
end
|
||||
if USE_GUROBI
|
||||
optimizer = optimizer_with_attributes(Gurobi.Optimizer, "Threads" => NUM_THREADS)
|
||||
end
|
||||
model = build_model(instance=instance,
|
||||
optimizer=optimizer,
|
||||
variable_names=true,
|
||||
formulation=formulation)
|
||||
|
||||
JuMP.write_to_file(model.mip, "test.mps")
|
||||
JuMP.write_to_file(model.mip, "test.mps")
|
||||
|
||||
# Optimize and retrieve solution
|
||||
UnitCommitment.optimize!(model)
|
||||
solution = get_solution(model)
|
||||
|
||||
# Verify solution
|
||||
@test UnitCommitment.validate(instance, solution)
|
||||
# Optimize and retrieve solution
|
||||
UnitCommitment.optimize!(model)
|
||||
solution = get_solution(model)
|
||||
|
||||
# Verify solution
|
||||
@test UnitCommitment.validate(instance, solution)
|
||||
|
||||
# Reoptimize with fixed solution
|
||||
UnitCommitment.fix!(model, solution)
|
||||
UnitCommitment.optimize!(model)
|
||||
@test UnitCommitment.validate(instance, solution)
|
||||
end
|
||||
end
|
||||
# Reoptimize with fixed solution
|
||||
UnitCommitment.fix!(model, solution)
|
||||
UnitCommitment.optimize!(model)
|
||||
@test UnitCommitment.validate(instance, solution)
|
||||
|
||||
#@show solution
|
||||
end # loop over components
|
||||
end # end testset Run
|
||||
end # end test
|
||||
|
||||
Reference in New Issue
Block a user