The source files for all examples can be found in /test.
Re-ordering days in a year
This script illustrates how to re-order days in a year, for example in order to go from 10 representative days to a continuous year of 365 (representative) days.
First we create an instance of a PeriodsFinder. Here we are using the default .yaml configuration file, but keep in mind that a lot of the following lines of code could be avoided by creating your own custom file.
using RepresentativePeriodsFinder;
RPF = RepresentativePeriodsFinder;
config_file = RPF.datadir("default.yaml");
pf = PeriodsFinder(config_file, populate_entries=true);[ Info: Adding LFW...
[ Info: Interpolating missing values...
[ Info: Adding Residual Load...
[ Info: Interpolating missing values...
[ Info: Resampling...
[ Info: Adding Load...
[ Info: Interpolating missing values...
[ Info: Adding LFS...
[ Info: Interpolating missing values...Change the result directory name.
script_name = splitext(splitdir(@__FILE__)[2])[1];
result_dir = joinpath(@__DIR__, "results", script_name);
pf.config["results"]["result_dir"] = result_dir;Turn off automatic plot creation.
pf.config["results"]["create_plots"] = false;We will only concern ourselves with mapping load, just to make things simple.
delete!(pf.config["time_series"], "LFW");
delete!(pf.config["time_series"], "LFS",);
delete!(pf.config["time_series"], "Residual Load");Next we define the total number of periods and the number of representative periods. This script was run without access to a professional solver such as CPLEX or Gurobi, so we will limit ourselves to creating a "year" of 20 days. Further on we will use an alternative formulation which will be able to create a full year of 365 days.
pf.config["method"]["options"]["total_periods"] = 20;
pf.config["method"]["options"]["representative_periods"] = 10;We define our representative periods, where each element in the vector is a day in the year.
rep_periods = [1, 3, 4, 5, 7, 12, 15, 16, 18, 19];We populate the results of the selection variable, pf.u, with our representative days. This is the only step that (currently) cannot be done within the .yaml file.
pf.u = zeros(Bool, 20);
pf.u[rep_periods] .= 1 ;The following lines define the selection and ordering method, specifically we will use an optimisation method which minimises only the absolute (as opposed to squared) time series error.
delete!(pf.config["method"], "clustering");
delete!(pf.config["method"]["optimization"], "duration_curve_error");
pf.config["method"]["optimization"]["time_series_error"]["type"] = "absolute";We set binary ordering to false, which means that a non-representative day can be represented by a linear combination of representative days.
pf.config["method"]["optimization"]["binary_ordering"] = false;We set the mandatory periods to be selected to our representative periods.
pf.config["method"]["options"]["mandatory_periods"] = rep_periods;Setup the optimizer and solve to re-order the days. Importantly don't forget to pass the keyword argument reset=false!
using Ipopt, JuMP;
opt = optimizer_with_attributes(Ipopt.Optimizer, "max_iter" => 100);
find_representative_periods(pf, optimizer=opt, reset=false);┌ Warning: Weight for error ord_err_1 is 0 and it will not impact period selection.
└ @ RepresentativePeriodsFinder /builds/UCM/representativeperiodsfinder.jl/src/util/get.jl:128
******************************************************************************
This program contains Ipopt, a library for large-scale nonlinear optimization.
Ipopt is released as open source code under the Eclipse Public License (EPL).
For more information visit https://github.com/coin-or/Ipopt
******************************************************************************
This is Ipopt version 3.14.4, running with linear solver MUMPS 5.4.1.
Number of nonzeros in equality constraint Jacobian...: 420
Number of nonzeros in inequality constraint Jacobian.: 10780
Number of nonzeros in Lagrangian Hessian.............: 0
Total number of variables............................: 690
variables with only lower bounds: 690
variables with lower and upper bounds: 0
variables with only upper bounds: 0
Total number of equality constraints.................: 31
Total number of inequality constraints...............: 1181
inequality constraints with only lower bounds: 970
inequality constraints with lower and upper bounds: 0
inequality constraints with only upper bounds: 211
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
0 1.2555188e+01 1.99e+01 3.04e+00 -1.0 0.00e+00 - 0.00e+00 0.00e+00 0
1 1.5161980e+01 1.86e+01 1.25e+01 -1.0 2.02e+00 - 5.12e-03 6.64e-02f 1
2 1.9563632e+01 1.67e+01 1.12e+01 -1.0 2.76e+00 - 7.04e-02 9.88e-02f 1
3 3.2460555e+01 1.24e+01 8.34e+00 -1.0 2.72e+00 - 2.62e-01 2.58e-01f 1
4 5.1853264e+01 7.84e+00 5.27e+00 -1.0 2.33e+00 - 4.58e-01 3.69e-01f 1
5 6.7258695e+01 5.52e+00 5.94e+00 -1.0 1.97e+00 - 5.01e-01 2.96e-01f 1
6 1.0312496e+02 2.09e+00 9.95e+00 -1.0 8.94e-01 - 8.35e-01 6.21e-01f 1
7 1.5742099e+02 1.97e-02 8.06e-01 -1.0 4.91e-01 - 9.81e-01 9.91e-01f 1
8 5.1926780e+01 1.79e-03 4.48e+02 -1.7 5.91e-01 - 6.73e-01 9.09e-01f 1
9 4.1378251e+01 2.77e-09 2.00e-07 -1.7 4.51e-01 - 1.00e+00 1.00e+00f 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
10 2.1600149e+01 2.04e-09 4.87e+00 -2.5 3.03e-01 - 8.06e-01 7.66e-01f 1
11 1.7720571e+01 3.86e-10 9.00e+01 -2.5 1.33e-01 - 7.15e-01 8.11e-01f 1
12 1.6879957e+01 7.19e-10 1.01e+02 -2.5 6.33e-02 - 6.49e-01 1.00e+00f 1
13 1.6873420e+01 4.35e-10 2.83e-08 -2.5 1.89e-02 - 1.00e+00 1.00e+00f 1
14 1.4514591e+01 1.42e-10 1.34e+01 -3.8 9.35e-02 - 6.05e-01 6.74e-01f 1
15 1.4063704e+01 2.34e-10 3.66e+00 -3.8 1.25e-01 - 5.91e-01 4.39e-01f 1
16 1.3689554e+01 7.83e-11 2.12e+01 -3.8 1.07e-01 - 6.91e-01 6.65e-01f 1
17 1.3506395e+01 2.31e-10 1.49e+00 -3.8 3.69e-02 - 8.61e-01 1.00e+00f 1
18 1.3506027e+01 7.11e-15 1.50e-09 -3.8 9.95e-03 - 1.00e+00 1.00e+00f 1
19 1.3377581e+01 3.51e-11 2.24e-01 -5.7 1.66e-02 - 7.19e-01 7.27e-01f 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
20 1.3346898e+01 2.78e-11 5.65e+00 -5.7 9.97e-03 - 8.67e-01 6.53e-01f 1
21 1.3332143e+01 2.29e-12 7.57e+04 -5.7 6.28e-03 - 1.13e-01 9.18e-01f 1
22 1.3330838e+01 2.08e-11 1.85e-11 -5.7 6.38e-04 - 1.00e+00 1.00e+00f 1
23 1.3330829e+01 7.11e-15 1.85e-11 -5.7 7.50e-04 - 1.00e+00 1.00e+00h 1
24 1.3329027e+01 7.04e-12 4.94e+00 -8.6 5.21e-04 - 9.10e-01 8.40e-01f 1
25 1.3328791e+01 4.83e-11 1.14e+01 -8.6 6.20e-04 - 8.39e-01 6.88e-01f 1
26 1.3328705e+01 9.07e-12 8.91e+00 -8.6 2.29e-04 - 5.35e-01 8.12e-01f 1
In iteration 26, 1 Slack too small, adjusting variable bound
27 1.3328694e+01 3.93e-12 3.90e+00 -8.6 4.36e-05 - 7.27e-01 5.67e-01f 1
In iteration 27, 1 Slack too small, adjusting variable bound
28 1.3328688e+01 8.09e-11 1.15e+00 -8.6 1.89e-05 - 7.42e-01 7.06e-01h 1
29 1.3328686e+01 3.55e-15 1.38e+00 -8.6 5.56e-06 - 5.43e-01 1.00e+00h 1
iter objective inf_pr inf_du lg(mu) ||d|| lg(rg) alpha_du alpha_pr ls
30 1.3328686e+01 2.15e-15 2.66e-14 -8.6 6.16e-08 - 1.00e+00 1.00e+00h 1
Number of Iterations....: 30
(scaled) (unscaled)
Objective...............: 1.3328686337693618e+01 1.3328686337693618e+01
Dual infeasibility......: 2.6645352591003757e-14 2.6645352591003757e-14
Constraint violation....: 2.1521381349761983e-15 2.1521381349761983e-15
Variable bound violation: 9.9189549019547981e-09 9.9189549019547981e-09
Complementarity.........: 6.5341399297206729e-09 6.5341399297206729e-09
Overall NLP error.......: 6.5341399297206729e-09 6.5341399297206729e-09
Number of objective function evaluations = 31
Number of objective gradient evaluations = 31
Number of equality constraint evaluations = 31
Number of inequality constraint evaluations = 31
Number of equality constraint Jacobian evaluations = 1
Number of inequality constraint Jacobian evaluations = 1
Number of Lagrangian Hessian evaluations = 1
Total seconds in IPOPT = 0.619
EXIT: Optimal Solution Found.We can plot the resulting synthetic time series. Note how days 1 and 3, which are representative, match up exactly while other days must be approximated.
using Dates;
timestamps = Dict("Load" => DateTime(1970,1,1):Hour(1):DateTime(1970,1,7));
create_synthetic_time_series_plot(pf, "Load"; timestamps=timestamps)
We can also check out the heatmap, which shows how representative days are mapped to non-representative days throughout the year:
create_ordering_heatmap(pf)
We can also write out the this synthetic time series to pf.config["results"]["result_dir"].
write_out_synthetic_timeseries(pf, timestamps=timestamps)This last optimisation was done by solving a linear problem. We can also do this by solving a binary problem, where each representative day is mapped to a non-representative day as opposed to using a linear combination of days. The advantage of this is that we can define fancier error functions.
Change the total number of days and re-define our representative periods:
pf.config["method"]["options"]["total_periods"] = 365;
rep_periods = [i for i in 1:35:350];
pf.config["method"]["options"]["mandatory_periods"] = rep_periods;
pf.u = zeros(Bool, 365);
pf.u[rep_periods] .= 1;For reasons not worth getting into, we need to re-populate pf:
populate_entries!(pf);[ Info: Adding Load...
[ Info: Interpolating missing values...We get rid of the time series error entry:
delete!(pf.config["method"]["optimization"], "time_series_error");Now we define our ordering error function, which is the error associated with representing a day x with another day y.
pf.config["method"]["options"]["ordering_error"] = Dict(
"ord_err_1" => Dict(
"function" => (x,y) -> sum((x .- y).^2),
"weight" => 1.0
)
);Here we've used the 2-norm error, but we could have specified anything we wanted.
We set binary ordering to true:
pf.config["method"]["optimization"]["binary_ordering"] = true;Setup the optimizer and solve to re-order the days.
using Cbc;
opt = optimizer_with_attributes(Cbc.Optimizer, "seconds" => 60);
find_representative_periods(pf, optimizer=opt, reset=false);Let's inspect the heatmap again
result_dir = joinpath(@__DIR__, "results", script_name * "_binary");
pf.config["results"]["result_dir"] = result_dir;
create_ordering_heatmap(pf)
Note how we have solid colors now, since we don't represent days by linear combinations of representative days.
This page was generated using Literate.jl.