CONOPT
Loading...
Searching...
No Matches
pinadd2ddir.py
Go to the documentation of this file.
7
8import os
9import sys
10import math
11
12import conopt as co
13
14sys.path.append('../common/')
15import std
16
17
18class PinAdd2DDirModelData(co.ModelData):
19 """ """
20
21 def __init__(self):
22 self.CONS_SEQ = 0
23 self.CONS_DREV = 1
24
25 self.T = 0
26
27 # index arrays for the variables
28 self.vartd = []
29 self.varcs = []
30 self.vars = []
31 self.vard = []
32 self.varr = []
33 self.varp = []
34 self.varrev = []
35
36 # index arrays for the constraints
37 self.consttdeq = []
38 self.constseq = []
39 self.constcseq = []
40 self.constdeq = []
41 self.constreq = []
42 self.constdrev = []
43
44 self.consmapping = {}
45
46 self.hessianstart = []
47 super().__init__()
48
49 def buildModel(self, T, xkeep, xstat, estat):
50 """
51 adding the variables and constraints to the model
52 @ingroup PYTHON1THREAD_PINADD2DDIR
53 """
54
55 self.T = T
56 demand = [1.0 + 2.3 * pow(1.015, t) for t in range(T)]
57
58 # adding the variables to the model
59 for t in range(T):
60 # td: Lower=0, Upper=inf, Curr=18
61 varidx = self.addVariable(0, co.Conopt.Infinity, 18)
62 self.vartd.append(varidx)
63
64 # cs: Lower=0, Upper=inf, Curr=7*t
65 varidx = self.addVariable(0, co.Conopt.Infinity, 7 * (t + 1))
66 self.varcs.append(varidx)
67
68 # s: Lower=0, Upper=inf, Curr=7
69 varidx = self.addVariable(0, co.Conopt.Infinity, 7)
70 self.vars.append(varidx)
71
72 # d: Lower=0, Upper=inf, Curr=td-s
73 varidx = self.addVariable(
74 0,
75 co.Conopt.Infinity,
76 self.getVariable(self.vartd[t]).curr
77 - self.getVariable(self.vars[t]).curr,
78 )
79 self.vard.append(varidx)
80
81 # r: Lower=1, Upper=inf, Curr=r(t-1)-d
82 if t > 0:
83 varidx = self.addVariable(
84 1,
85 co.Conopt.Infinity,
86 self.getVariable(self.varr[t - 1]).curr
87 - self.getVariable(self.vard[t]).curr,
88 )
89 else:
90 varidx = self.addVariable(
91 1,
92 co.Conopt.Infinity,
93 500 - self.getVariable(self.vard[t]).curr,
94 )
95 self.varr.append(varidx)
96
97 # p: Lower=1, Upper=inf, Curr=14
98 varidx = self.addVariable(1, co.Conopt.Infinity, 14)
99 self.varp.append(varidx)
100
101 # rev: Lower=-inf, Upper=inf, Curr=0
102 varidx = self.addVariable(-co.Conopt.Infinity, co.Conopt.Infinity)
103 self.varrev.append(varidx)
104
105 # This is a restart: Use the initial values from last solve for
106 # the variables in the first periods and extrapolate the last
107 # period using a linear extrapolation between the last two
108 # periods.
109 if len(xkeep) > 0:
110 for i in range(len(xkeep)):
111 self.getVariable(i).curr = xkeep[i]
112
113 # linear extrapolation for the last period of variable td
114 self.getVariable(self.vartd[T - 1]).curr = (
115 2 * self.getVariable(self.vartd[T - 2]).curr
116 - self.getVariable(self.vartd[T - 3]).curr
117 )
118 # cs
119 self.getVariable(self.varcs[T - 1]).curr = (
120 2 * self.getVariable(self.varcs[T - 2]).curr
121 - self.getVariable(self.varcs[T - 3]).curr
122 )
123 # s
124 self.getVariable(self.vars[T - 1]).curr = (
125 2 * self.getVariable(self.vars[T - 2]).curr
126 - self.getVariable(self.vars[T - 3]).curr
127 )
128 # d
129 self.getVariable(self.vard[T - 1]).curr = (
130 2 * self.getVariable(self.vard[T - 2]).curr
131 - self.getVariable(self.vard[T - 3]).curr
132 )
133 # r
134 self.getVariable(self.varr[T - 1]).curr = (
135 2 * self.getVariable(self.varr[T - 2]).curr
136 - self.getVariable(self.varr[T - 3]).curr
137 )
138 # p
139 self.getVariable(self.varp[T - 1]).curr = (
140 2 * self.getVariable(self.varp[T - 2]).curr
141 - self.getVariable(self.varp[T - 3]).curr
142 )
143 # rev
144 self.getVariable(self.varrev[T - 1]).curr = (
145 2 * self.getVariable(self.varrev[T - 2]).curr
146 - self.getVariable(self.varrev[T - 3]).curr
147 )
148
149 # The variables from the last solve are given the old status and
150 # the variables in the new period are given those in the last
151 # period. Similarly with the Equation status:
152 for i in range(len(xstat)):
153 self.getVariable(i).varstatus = xstat[i]
154
155 # td
156 self.getVariable(self.vartd[T - 1]).varstatus = self.getVariable(
157 self.vartd[T - 2]
158 ).varstatus
159 # cs
160 self.getVariable(self.varcs[T - 1]).varstatus = self.getVariable(
161 self.varcs[T - 2]
162 ).varstatus
163 # s
164 self.getVariable(self.vars[T - 1]).varstatus = self.getVariable(
165 self.vars[T - 2]
166 ).varstatus
167 # d
168 self.getVariable(self.vard[T - 1]).varstatus = self.getVariable(
169 self.vard[T - 2]
170 ).varstatus
171 # r
172 self.getVariable(self.varr[T - 1]).varstatus = self.getVariable(
173 self.varr[T - 2]
174 ).varstatus
175 # p
176 self.getVariable(self.varp[T - 1]).varstatus = self.getVariable(
177 self.varp[T - 2]
178 ).varstatus
179 # rev
180 self.getVariable(self.varrev[T - 1]).varstatus = self.getVariable(
181 self.varrev[T - 2]
182 ).varstatus
183
184 # adding the objective
185 objcoeff = [pow(1.05, 1 - (t + 1)) for t in range(T)]
186 objnlflag = [0] * T
187 objidx = self.addConstraint(
188 co.ConstraintType_Free, 0.0, self.varrev, objcoeff, objnlflag
189 )
190
191 # adding the constraints to the model
192 for t in range(T):
193 if t == 0:
194 considx = self.addConstraint(
195 co.ConstraintType_Eq,
196 demand[t] + 0.87 * 18.0,
197 [self.vartd[t], self.varp[t]], # the variables
198 [1.0, 0.13], # the coefficients
199 [0, 0], # nlflags
200 )
201 else:
202 considx = self.addConstraint(
203 co.ConstraintType_Eq,
204 demand[t],
205 [self.vartd[t], self.vartd[t - 1], self.varp[t]],
206 [1.0, -0.87, 0.13],
207 [0, 0, 0],
208 )
209 self.consttdeq.append(considx)
210
211 if t == 0:
212 considx = self.addConstraint(
213 co.ConstraintType_Eq,
214 0.75 * 6.5,
215 [self.vars[t], self.varp[t], self.varcs[t]],
216 [1.0, 0.0, 0.0],
217 [0, 1, 1],
218 )
219 else:
220 considx = self.addConstraint(
221 co.ConstraintType_Eq,
222 0.0,
223 [
224 self.vars[t],
225 self.vars[t - 1],
226 self.varp[t],
227 self.varcs[t],
228 ],
229 [1.0, -0.75, 0.0, 0.0],
230 [0, 0, 1, 1],
231 )
232 self.constseq.append(considx)
233 self.consmapping[considx] = (self.CONS_SEQ, t)
234
235 if t == 0:
236 considx = self.addConstraint(
237 co.ConstraintType_Eq,
238 0.0,
239 [self.varcs[t], self.vars[t]],
240 [1.0, -1.0],
241 [0, 0],
242 )
243 else:
244 considx = self.addConstraint(
245 co.ConstraintType_Eq,
246 0.0,
247 [self.varcs[t], self.varcs[t - 1], self.vars[t]],
248 [1.0, -1.0, -1.0],
249 [0, 0, 0],
250 )
251 self.constcseq.append(considx)
252
253 considx = self.addConstraint(
254 co.ConstraintType_Eq,
255 0.0,
256 [self.vard[t], self.vartd[t], self.vars[t]],
257 [1.0, -1.0, 1.0],
258 [0, 0, 0],
259 )
260 self.constdeq.append(considx)
261
262 if t == 0:
263 considx = self.addConstraint(
264 co.ConstraintType_Eq,
265 500.0,
266 [self.varr[t], self.vard[t]],
267 [1.0, 1.0],
268 [0, 0],
269 )
270 else:
271 considx = self.addConstraint(
272 co.ConstraintType_Eq,
273 0.0,
274 [self.varr[t], self.varr[t - 1], self.vard[t]],
275 [1.0, -1.0, 1.0],
276 [0, 0, 0],
277 )
278 self.constreq.append(considx)
279
280 considx = self.addConstraint(
281 co.ConstraintType_Eq,
282 0.0,
283 [self.varrev[t], self.vard[t], self.varp[t], self.varr[t]],
284 [1.0, 0.0, 0.0, 0.0],
285 [0, 1, 1, 1],
286 )
287 self.constdrev.append(considx)
288 self.consmapping[considx] = (self.CONS_DREV, t)
289
290 # Restore values from previous runs
291 if len(estat) > 0:
292 # the equation status
293 for i in range(len(estat)):
294 self.getConstraint(i).slackstatus = estat[i]
295
296 # tdeq
297 self.getConstraint(
298 self.consttdeq[T - 1]
299 ).slackstatus = self.getConstraint(self.consttdeq[T - 2]).slackstatus
300 # seq
301 self.getConstraint(
302 self.constseq[T - 1]
303 ).slackstatus = self.getConstraint(self.constseq[T - 2]).slackstatus
304 # cseq
305 self.getConstraint(
306 self.constcseq[T - 1]
307 ).slackstatus = self.getConstraint(self.constcseq[T - 2]).slackstatus
308 # deq
309 self.getConstraint(
310 self.constdeq[T - 1]
311 ).slackstatus = self.getConstraint(self.constdeq[T - 2]).slackstatus
312 # req
313 self.getConstraint(
314 self.constreq[T - 1]
315 ).slackstatus = self.getConstraint(self.constreq[T - 2]).slackstatus
316 # drev
317 self.getConstraint(
318 self.constdrev[T - 1]
319 ).slackstatus = self.getConstraint(self.constdrev[T - 2]).slackstatus
320
321 # setting the objective constraint
322 self.setObjectiveElement(co.ObjectiveElement_Constraint, objidx)
323
324 # setting the optimisation direction
325 self.setOptimizationSense(co.Sense_Maximize)
326
327 hsrow = []
328 hscol = []
329
330 for t in range(T):
331 # storing the start index for the hessian in each time period.
332 # This will provide a mapping that can be used in the hessian
333 # evaluation
334 self.hessianstart.append(len(hscol))
335
336 hscol.append(self.varcs[t])
337 hsrow.append(self.varcs[t])
338 hscol.append(self.varcs[t])
339 hsrow.append(self.varp[t])
340 hscol.append(self.vard[t])
341 hsrow.append(self.varr[t])
342 hscol.append(self.vard[t])
343 hsrow.append(self.varp[t])
344 hscol.append(self.varr[t])
345 hsrow.append(self.varr[t])
346 self.setSDLagrangianStructure(hsrow, hscol)
347
348 def evaluateNonlinearTerm(self, x, rowno, ignerr, thread):
349 """
350 @copydoc conopt.ModelData.evaluateNonlinearTerm
351 @ingroup PYTHON1THREAD_PINADD2DDIR
352 """
353 if rowno not in self.consmapping:
354 return
355
356 # unpack consset and time index
357 consset, t = self.consmapping[rowno]
358 assert consset in (self.CONS_SEQ, self.CONS_DREV)
359
360 g = 0
361 if consset == self.CONS_SEQ:
362 h1 = 1.1 + 0.1 * x[self.varp[t]]
363 h2 = pow(1.02, -x[self.varcs[t]] / 7.0)
364 g = -h1 * h2
365
366 elif consset == self.CONS_DREV:
367 g = -x[self.vard[t]] * (x[self.varp[t]] - 250.0 / x[self.varr[t]])
368
369 return g
370
371 def evaluateNonlinearJacobian(self, x, rowno, jacnum, ignerr, thread):
372 """
373 @copydoc conopt.ModelData.evaluateNonlinearJacobian
374 @ingroup PYTHON1THREAD_PINADD2DDIR
375 """
376 if rowno not in self.consmapping:
377 return
378
379 # unpack consset and time index
380 consset, t = self.consmapping[rowno]
381 assert consset in (self.CONS_SEQ, self.CONS_DREV)
382
383 jac = []
384 if consset == self.CONS_SEQ:
385 h1 = 1.1 + 0.1 * x[self.varp[t]]
386 h2 = pow(1.02, -x[self.varcs[t]] / 7.0)
387 jac.append(h1 * h2 * math.log(1.02) / 7.0)
388 jac.append(-h2 * 0.1)
389
390 elif consset == self.CONS_DREV:
391 jac.append(-(x[self.varp[t]] - 250.0 / x[self.varr[t]]))
392 jac.append(-x[self.vard[t]] * 250.0 / pow(x[self.varr[t]], 2))
393 jac.append(-x[self.vard[t]])
394
395 return jac
396
397 def evaluateDirectionalSD(self, x, dx, rowno, jacnum, thread):
398 """
399 @copydoc conopt.ModelData.evaluateDirectionalSD
400 @ingroup PYTHON1THREAD_PINADD2DDIR
401 """
402 # returning an iterator based on the row number.
403 # NOTE: a map is just an array of pairs. So for the iterator, the
404 # first element is the key and the second is the value. In our case,
405 # then value is also a pair.
406 if rowno not in self.consmapping:
407 return
408
409 # storing the value (a pair) as a separate variable to make the
410 # extraction easier
411 # unpack consset and time index
412 consset, t = self.consmapping[rowno]
413 assert consset in (self.CONS_SEQ, self.CONS_DREV)
414 dirsd = [0 for i in range(jacnum)]
415 if consset == self.CONS_SEQ:
416 # seq equation. Nonlinear term = -(1.1+0.1*p)*1.02**(-cs/7)
417 # Hessian (diagonal and lower triangle): A total of 2 nonzero elements per period
418 # 1 5
419 # cs p
420 # 1 : cs -(1.1+0.1p)*1.02**(-cs/7)*(log(1.02)/7)**2
421 # 5 : p 0.1*1.02**(-cs/7)*(log(1.02)72)
422
423 cs = x[self.varcs[t]]
424 p = x[self.varp[t]]
425 h1 = 1.1 + 0.1 * p
426 h2 = pow(1.02, -cs / 7.0)
427 h11 = -h1 * h2 * pow(math.log(1.02) / 7.0, 2)
428 h15 = 0.1 * h2 * (math.log(1.02) / 7.0)
429 dirsd[self.varcs[t]] = h11 * dx[self.varcs[t]] + h15 * dx[self.varp[t]]
430 dirsd[self.varp[t]] = h15 * dx[self.varcs[t]]
431
432 elif consset == self.CONS_DREV:
433 # drev equation. Nonlinear term = -d*(p-250/r)
434 # Hessian (diagonal and lower triangle): A total of 3 nonzero elements per period
435 # 3 4 5
436 # d r p
437 # 3 : d
438 # 4 : r -250/r**2 500*d/r**3
439 # 5 : p -1
440 d = x[self.vard[t]]
441 r = x[self.varr[t]]
442 h34 = -250.0 / pow(r, 2)
443 h35 = -1.0
444 h44 = 500.0 * d / pow(r, 3)
445 dirsd[self.vard[t]] = h34 * dx[self.varr[t]] + h35 * dx[self.varp[t]]
446 dirsd[self.varr[t]] = h34 * dx[self.vard[t]] + h44 * dx[self.varr[t]]
447 dirsd[self.varp[t]] = h35 * dx[self.vard[t]]
448
449 else:
450 # Error - this equation is not nonlinear
451 print('\nError. Pin_2DDir called with rowno = %d.\n\n', rowno)
452 return
453
454 return dirsd
455
456 def evaluateSDLagrangian(self, x, u, hessianrow, hessiancol):
457 """
458 @copydoc conopt.ModelData.evaluateSDLagrangian
459 @ingroup PYTHON1THREAD_PINADD2DDIR
460 """
461 numhessian = len(hessianrow)
462 hessian = [0 for i in range(numhessian)]
463
464 # Normal Evaluation mode
465 for t in range(self.T):
466 hessidx = self.hessianstart[t]
467 cs = x[self.varcs[t]]
468 d = x[self.vard[t]]
469 r = x[self.varr[t]]
470 p = x[self.varp[t]]
471
472 h1 = 1.1 + 0.1 * p
473 h2 = pow(1.02, -cs / 7.0)
474
475 # the second derivative of the SEQ equations
476 hessian[hessidx] = (
477 -h1 * h2 * pow(math.log(1.02) / 7.0, 2) * u[self.constseq[t]]
478 )
479 hessian[hessidx + 1] = (
480 0.1 * h2 * (math.log(1.02) / 7.0) * u[self.constseq[t]]
481 )
482
483 # the second derivative of the DREV equations
484 hessian[hessidx + 2] = -250.0 / pow(r, 2) * u[self.constdrev[t]]
485 hessian[hessidx + 3] = -1.0 * u[self.constdrev[t]]
486 hessian[hessidx + 4] = 500.0 * d / pow(r, 3) * u[self.constdrev[t]]
487
488 return hessian
489
490
491if __name__ == '__main__':
492 name = os.path.basename(__file__)[:-3]
493
494 conopt = co.Conopt(name)
495 msghdlr = std.TutMessageHandler(name)
496 conopt.setMessageHandler(msghdlr)
497
498 # getting the license variables
499 license_int_1 = os.environ.get('CONOPT_LICENSE_INT_1', None)
500 license_int_2 = os.environ.get('CONOPT_LICENSE_INT_2', None)
501 license_int_3 = os.environ.get('CONOPT_LICENSE_INT_3', None)
502 license_text = os.environ.get('CONOPT_LICENSE_TEXT', None)
503 if (
504 license_int_1 is not None
505 and license_int_2 is not None
506 and license_int_3 is not None
507 and license_text is not None
508 ):
509 conopt.setLicense(
510 int(license_int_1),
511 int(license_int_2),
512 int(license_int_3),
513 license_text,
514 )
515
516 expected_sols = [1170.49, 1225.86, 1280.28, 1333.74, 1386.24]
517 for i in range(16, 21):
518 model = PinAdd2DDirModelData()
519
520 xkeep = conopt.getVariableValues()
521 xstat = conopt.getVariableStatus()
522 estat = conopt.getConstraintStatus()
523
524 # building the model
525 model.buildModel(
526 i, xkeep, xstat, estat
527 ) # Add T, and other vectors as parameters
528
529 conopt.loadModel(model)
530
531 coi_error = conopt.solve()
532
533 retcode = std.checkSolve(
534 conopt, expected_sols[i - 16], coi_error, tol=0.01
535 )
536
537 sys.exit(retcode)
static int checkSolve(String name, int model_status, int solution_status, double objective, double expected_objective, double tol)
Definition std.java:20
buildModel(self, T, xkeep, xstat, estat)
adding the variables and constraints to the model
evaluateNonlinearTerm(self, x, rowno, ignerr, thread)
callback method for evaluating the nonlinear terms in a given row
evaluateSDLagrangian(self, x, u, hessianrow, hessiancol)
Computes and returns the numerical values of the Lagrangian of the Hessian.
evaluateNonlinearJacobian(self, x, rowno, jacnum, ignerr, thread)
callback method for evaluating the jacobian for the nonlinear terms in a given row
evaluateDirectionalSD(self, x, dx, rowno, jacnum, thread)
computes the directional second derivative for a single constraint