Skip to content

Commit b453355

Browse files
committed
update examples
1 parent acb658a commit b453355

35 files changed

Lines changed: 485 additions & 482 deletions

Manifest.toml

Lines changed: 249 additions & 137 deletions
Large diffs are not rendered by default.

Project.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ KaHyPar = "2a6221f6-aa48-11e9-3542-2d9e0ef01880"
99
LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d"
1010
MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195"
1111
MPIClusterManagers = "e7922434-ae4b-11e9-05c5-9780451d2c66"
12+
Measures = "442fdcdd-2543-5da2-b0f3-8c86c306513e"
13+
PipsNLP = "0e2b999c-8a3a-11ea-3c4b-c36350f324a0"
1214
Plasmo = "d3f7391f-f14a-50cc-bbe4-76a32d1bad3c"
15+
PlasmoPlots = "a21664a3-f668-4e98-8a13-7892286ec16a"
1316
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
1417
PowerModels = "c36e90e8-916a-50a6-bd94-075b64ef4655"
18+
PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee"
1519
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
1620
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

README.md

Lines changed: 12 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# PlasmoExamples
2-
Example scripts that showcase the core functionality of [Plasmo.jl](https://github.com/zavalab/Plasmo.jl). This repository shows how to:
3-
* Use optigraphs to create and solve hierarchical optimization models
4-
* Solve a natural gas optimal control problem with [PipsSolver.jl](https://github.com/zavalab/PipsSolver.jl),
5-
* Solve a DC optimal power flow problem with [SchwarzSolver.jl](https://github.com/zavalab/SchwarzSolver.jl)
2+
Example scripts that showcase using [Plasmo.jl](https://github.com/zavalab/Plasmo.jl). This repository shows how to:
3+
* Use `OptiGraphs` to model and solve optimization models
4+
* Solve a natural gas optimal control problem with [PipsNLP.jl](https://github.com/zavalab/PipsNLP.jl) using partitioning to obtain subproblems
5+
* Solve a DC optimal power flow problem with [SchwarzOpt.jl](https://github.com/zavalab/SchwarzOpt.jl) using overlapping schwarz decomposition
66

77

88
## Requirements
9-
* Julia 1.0.5
9+
* Julia 1.3 or later
1010
* The GR plotting backend to create plots for the example scripts.
1111
* PIPS-NLP with the ma57 linear solver. Use the [PIPS-NLP](https://github.com/Argonne-National-Laboratory/PIPS) installation instructions.
1212
* A working MPI installation for PIPS-NLP. The scripts in this repository have been tested with [mpich](https://www.mpich.org/downloads/) version 3.3.2
@@ -20,7 +20,7 @@ The Julia packages needed to run the examples and case studies can be installed
2020
To setup the necessary Julia environment, first clone the PlasmoExamples directory with:
2121

2222
```
23-
git clone https://github.com/jalving/PlasmoExamples.git
23+
git clone https://github.com/zavalab/PlasmoExamples.git
2424
```
2525

2626
Once cloned, navigate to the `PlasmoExamples` folder and begin a Julia REPL session. The necessary Julia dependencies can then be downloaded as follows:
@@ -78,11 +78,9 @@ $ julia run_examples.jl
7878
This will run the 5 example scripts in the examples folder and create corresponding plots in `PlasmoExamples/examples/figures`. The example scripts perform the following:
7979

8080
* example1.jl creates a simple optigraph, solves it with GLPK, and plots the optigraph structure.
81-
* example2.jl creates a hierarchical optigraph with a shared optiedge (linkconstraint), solves it with GLPK, and plots the optigraph structure.
82-
* example3.jl creates a hierarchical optigraph with a shared optinode, solves it with GLPK, and plots the optigraph structure.
83-
* example4.jl creates an optigraph for an optimal control model, partitions it with KaHyPar, and aggregates the resulting subgraphs into new optinodes. This example
84-
also creates plots for the optigraph structure corresponding to the original optigraph, the partitioned optigraph, and the aggregated optigraph.
85-
* example5.jl builds on example4.jl and creates overlapping subgraphs. It also plots the overlapping graph structures.
81+
* example2.jl creates a hierarchical optigraph with a shared optiedge and optinode (i.e. it contains linkconstraints), solves it with GLPK, and plots the optigraph structure.
82+
* example3.jl creates an optigraph for an optimal control model and produces the plot.
83+
* example4.jl builds on example3.jl and partitions the control problem with KaHyPar, aggregates the resulting subgraphs into new optinodes, and also shows how to create overlapping subgraphs. It also plots the resulting graph structures.
8684

8785

8886
## Running Case Study 1
@@ -112,10 +110,10 @@ julia> include("run_casestudy1.jl")
112110
This will run the case study just like before. However, it is now possible to modify partitioning parameters (`n_parts`, `n_processes`, and `max_imbalance`) in the `run_casestudy1.jl` script
113111
(by modifying the script with any text editor) without restarting Julia. It is also possible to scale the model size by modifying `nx` (the number of space discretization points for each pipeline).
114112
After modifying the script, just run the `include("run_casestudy1.jl")` statement again.
115-
We recommend always keeping `n_parts` the same as `n_processes`. We have observed some issues with `PipsSolver.jl` and non-uniform sub-problem allocations.
113+
We recommend always keeping `n_parts` the same as `n_processes`. We have observed some issues with `PipsNLP.jl` and non-uniform sub-problem allocations.
116114

117115
## Running Case Study 2
118-
Case study 2 solves a DC optimal power flow problem with `SchwarzSolver.jl`. The solver can take advantage of multiple threads set with the
116+
Case study 2 solves a DC optimal power flow problem with `SchwarzOpt.jl`. The solver can take advantage of multiple threads set with the
119117
`JULIA_NUM_THREADS` environment variable. Since the case study partitions the problem into 4 partitions, it can benefit from up to 4 threads which solve
120118
the sub-problems in parallel. The following commands can be used to set the number of threads and execute the case study:
121119

@@ -146,25 +144,4 @@ formulated (small partitions for a network can lead to feasibility issues).
146144

147145
## Other documentation
148146
Documentation describing Plasmo.jl and its underlying functions can be found at the [github pages](https://zavalab.github.io/Plasmo.jl/dev/) site.
149-
The source code for v0.3.0 can be found at: [https://github.com/zavalab/Plasmo.jl/tree/v0.3.0](https://github.com/zavalab/Plasmo.jl/tree/v0.3.0).
150-
151-
### Overview of source code files
152-
The Plasmo.jl source code for version 0.3.0 is defined by modules located at [https://github.com/zavalab/Plasmo.jl/tree/v0.3.0/src](https://github.com/zavalab/Plasmo.jl/tree/v0.3.0/src).
153-
Here is a brief overview of the modules:
154-
155-
* hypergraphs/hypergraph.jl - defines the `HyperGraph` object used to create a hypergraph representation of an optigraph. Notably, it extends a `LightGraphs.AbstractGraph`, so common lightgraphs
156-
operations work with the `HyperGraph` object (such as `neighbors` and `incidence_matrix`).
157-
* extras/kahypar.jl - defines a simple conveniece wrapper for the KaHyPar package if it is loaded. Makes it easier to directly partition an optigraph using KaHyPar.
158-
* extras/plots.jl - defines special plots for Plasmo.jl if the `Plots.jl` package is loaded. Extends the `Plots.plot` and `Plots.spy` functions for the `OptiGraph` object.
159-
* combine.jl - contains functions to aggregate an optigraph into an optinode, or to aggregate optigraph subgraphs into optinodes.
160-
* copy.jl - contains functions to copy objective and constraint expressions. Used extensively for the aggregate functions defined in combine.jl.
161-
* graph_functions.jl - defines functions to perform graph operations on an optigraph such as querying the neighborhood around a set of nodes or performing subgraph expansion. Uses
162-
hypergraph.jl to perform said operations.
163-
* graph_interface.jl - currently only contains a function to generate a `HyperGraph` object from an `OptiGraph` object. Eventually other graph representations will be available.
164-
* macros.jl - contains the Julia macros used to model with optigraphs.
165-
* optiedge.jl - defines the `OptiEdge` and `LinkConstraint` objects, as well as functions that work on these objects.
166-
* optigraph.jl - defines the `OptiGraph` objects, as well as functions that work on an optigraph. Notably, an `OptiGraph` extends a `JuMP.AbstractModel` so JuMP macros work with optigraphs.
167-
* optinode.jl - defines the `OptiNode` object, as well as functions that work on optinodes. The optinode also extends a `JuMP.AbstractModel`.
168-
* partition.jl - defines the `Partition` object which informs how to partition an optigraph. The `Partition` object is used in the `make_subgraphs!` function to create subgraphs in an optigraph.
169-
* solve.jl - extends JuMP.jl functions to solve an optigraph using JuMP compatible solvers.
170-
* Plasmo.jl - imports and exports functions and defines abstract types.
147+
The source code for v0.3.0 can be found at: [https://github.com/zavalab/Plasmo.jl/tree/v0.3.0](https://github.com/zavalab/Plasmo.jl/tree/v0.4.0).

case_studies/gasnetwork/gasnetwork.jl

Lines changed: 50 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,63 +6,66 @@ using JLD2
66
horizon = 24*3600 #the time horizon is in seconds
77
nt = 24 #number of time points
88
dt = horizon / (nt - 1) #time delta
9-
#nx = 3
9+
#nx = 10
1010

1111
include((@__DIR__)*"/modelfunctions.jl")
1212
include((@__DIR__)*"/load_data.jl")
1313

14-
gas_network = OptiGraph()
15-
for pipe in pipelines
16-
add_subgraph!(gas_network,pipe)
17-
end
18-
for compressor in compressors
19-
add_subgraph!(gas_network,compressor)
20-
end
21-
for junction in junctions
22-
add_subgraph!(gas_network,junction)
23-
end
14+
function create_gas_network_problem()
15+
gas_network = OptiGraph()
16+
for pipe in pipelines
17+
add_subgraph!(gas_network,pipe)
18+
end
19+
for compressor in compressors
20+
add_subgraph!(gas_network,compressor)
21+
end
22+
for junction in junctions
23+
add_subgraph!(gas_network,junction)
24+
end
2425

25-
for pipe in pipelines
26-
junction_from,junction_to = pipe_map[pipe]
27-
@linkconstraint(gas_network,[t = 1:nt],pipe[:pin][t] == junction_from[:time_nodes][t][:pressure])
28-
@linkconstraint(gas_network,[t = 1:nt],pipe[:pout][t] == junction_to[:time_nodes][t][:pressure])
29-
end
26+
for pipe in pipelines
27+
junction_from,junction_to = pipe_map[pipe]
28+
@linkconstraint(gas_network,[t = 1:nt],pipe[:pin][t] == junction_from[:time_nodes][t][:pressure])
29+
@linkconstraint(gas_network,[t = 1:nt],pipe[:pout][t] == junction_to[:time_nodes][t][:pressure])
30+
end
3031

31-
for compressor in compressors
32-
junction_from,junction_to = compressor_map[compressor]
33-
nodes = compressor[:time_nodes]
34-
@linkconstraint(gas_network,[t = 1:nt],nodes[t][:psuction] == junction_from[:time_nodes][t][:pressure])
35-
@linkconstraint(gas_network,[t = 1:nt],nodes[t][:pdischarge] == junction_to[:time_nodes][t][:pressure])
36-
end
32+
for compressor in compressors
33+
junction_from,junction_to = compressor_map[compressor]
34+
nodes = compressor[:time_nodes]
35+
@linkconstraint(gas_network,[t = 1:nt],nodes[t][:psuction] == junction_from[:time_nodes][t][:pressure])
36+
@linkconstraint(gas_network,[t = 1:nt],nodes[t][:pdischarge] == junction_to[:time_nodes][t][:pressure])
37+
end
3738

38-
for junction in junctions
39-
devices_in = junction_map_in[junction]
40-
devices_out = junction_map_out[junction]
39+
for junction in junctions
40+
devices_in = junction_map_in[junction]
41+
devices_out = junction_map_out[junction]
4142

42-
if length(devices_in) > 0
43-
flow_in = [sum(devices_in[i][:fout][t] for i = 1:length(devices_in)) for t = 1:nt]
44-
else
45-
flow_in = zeros(nt)
46-
end
47-
if length(devices_out) > 0
48-
flow_out = [sum(devices_out[i][:fin][t] for i = 1:length(devices_out)) for t = 1:nt]
49-
else
50-
flow_out = zeros(nt)
51-
end
52-
total_supplied = [junction[:time_nodes][t][:total_supplied] for t = 1:nt]
53-
total_delivered = [junction[:time_nodes][t][:total_delivered] for t = 1:nt]
43+
if length(devices_in) > 0
44+
flow_in = [sum(devices_in[i][:fout][t] for i = 1:length(devices_in)) for t = 1:nt]
45+
else
46+
flow_in = zeros(nt)
47+
end
48+
if length(devices_out) > 0
49+
flow_out = [sum(devices_out[i][:fin][t] for i = 1:length(devices_out)) for t = 1:nt]
50+
else
51+
flow_out = zeros(nt)
52+
end
53+
total_supplied = [junction[:time_nodes][t][:total_supplied] for t = 1:nt]
54+
total_delivered = [junction[:time_nodes][t][:total_delivered] for t = 1:nt]
5455

55-
@linkconstraint(gas_network,[t = 1:nt], flow_in[t] - flow_out[t] + total_supplied[t] - total_delivered[t] == 0)
56-
end
56+
@linkconstraint(gas_network,[t = 1:nt], flow_in[t] - flow_out[t] + total_supplied[t] - total_delivered[t] == 0)
57+
end
5758

5859

59-
#Fix Demands
60-
for (i,j_data) in junction_data
61-
jmodel = jmap[i]
62-
dvalues = j_data[:demand_values]
63-
n_demands = length(dvalues)
64-
nodes = jmodel[:time_nodes]
65-
for (t,node) in enumerate(nodes)
66-
@constraint(node,[d = 1:n_demands],node[:fdemand][d] == dvalues[d][t])
60+
#Fix Demands
61+
for (i,j_data) in junction_data
62+
jmodel = jmap[i]
63+
dvalues = j_data[:demand_values]
64+
n_demands = length(dvalues)
65+
nodes = jmodel[:time_nodes]
66+
for (t,node) in enumerate(nodes)
67+
@constraint(node,[d = 1:n_demands],node[:fdemand][d] == dvalues[d][t])
68+
end
6769
end
70+
return gas_network
6871
end

case_studies/gasnetwork/modelfunctions.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ function create_pipeline_model(data,nt,nx)
6060
@variable(node, 1 <= px <= 100,start = 10)
6161
@variable(node, 0 <= fx <= 100, start = 10)
6262
@variable(node,slack >= 0, start = 1)
63-
@NLnodeconstraint(node, slack*px - c3*fx*fx == 0)
63+
@NLconstraint(node, slack*px - c3*fx*fx == 0)
6464
end
6565

6666
#Setup boundary variables
@@ -95,7 +95,7 @@ function create_compressor_model(data,nt)
9595
@variable(node, flow >= 0, start = 10)
9696
@variable(node, 1 <= boost <= 30,start = 1)
9797
@constraint(node, pdischarge == psuction + boost)
98-
@NLnodeconstraint(node, power == c4*flow*((pdischarge/psuction)^om-1) )
98+
@NLconstraint(node, power == c4*flow*((pdischarge/psuction)^om-1) )
9999
@objective(node, Min, 0.1*power*(dt/3600.0))
100100
end
101101
@expression(graph,fin[t=1:nt],time_nodes[t][:flow])
Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
#create gasnetwork optigraph model
2-
include((@__DIR__)*"/gasnetwork.jl")
3-
41
#Obtain a hyergraph representation of the gas_network
5-
hypergraph,hyper_map = gethypergraph(gas_network)
2+
hypergraph,hyper_map = hyper_graph(gas_network)
63

74
#Setup node and edge weights
8-
n_vertices = length(vertices(hypergraph))
95
node_sizes = [num_variables(node) for node in all_nodes(gas_network)]
106
edge_weights = [num_linkconstraints(edge) for edge in all_edges(gas_network)]
117

@@ -14,19 +10,18 @@ node_vector = KaHyPar.partition(hypergraph,n_parts,configuration = :edge_cut,
1410
imbalance = max_imbalance, node_sizes = node_sizes, edge_weights = edge_weights)
1511

1612
#Create an optigraph partition
17-
partition = Partition(gas_network,node_vector,hyper_map)
13+
partition = Partition(node_vector,hyper_map)
1814

1915
#Setup subgraphs based on partition
20-
make_subgraphs!(gas_network,partition)
16+
apply_partition!(gas_network,partition)
2117

22-
#Combine the subgraphs into model-nodes
18+
#aggregate the subgraphs into optinodes
2319
combined_graph , combine_map = aggregate(gas_network,0)
2420

2521
#scale the objective function on each optinode
2622
for node in all_nodes(combined_graph)
2723
@objective(node,Min,1e-6*objective_function(node))
2824
end
29-
3025
##############################
3126
# Solve with PIPS-NLP
3227
##############################
@@ -35,10 +30,11 @@ julia_workers = sort(collect(values(manager.mpi2j)))
3530

3631
#Distribute the optigraph among the workers
3732
#Here, we create the variable `pipsgraph` on each worker
38-
remote_references = PipsSolver.distribute(combined_graph,julia_workers,remote_name = :pipsgraph)
33+
remote_references = PipsNLP.distribute_optigraph(combined_graph,julia_workers,remote_name = :pipsgraph)
3934

35+
#NOTE: PIPS hits restoration phase; needs better initialization
4036
#Solve with PIPS-NLP on each mpi rank
4137
@mpi_do manager begin
4238
using MPI
43-
PipsSolver.pipsnlp_solve(pipsgraph)
39+
PipsNLP.pipsnlp_solve(pipsgraph)
4440
end

examples/create_dynamic_problem.jl

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using Plasmo
2+
3+
function create_dynamic_problem()
4+
T = 100 #number of time points
5+
d = sin.(1:T) #disturbance vector
6+
7+
graph = OptiGraph()
8+
@optinode(graph,state[1:T])
9+
@optinode(graph,control[1:T-1])
10+
11+
for node in state
12+
@variable(node,x)
13+
@constraint(node, x >= 0)
14+
@objective(node,Min,x^2)
15+
end
16+
for node in control
17+
@variable(node,u)
18+
@constraint(node, u >= -1000)
19+
@objective(node,Min,u^2)
20+
end
21+
22+
@linkconstraint(graph,[i = 1:T-1],state[i+1][:x] == state[i][:x] + control[i][:u] + d[i])
23+
n1 = state[1]
24+
@constraint(n1,n1[:x] == 0)
25+
return graph
26+
end

examples/create_optigraph.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
function simple_optigraph()
2+
graph = OptiGraph()
3+
4+
@optinode(graph,n1)
5+
@variable(n1, y >= 2)
6+
@variable(n1,x >= 0)
7+
@constraint(n1,x + y >= 3)
8+
@objective(n1, Min, y)
9+
10+
@optinode(graph,n2)
11+
@variable(n2, y)
12+
@variable(n2,x >= 0)
13+
@constraint(n2,x + y >= 3)
14+
@objective(n2, Min, y)
15+
16+
@optinode(graph,n3)
17+
@variable(n3, y )
18+
@variable(n3,x >= 0)
19+
@constraint(n3,x + y >= 3)
20+
@objective(n3, Min, y)
21+
22+
#Create a link constraint linking the 3 models
23+
@linkconstraint(graph, n1[:x] + n2[:x] + n3[:x] == 3)
24+
return graph
25+
end

examples/example1.jl

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,42 @@
11
using Plasmo
2-
using Plots
3-
using PlasmoPlots
2+
using Plots, PlasmoPlots
43
using GLPK
54

6-
graph1 = OptiGraph()
5+
graph = OptiGraph()
76

8-
@optinode(graph1,n1)
7+
@optinode(graph,n1)
98
@variable(n1, y >= 2)
109
@variable(n1,x >= 0)
1110
@constraint(n1,x + y >= 3)
1211
@objective(n1, Min, y)
1312

14-
@optinode(graph1,n2)
13+
@optinode(graph,n2)
1514
@variable(n2, y)
1615
@variable(n2,x >= 0)
1716
@constraint(n2,x + y >= 3)
1817
@objective(n2, Min, y)
1918

20-
@optinode(graph1,n3)
19+
@optinode(graph,n3)
2120
@variable(n3, y )
2221
@variable(n3,x >= 0)
2322
@constraint(n3,x + y >= 3)
2423
@objective(n3, Min, y)
2524

2625
#Create a link constraint linking the 3 models
27-
@linkconstraint(graph1, n1[:x] + n2[:x] + n3[:x] == 3)
28-
set_optimizer(graph1,GLPK.Optimizer)
29-
optimize!(graph1)
26+
@linkconstraint(graph, n1[:x] + n2[:x] + n3[:x] == 3)
27+
set_optimizer(graph,GLPK.Optimizer)
28+
optimize!(graph)
3029

3130
#Query Solution
3231
println("n1[:x] = ",value(n1[:x]))
3332
println("n2[:x] = ",value(n2[:x]))
3433
println("n3[:x] = ",value(n3[:x]))
35-
println("objective value = ",objective_value(graph1))
34+
println("objective value = ",objective_value(graph))
3635

37-
38-
plt_graph1 = Plots.plot(graph1,node_labels = true,markersize = 60,labelsize = 30,
36+
plt_graph = PlasmoPlots.layout_plot(graph,node_labels = true,markersize = 60,labelsize = 30,
3937
linewidth = 4,layout_options = Dict(:tol => 0.01,:iterations => 2));
40-
plt_matrix1 = Plots.spy(graph1,node_labels = true,markersize = 30);
38+
plt_matrix = PlasmoPlots.matrix_plot(graph,node_labels = true,markersize = 40,labelsize = 32);
39+
40+
Plots.savefig(plt_graph,"figures/example1_layout.pdf")
41+
42+
Plots.savefig(plt_matrix,"figures/example1_matrix.pdf")

0 commit comments

Comments
 (0)