You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
70 lines
15 KiB
70 lines
15 KiB
<!DOCTYPE html>
|
|
<html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0"/><title>Decomposition methods · UnitCommitment.jl</title><meta name="title" content="Decomposition methods · UnitCommitment.jl"/><meta property="og:title" content="Decomposition methods · UnitCommitment.jl"/><meta property="twitter:title" content="Decomposition methods · UnitCommitment.jl"/><meta name="description" content="Documentation for UnitCommitment.jl."/><meta property="og:description" content="Documentation for UnitCommitment.jl."/><meta property="twitter:description" content="Documentation for UnitCommitment.jl."/><script data-outdated-warner src="../../assets/warner.js"></script><link href="https://cdnjs.cloudflare.com/ajax/libs/lato-font/3.0.0/css/lato-font.min.css" rel="stylesheet" type="text/css"/><link href="https://cdnjs.cloudflare.com/ajax/libs/juliamono/0.050/juliamono.min.css" rel="stylesheet" type="text/css"/><link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/fontawesome.min.css" rel="stylesheet" type="text/css"/><link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/solid.min.css" rel="stylesheet" type="text/css"/><link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/brands.min.css" rel="stylesheet" type="text/css"/><link href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/katex.min.css" rel="stylesheet" type="text/css"/><script>documenterBaseURL="../.."</script><script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js" data-main="../../assets/documenter.js"></script><script src="../../search_index.js"></script><script src="../../siteinfo.js"></script><script src="../../../versions.js"></script><link class="docs-theme-link" rel="stylesheet" type="text/css" href="../../assets/themes/documenter-dark.css" data-theme-name="documenter-dark" data-theme-primary-dark/><link class="docs-theme-link" rel="stylesheet" type="text/css" href="../../assets/themes/documenter-light.css" data-theme-name="documenter-light" data-theme-primary/><script src="../../assets/themeswap.js"></script><link href="../../assets/custom.css" rel="stylesheet" type="text/css"/></head><body><div id="documenter"><nav class="docs-sidebar"><div class="docs-package-name"><span class="docs-autofit"><a href="../../">UnitCommitment.jl</a></span></div><button class="docs-search-query input is-rounded is-small is-clickable my-2 mx-auto py-1 px-2" id="documenter-search-query">Search docs (Ctrl + /)</button><ul class="docs-menu"><li><a class="tocitem" href="../../">Home</a></li><li><span class="tocitem">Tutorials</span><ul><li><a class="tocitem" href="../usage/">Getting started</a></li><li><a class="tocitem" href="../customizing/">Model customization</a></li><li><a class="tocitem" href="../lmp/">Locational Marginal Prices</a></li><li><a class="tocitem" href="../market/">Market Clearing</a></li><li class="is-active"><a class="tocitem" href>Decomposition methods</a><ul class="internal"><li><a class="tocitem" href="#1.-Time-decomposition"><span>1. Time decomposition</span></a></li><li><a class="tocitem" href="#2.-Scenario-decomposition-with-Progressive-Hedging"><span>2. Scenario decomposition with Progressive Hedging</span></a></li></ul></li></ul></li><li><span class="tocitem">User guide</span><ul><li><a class="tocitem" href="../../guides/problem/">Problem definition</a></li><li><a class="tocitem" href="../../guides/format/">JSON data format</a></li><li><a class="tocitem" href="../../guides/instances/">Benchmark instances</a></li></ul></li><li><a class="tocitem" href="../../api/">API Reference</a></li></ul><div class="docs-version-selector field has-addons"><div class="control"><span class="docs-label button is-static is-size-7">Version</span></div><div class="docs-selector control is-expanded"><div class="select is-fullwidth is-size-7"><select id="documenter-version-selector"></select></div></div></div></nav><div class="docs-main"><header class="docs-navbar"><a class="docs-sidebar-button docs-navbar-link fa-solid fa-bars is-hidden-desktop" id="documenter-sidebar-button" href="#"></a><nav class="breadcrumb"><ul class="is-hidden-mobile"><li><a class="is-disabled">Tutorials</a></li><li class="is-active"><a href>Decomposition methods</a></li></ul><ul class="is-hidden-tablet"><li class="is-active"><a href>Decomposition methods</a></li></ul></nav><div class="docs-right"><a class="docs-navbar-link" href="https://github.com/ANL-CEEESA/UnitCommitment.jl" title="View the repository on GitHub"><span class="docs-icon fa-brands"></span><span class="docs-label is-hidden-touch">GitHub</span></a><a class="docs-navbar-link" href="https://github.com/ANL-CEEESA/UnitCommitment.jl/blob/dev/docs/src/tutorials/decomposition.md" title="Edit source on GitHub"><span class="docs-icon fa-solid"></span></a><a class="docs-settings-button docs-navbar-link fa-solid fa-gear" id="documenter-settings-button" href="#" title="Settings"></a><a class="docs-article-toggle-button fa-solid fa-chevron-up" id="documenter-article-toggle-button" href="javascript:;" title="Collapse all docstrings"></a></div></header><article class="content" id="documenter-page"><h1 id="Decomposition-methods"><a class="docs-heading-anchor" href="#Decomposition-methods">Decomposition methods</a><a id="Decomposition-methods-1"></a><a class="docs-heading-anchor-permalink" href="#Decomposition-methods" title="Permalink"></a></h1><h2 id="1.-Time-decomposition"><a class="docs-heading-anchor" href="#1.-Time-decomposition">1. Time decomposition</a><a id="1.-Time-decomposition-1"></a><a class="docs-heading-anchor-permalink" href="#1.-Time-decomposition" title="Permalink"></a></h2><p>Solving unit commitment instances that have long time horizons (for example, year-long 8760-hour instances) requires a substantial amount of computational power. To address this issue, UC.jl offers a time decomposition method, which breaks the instance down into multiple overlapping subproblems, solves them sequentially, then reassembles the solution.</p><p>When solving a unit commitment instance with a dense time slot structure, computational complexity can become a significant challenge. For instance, if the instance contains hourly data for an entire year (8760 hours), solving such a model can require a substantial amount of computational power. To address this issue, UC.jl provides a time_decomposition method within the <code>optimize!</code> function. This method decomposes the problem into multiple sub-problems, solving them sequentially.</p><p>The <code>optimize!</code> function takes 5 parameters: a unit commitment instance, a <code>TimeDecomposition</code> method, an optimizer, and two optional functions <code>after_build</code> and <code>after_optimize</code>. It returns a solution dictionary. The <code>TimeDecomposition</code> method itself requires four arguments: <code>time_window</code>, <code>time_increment</code>, <code>inner_method</code> (optional), and <code>formulation</code> (optional). These arguments define the time window for each sub-problem, the time increment to move to the next sub-problem, the method used to solve each sub-problem, and the formulation employed, respectively. The two functions, namely <code>after_build</code> and <code>after_optimize</code>, are invoked subsequent to the construction and optimization of each sub-model, respectively. It is imperative that the <code>after_build</code> function requires its two arguments to be consistently mapped to <code>model</code> and <code>instance</code>, while the <code>after_optimize</code> function necessitates its three arguments to be consistently mapped to <code>solution</code>, <code>model</code>, and <code>instance</code>.</p><p>The code snippet below illustrates an example of solving an instance by decomposing the model into multiple 36-hour sub-problems using the <code>XavQiuWanThi2019</code> method. Each sub-problem advances 24 hours at a time. The first sub-problem covers time steps 1 to 36, the second covers time steps 25 to 60, the third covers time steps 49 to 84, and so on. The initial power levels and statuses of the second and subsequent sub-problems are set based on the results of the first 24 hours from each of their immediate prior sub-problems. In essence, this approach addresses the complexity of solving a large problem by tackling it in 24-hour intervals, while incorporating an additional 12-hour buffer to mitigate the closing window effect for each sub-problem. Furthermore, the <code>after_build</code> function imposes the restriction that <code>g3</code> and <code>g4</code> cannot be activated simultaneously during the initial time slot of each sub-problem. On the other hand, the <code>after_optimize</code> function is invoked to calculate the conventional Locational Marginal Prices (LMPs) for each sub-problem, and subsequently appends the computed values to the <code>lmps</code> vector.</p><blockquote><p><strong>Warning</strong> Specifying <code>TimeDecomposition</code> as the value of the <code>inner_method</code> field of another <code>TimeDecomposition</code> causes errors when calling the <code>optimize!</code> function due to the different argument structures between the two <code>optimize!</code> functions.</p></blockquote><pre><code class="language-julia hljs">using UnitCommitment, JuMP, Cbc, HiGHS
|
|
|
|
import UnitCommitment:
|
|
TimeDecomposition,
|
|
ConventionalLMP,
|
|
XavQiuWanThi2019,
|
|
Formulation
|
|
|
|
# specifying the after_build and after_optimize functions
|
|
function after_build(model, instance)
|
|
@constraint(
|
|
model,
|
|
model[:is_on]["g3", 1] + model[:is_on]["g4", 1] <= 1,
|
|
)
|
|
end
|
|
|
|
lmps = []
|
|
function after_optimize(solution, model, instance)
|
|
lmp = UnitCommitment.compute_lmp(
|
|
model,
|
|
ConventionalLMP(),
|
|
optimizer = HiGHS.Optimizer,
|
|
)
|
|
return push!(lmps, lmp)
|
|
end
|
|
|
|
# assume the instance is given as a 120h problem
|
|
instance = UnitCommitment.read("instance.json")
|
|
|
|
solution = UnitCommitment.optimize!(
|
|
instance,
|
|
TimeDecomposition(
|
|
time_window = 36, # solve 36h problems
|
|
time_increment = 24, # advance by 24h each time
|
|
inner_method = XavQiuWanThi2019.Method(),
|
|
formulation = Formulation(),
|
|
),
|
|
optimizer = Cbc.Optimizer,
|
|
after_build = after_build,
|
|
after_optimize = after_optimize,
|
|
)</code></pre><h2 id="2.-Scenario-decomposition-with-Progressive-Hedging"><a class="docs-heading-anchor" href="#2.-Scenario-decomposition-with-Progressive-Hedging">2. Scenario decomposition with Progressive Hedging</a><a id="2.-Scenario-decomposition-with-Progressive-Hedging-1"></a><a class="docs-heading-anchor-permalink" href="#2.-Scenario-decomposition-with-Progressive-Hedging" title="Permalink"></a></h2><p>By default, UC.jl uses the Extensive Form (EF) when solving stochastic instances. This approach involves constructing a single JuMP model that contains data and decision variables for all scenarios. Although EF has optimality guarantees and performs well with small test cases, it can become computationally intractable for large instances or substantial number of scenarios.</p><p>Progressive Hedging (PH) is an alternative (heuristic) solution method provided by UC.jl in which the problem is decomposed into smaller scenario-based subproblems, which are then solved in parallel in separate Julia processes, potentially across multiple machines. Quadratic penalty terms are used to enforce convergence of first-stage decision variables. The method is closely related to the Alternative Direction Method of Multipliers (ADMM) and can handle larger instances, although it is not guaranteed to converge to the optimal solution. Our implementation of PH relies on Message Passing Interface (MPI) for communication. We refer to <a href="https://github.com/JuliaParallel/MPI.jl">MPI.jl Documentation</a> for more details on installing MPI.</p><p>The following example shows how to solve SCUC instances using progressive hedging. The script should be saved in a file, say <code>ph.jl</code>, and executed using <code>mpiexec -n <num-scenarios> julia ph.jl</code>.</p><pre><code class="language-julia hljs">using HiGHS
|
|
using MPI
|
|
using UnitCommitment
|
|
using Glob
|
|
|
|
# 1. Initialize MPI
|
|
MPI.Init()
|
|
|
|
# 2. Configure progressive hedging method
|
|
ph = UnitCommitment.ProgressiveHedging()
|
|
|
|
# 3. Read problem instance
|
|
instance = UnitCommitment.read(["example/s1.json", "example/s2.json"], ph)
|
|
|
|
# 4. Build JuMP model
|
|
model = UnitCommitment.build_model(
|
|
instance = instance,
|
|
optimizer = HiGHS.Optimizer,
|
|
)
|
|
|
|
# 5. Run the decentralized optimization algorithm
|
|
UnitCommitment.optimize!(model, ph)
|
|
|
|
# 6. Fetch the solution
|
|
solution = UnitCommitment.solution(model, ph)
|
|
|
|
# 7. Close MPI
|
|
MPI.Finalize()</code></pre><p>When using PH, the model can be customized as usual, with different formulations or additional user-provided constraints. Note that <code>read</code>, in this case, takes <code>ph</code> as an argument. This allows each Julia process to read only the instance files that are relevant to it. Similarly, the <code>solution</code> function gathers the optimal solution of each processes and returns a combined dictionary.</p><p>Each process solves a sub-problem with <span>$\frac{s}{p}$</span> scenarios, where <span>$s$</span> is the total number of scenarios and <span>$p$</span> is the number of MPI processes. For instance, if we have 15 scenario files and 5 processes, then each process will solve a JuMP model that contains data for 3 scenarios. If the total number of scenarios is not divisible by the number of processes, then an error will be thrown.</p><div class="admonition is-warning"><header class="admonition-header">Warning</header><div class="admonition-body"><p>Currently, PH can handle only equiprobable scenarios. Further, <code>solution(model, ph)</code> can only handle cases where only one scenario is modeled in each process.</p></div></div></article><nav class="docs-footer"><a class="docs-footer-prevpage" href="../market/">« Market Clearing</a><a class="docs-footer-nextpage" href="../../guides/problem/">Problem definition »</a><div class="flexbox-break"></div><p class="footer-message">Powered by <a href="https://github.com/JuliaDocs/Documenter.jl">Documenter.jl</a> and the <a href="https://julialang.org/">Julia Programming Language</a>.</p></nav></div><div class="modal" id="documenter-settings"><div class="modal-background"></div><div class="modal-card"><header class="modal-card-head"><p class="modal-card-title">Settings</p><button class="delete"></button></header><section class="modal-card-body"><p><label class="label">Theme</label><div class="select"><select id="documenter-themepicker"><option value="documenter-light">documenter-light</option><option value="documenter-dark">documenter-dark</option><option value="auto">Automatic (OS)</option></select></div></p><hr/><p>This document was generated with <a href="https://github.com/JuliaDocs/Documenter.jl">Documenter.jl</a> version 1.2.1 on <span class="colophon-date" title="Tuesday 21 May 2024 10:56">Tuesday 21 May 2024</span>. Using Julia version 1.10.3.</p></section><footer class="modal-card-foot"></footer></div></div></div></body></html>
|