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.

200 lines
7.5 KiB

  1. /* Power plant LP scheduler */
  2. /* Implemented, inspected, written and converted to GNU MathProg
  3. by NASZVADI, Peter, 199x-2017 <vuk@cs.elte.hu> */
  4. /*
  5. Fast electric power plant scheduler implementation based on new
  6. results in author's Thesis.
  7. The base problem is:
  8. * given some power plants
  9. * a short time scale partitioned to equidistant intervals
  10. * the task is to yielding the cheapest schedule for the plants
  11. * the daily demand forecast is usually accurate and part of the input
  12. The power plants has technical limitations:
  13. * upper and lower bounds of produced energy
  14. * and also a gradient barrier in both directions
  15. (can depend on time, but this GMPL implementation is simplified)
  16. * Also units with same properties (technical and price) should be
  17. scheduled together usually with near same performance values
  18. * Assumed a simplified network topology, which is contractive, so
  19. keeping Kirchhoff's laws is a necessary constraint too
  20. * All solutions must be integer
  21. The LP relaxation is equivalent with the MIP problem due to the
  22. model's matrix interesting property: it is Totally Unimodular
  23. (proven in 2004 by author) and also a Network Matrix (2006,
  24. presented at OTDK 2016, Szeged, Hungary) so:
  25. * it is strictly polynomial if it is solved by most simplex algs
  26. * all base solutions become integer if the RHS vector is integer
  27. (it is in real life, so this is an acceptable assumption)
  28. * The transposed matrix is NOT a Network Matrix in most cases!
  29. However, adding several other constraints easily turns the problem
  30. to be NP-hard, which is also pinpointed and discussed in the Thesis.
  31. See more about electric power plants' scheduling in the
  32. author's Thesis (in Hungarian):
  33. http://www.cs.elte.hu/matdiploma/vuk.pdf
  34. It is possible to run with custom parameters, what is needed
  35. to define is:
  36. * TIME set (daylightsaving cases or other than hour intervals)
  37. * PLANTS set (the 'Demand' is mandatory and usually negative)
  38. * PRICE parameter (can be negative if energy is sold to a consumer)
  39. * BOUND parameter (technical bounds)
  40. * MAXGRAD parameter (technical bounds)
  41. Then generate a pretty-printed solution by typing:
  42. glpsol --math powplant.mod [--data NEW_DATA.dat]
  43. where "NEW_DATA.dat" should contain the above 5 structures filled
  44. with custom data. Square brackets shoudn't be entered, and specifying
  45. custom data file is optional.
  46. */
  47. set TIME, default {
  48. '00:00', '01:00', '02:00', '03:00', '04:00',
  49. '05:00', '06:00', '07:00', '08:00', '09:00',
  50. '10:00', '11:00', '12:00', '13:00', '14:00',
  51. '15:00', '16:00', '17:00', '18:00', '19:00',
  52. '20:00', '21:00', '22:00', '23:00', '24:00'
  53. };
  54. /* Time labels, assumed natural ordering. daylightsaving's bias
  55. can be inserted p.ex. in Central Europe like:
  56. ... '01:00', '02:00', '02:00b', '03:00', ... */
  57. set TADJ := setof{(r, s) in TIME cross TIME: r < s}(r, s) diff
  58. setof{(t, u, v) in TIME cross TIME cross TIME: t < u and u < v}(t, v);
  59. /* Tricky adjacent time label generator because GMPL lacks order determination
  60. of set elements (except intervals composed of equidistant numbers) */
  61. set PLANTS, default {'Demand'};
  62. /* Demand is a promoted, mandatory one, usually filled
  63. with negative MW values in data section */
  64. set DIRECTION, default {'Up', 'Down'};
  65. /* All possible directions of gradients, do not touch */
  66. param MAXINT, default 10000;
  67. /* A "macro" for bounding absolute value of all used numbers
  68. and used as default value */
  69. param PRICE{PLANTS}, default MAXINT;
  70. /* Should be specified in data section, self-explanatory.
  71. can be negative if there are energy buyers */
  72. param BOUND{(p, t, d) in PLANTS cross TIME cross DIRECTION},
  73. default if t = '00:00' then if d = 'Down' then BOUND[p, t, 'Up'] else 0 else
  74. if p <> 'Demand' or d = 'Up' then sum{(u, v) in TADJ: v = t} BOUND[p, u, d]
  75. else BOUND[p, t, 'Up'];
  76. /* Obvious, technical bounds of each power plant unit (real or virtual like
  77. 'Demand'). If some parts are not given in data section, calculated
  78. from preceeding values. Also for time '00:00', its 'Down' values by
  79. default are the same as denoted with 'Up' */
  80. param MAXGRAD{(p, d) in PLANTS cross DIRECTION}, default MAXINT;
  81. /* Usually nonnegative integer, might differ in distinct directions per unit
  82. in the cited thesis, it is allowed to gradient bounds to depend on time,
  83. but this is a simplified model */
  84. var x{(t, p) in TIME cross PLANTS}, <= BOUND[p, t, 'Up'], >= BOUND[p, t, 'Down'];
  85. /* The schedule, dimension is MW */
  86. s.t. kirchhoff{t in TIME: t <> '00:00'}: sum{p in PLANTS} x[t, p] = 0;
  87. /* Conservative property */
  88. s.t. gradient{(p, t, u) in PLANTS cross TADJ}:
  89. -MAXGRAD[p, 'Down'] <= x[t, p] - x[u, p] <= MAXGRAD[p, 'Up'];
  90. /* Technical limitations, each unit usually cannot change performance
  91. arbitrarily in a short time, so limited in both directions per time unit*/
  92. minimize obj: sum{(t, p) in TIME cross PLANTS}(x[t, p] * PRICE[p]);
  93. /* The objective is the cost of the schedule */
  94. solve;
  95. /* Pretty print solution in table */
  96. printf '+--------+';
  97. for{p in PLANTS}{
  98. printf '-% 6s-+', '------';
  99. }
  100. printf '\n';
  101. printf '|%7s |', ' ';
  102. for{p in PLANTS}{
  103. printf ' % 6s |', p;
  104. }
  105. printf '\n';
  106. printf '+--------+';
  107. for{p in PLANTS}{
  108. printf '-% 6s-+', '------';
  109. }
  110. printf '\n';
  111. for{t in TIME}{
  112. printf '|%7s |', t;
  113. for{p in PLANTS}{
  114. printf ' % 6s |', x[t, p].val;
  115. }
  116. printf '\n';
  117. }
  118. printf '+--------+';
  119. for{p in PLANTS}{
  120. printf '-% 6s-+', '------';
  121. }
  122. printf '\n';
  123. data;
  124. /*
  125. Generated random default values and names, the demand is the sum of
  126. 2 sinewaves.
  127. Also specified a treshold for nuclear plants from 15:00 till 19:00
  128. The sun is shining only morning and in the afternoon: 07:00-18:00, so
  129. solar plant cannot produce electric energy after sunset.
  130. Only touch this section, or export it to a data file!
  131. */
  132. set PLANTS 'Demand', 'Atom1', 'Atom2', 'Coal', 'Gas1', 'Gas2', 'Green', 'Oil', 'Solar', 'Dam';
  133. param PRICE :=
  134. 'Demand' 0
  135. 'Atom1' 2
  136. 'Atom2' 2
  137. 'Coal' 15.6
  138. 'Gas1' 12
  139. 'Gas2' 11.5
  140. 'Green' 8.8
  141. 'Oil' 23.3
  142. 'Solar' 7.6
  143. 'Dam' 3;
  144. /* price per MW */
  145. param BOUND :=
  146. [*, *, 'Up'] (tr): 'Atom1' 'Atom2' 'Coal' 'Gas1' 'Gas2' 'Green' 'Oil' 'Solar' 'Dam' :=
  147. '00:00' 240 240 100 150 150 20 90 0 20
  148. '01:00' 240 240 155 192 208 35 230 0 20
  149. [*, *, 'Up'] (tr): 'Atom1' 'Atom2' :=
  150. '15:00' 200 200
  151. '19:00' 235 235
  152. [*, *, 'Up'] (tr): 'Solar' :=
  153. '07:00' 20
  154. '18:00' 0
  155. [*, *, 'Down'] (tr): 'Atom1' 'Atom2' 'Coal' 'Gas1' 'Gas2' 'Green' 'Oil' 'Solar' 'Dam' :=
  156. '01:00' 100 100 50 62 68 0 75 0 20
  157. [*, *, 'Up'] : '01:00' '02:00' '03:00' '04:00' '05:00' '06:00' '07:00' '08:00' :=
  158. 'Demand' -868 -851 -837 -791 -887 -912 -1046 -1155
  159. [*, *, 'Up'] : '09:00' '10:00' '11:00' '12:00' '13:00' '14:00' '15:00' '16:00' :=
  160. 'Demand' -945 -873 -797 -990 -1241 -1134 -815 -782
  161. [*, *, 'Up'] : '17:00' '18:00' '19:00' '20:00' '21:00' '22:00' '23:00' '24:00' :=
  162. 'Demand' -772 -827 -931 -1105 -1215 -1249 -1183 -952;
  163. param MAXGRAD (tr)
  164. : 'Atom1' 'Atom2' 'Coal' 'Gas1' 'Gas2' 'Green' 'Oil' 'Solar' 'Dam' :=
  165. 'Up' 30 30 35 89 95 5 56 2 4
  166. 'Down' 30 30 45 96 102 5 56 2 4;
  167. end;