From 2d510ca7eadbc0b1f5acc609c87827766d547ae6 Mon Sep 17 00:00:00 2001 From: Jun He Date: Wed, 7 Jun 2023 13:22:56 -0400 Subject: [PATCH] updated doc for time decomp --- docs/src/usage.md | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/docs/src/usage.md b/docs/src/usage.md index 2190d80..69f8d7e 100644 --- a/docs/src/usage.md +++ b/docs/src/usage.md @@ -287,21 +287,41 @@ aelmp = UnitCommitment.compute_lmp( 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 `optimize!` function. This method decomposes the problem into multiple sub-problems, solving them sequentially. -The `optimize!` function takes three parameters: a unit commitment instance, a `TimeDecomposition` method, and an optimizer. It returns a solution dictionary. The `TimeDecomposition` method itself requires four arguments: `time_window`, `time_increment`, `inner_method`, and `formulation`. 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 `optimize!` function takes 5 parameters: a unit commitment instance, a `TimeDecomposition` method, an optimizer, and two optional functions `after_build` and `after_optimize`. It returns a solution dictionary. The `TimeDecomposition` method itself requires four arguments: `time_window`, `time_increment`, `inner_method` (optional), and `formulation` (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 `after_build` and `after_optimize`, are invoked subsequent to the construction and optimization of each sub-model, respectively. It is imperative that the `after_build` function requires its two arguments to be consistently mapped to `model` and `instance`, while the `after_optimize` function necessitates its three arguments to be consistently mapped to `solution`, `model`, and `instance`. -The code snippet below illustrates an example of solving an instance by decomposing the model into multiple 36-hour sub-problems using the `XavQiuWanThi2019` 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. +The code snippet below illustrates an example of solving an instance by decomposing the model into multiple 36-hour sub-problems using the `XavQiuWanThi2019` 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 `after_build` function imposes the restriction that `g3` and `g4` cannot be activated simultaneously during the initial time slot of each sub-problem. On the other hand, the `after_optimize` function is invoked to calculate the conventional Locational Marginal Prices (LMPs) for each sub-problem, and subsequently appends the computed values to the `lmps` vector. > **Warning** > Specifying `TimeDecomposition` as the value of the `inner_method` field of another `TimeDecomposition` causes errors when calling the `optimize!` function due to the different argument structures between the two `optimize!` functions. ```julia -using UnitCommitment, Cbc +using UnitCommitment, JuMP, Cbc, HiGHS import UnitCommitment: TimeDecomposition, - Formulation, - XavQiuWanThi2019 - + 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!( @@ -312,6 +332,8 @@ solution = UnitCommitment.optimize!( inner_method = XavQiuWanThi2019.Method(), formulation = Formulation(), ), - optimizer=Cbc.Optimizer + optimizer = Cbc.Optimizer, + after_build = after_build, + after_optimize = after_optimize, ) ``` \ No newline at end of file