model.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. # -*- coding: utf-8 -*-
  2. """
  3. Modulo principal para ejecutar el modelo de subasta
  4. """
  5. import logging
  6. from logging.handlers import RotatingFileHandler
  7. from os import path
  8. import queue
  9. import pandas as pd
  10. import pyomo.environ as pe
  11. import pyutilib.subprocess.GlobalData
  12. from numpy import array, zeros
  13. from openpyxl import load_workbook
  14. from pyomo.environ import SolverFactory
  15. from pyomo.kernel import value
  16. from simsdt.mct.makeMCT import linmct, readmct, set_dir_flujo
  17. from simsdt.ofertas.readBids import *
  18. from simsdt.red.create import branchnames, setbranch, setbus
  19. from simsdt.red.makeBdc import makeBdc
  20. from simsdt.red.makePTDF import makePTDF
  21. from simsdt.red.read import excel2net
  22. from simsdt.utils.arr2dict import arr2dict
  23. from simsdt.utils.idx_brch import RT_MAX, RT_MIN, R
  24. from qhandler import QueueHandler
  25. pyutilib.subprocess.GlobalData.DEFINE_SIGNAL_HANDLERS_DEFAULT = False
  26. # log_queue = queue.Queue()
  27. # qh = QueueHandler(log_queue)
  28. # formatter = logging.Formatter(
  29. # '%(asctime)s: %(message)s', datefmt='%Y-%m-%dT%H:%M:%S')
  30. # qh.setFormatter(formatter)
  31. fh = RotatingFileHandler(
  32. 'log/simsdt_model_monitor.log', maxBytes=10240, backupCount=10)
  33. fmt = logging.Formatter(
  34. '%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%dT%H:%M:%S')
  35. fh.setFormatter(fmt)
  36. # ch = logging.StreamHandler()
  37. # ch.setFormatter(fmt)
  38. # logging.basicConfig(handlers=[fh, ch, qh], level=logging.INFO)
  39. model_logger = logging.getLogger('simsdt.model')
  40. # model_logger.addHandler(qh)
  41. model_logger.addHandler(fh)
  42. class ModeloSubasta:
  43. def __init__(self, file, solver_path):
  44. self.file = file
  45. self.solver_path = solver_path
  46. def setmodel(self):
  47. model_logger.info('Modelo de Subasta de Derechos Firmes')
  48. # ============================================================================
  49. # Importación de datos desde archivo Excel
  50. # ============================================================================
  51. model_logger.info("Inicio del Script de Subasta")
  52. model_logger.info("Leyendo información de red")
  53. net = excel2net(self.file)
  54. model_logger.info(
  55. "Leyendo información de ofertas y derechos firmes existentes")
  56. ex = readexistentes(self.file)
  57. nex = ex.shape[0]
  58. of = readofertas(self.file)
  59. nof = of.shape[0]
  60. # ============================================================================
  61. # Construcción de vectores y matrices para los SETS y PARAMETERS
  62. # ============================================================================
  63. model_logger.info("Creando información de buses y líneas")
  64. # Datos de Red
  65. bus = setbus(net)
  66. branch = setbranch(net, bus)
  67. nb = bus.shape[0]
  68. nbr = branch.shape[0]
  69. model_logger.debug(
  70. "Se han cargado {0} buses y {1} líneas".format(nb, nbr))
  71. model_logger.info("Leyendo información de Resistencia de líneas")
  72. brnames = branchnames(bus, branch)
  73. r = branch[:, R]
  74. # Datos de límites superior e inferior asociados a las lineas
  75. model_logger.info(
  76. "Obteniendo datos de límites superior e inferior de líneas...")
  77. bu = branch[:, RT_MAX]/100
  78. bl = branch[:, RT_MIN]/100
  79. # Lineas para límites de MCT
  80. lin_mct = linmct(brnames)
  81. mct = readmct(self.file)/100
  82. dirf = set_dir_flujo()
  83. model_logger.info("Cálculando la matriz H")
  84. # Cálculo de la matriz H
  85. H = makePTDF(bus, branch)
  86. model_logger.info("Cálculando la matriz de Incidencia")
  87. # Matriz de Incidencia
  88. _, _, A = makeBdc(bus, branch)
  89. inc = A.toarray()**2
  90. model_logger.info("Creando Vectores de Derechos Firmes existentes")
  91. # Datos de los Derechos Firmes Existentes
  92. vite = setVITE(bus, ex)/100
  93. vrte = setVRTE(bus, ex)/100
  94. vitex = setVITEX(bus, ex)/100
  95. te = setTE(vite, vrte)
  96. model_logger.info("Creando vectores de Ofertas de Derechos firmes")
  97. # Datos de las ofertas de Derechos Firmes
  98. c = of.oferta.values
  99. cper = of.cper.values
  100. perk = of.per.values/100
  101. vit = setVIT(bus, of)/100
  102. vrt = setVRT(bus, of)/100
  103. vitx = setVITX(bus, of)/100
  104. t = setT(vit, vrt)
  105. # ============================================================================
  106. # Modelo de optimización subasta de derechos Firmes
  107. # ============================================================================
  108. model_logger.info("Inicio del problema de Optimización")
  109. # Inicio del modelo de optimización
  110. model = pe.ConcreteModel()
  111. # SETS
  112. model_logger.info("Creando Sets del problema")
  113. model.c = pe.Set(initialize=range(0, nbr)) # Número de circuitos
  114. model.n = pe.Set(initialize=range(0, nb)) # Número de nodos
  115. model.o = pe.Set(initialize=range(0, nex)) # Número de DF existentes
  116. model.k = pe.Set(initialize=range(0, nof)) # Número de Ofertas DF
  117. model.i = pe.Set(initialize=lin_mct.keys()) # Interconexiones
  118. # Sentidos de interconexiones
  119. model.s = pe.Set(initialize=['sn', 'ns'])
  120. # PARAMETERS
  121. model_logger.info("Creando Parametros del problema")
  122. # Ofertas derechos firmes
  123. model_logger.info("Parametros de Ofertas de Derechos Firmes")
  124. model.C = pe.Param(model.k, initialize=dict(enumerate(c)))
  125. model.Cper = pe.Param(model.k, initialize=dict(enumerate(cper)))
  126. model.Perk = pe.Param(model.k, initialize=dict(enumerate(perk)))
  127. # Parametros de la red
  128. model_logger.info("Parametros de Red")
  129. model.Bu = pe.Param(model.c, initialize=dict(enumerate(bu)))
  130. model.Bl = pe.Param(model.c, initialize=dict(enumerate(bl)))
  131. model.R = pe.Param(model.c, initialize=dict(enumerate(r)))
  132. model.Inc = pe.Param(model.c, model.n, initialize=arr2dict(inc))
  133. # elemento H[c,n]
  134. model_logger.info("Parametros de Matriz H")
  135. model.He = pe.Param(model.c, model.n, initialize=arr2dict(H))
  136. model_logger.info("Parametros de Derechos Firmes Existentes")
  137. # Vector de inyecciones df existentes
  138. model.VITE = pe.Param(model.n, model.o, initialize=arr2dict(vite))
  139. # Vector de retiros df existentes
  140. model.VRTE = pe.Param(model.n, model.o, initialize=arr2dict(vrte))
  141. # Vector de perididas df existentes
  142. model.VITEX = pe.Param(model.n, model.o, initialize=arr2dict(vitex))
  143. # DF existentes
  144. model.TE = pe.Param(model.n, model.o, initialize=arr2dict(te))
  145. model_logger.info("Parametros de Inyecciones y Retiros de Ofertas")
  146. # Vector inyecciones ofertas de compra df
  147. model.VIT = pe.Param(model.n, model.k, initialize=arr2dict(vit))
  148. # Vector retiros ofertas de compra df
  149. model.VRT = pe.Param(model.n, model.k, initialize=arr2dict(vrt))
  150. # Vector inyecciones perdidas oferta de compra df
  151. model.VITX = pe.Param(model.n, model.k, initialize=arr2dict(vitx))
  152. # Ofertas de compora DF
  153. model.T = pe.Param(model.n, model.k, initialize=arr2dict(t))
  154. model_logger.info("Parametros de Limites de flujos en las líneas")
  155. # Lim. Superior flujos asociados a linea c
  156. def bfu_param(model, c):
  157. return model.Bu[c] - sum(max(0, sum(model.He[c, n]*model.TE[n, o] for n in model.n)) for o in model.o)
  158. model.BFu = pe.Param(model.c, initialize=bfu_param)
  159. # LIm. Inferior flujos asociados a linea c
  160. def bfl_param(model, c):
  161. return model.Bl[c] - sum(max(0, -sum(model.He[c, n]*model.TE[n, o] for n in model.n)) for o in model.o)
  162. model.BFl = pe.Param(model.c, initialize=bfl_param)
  163. # Máximas Capacidades de Trasferencia
  164. model.Mct = pe.Param(model.i, model.s, initialize=mct.mct.to_dict())
  165. model.DirF = pe.Param(model.i, initialize=dirf)
  166. # VARIABLES
  167. model_logger.info("Creando Variables del problema")
  168. # Variables primales
  169. model.alpha_k = pe.Var(
  170. model.k, within=pe.NonNegativeReals, bounds=(0, 1))
  171. model.gamma_k = pe.Var(model.k, within=pe.NonNegativeReals)
  172. # Flujos
  173. model.F = pe.Var(model.c)
  174. model.PL = pe.Var(model.c)
  175. model.Per = pe.Var(model.n)
  176. model.F_k = pe.Var(model.c, model.k)
  177. model.Fp_k = pe.Var(model.c, model.k, within=pe.NonNegativeReals)
  178. model.Fp = pe.Var(model.c, within=pe.NonNegativeReals)
  179. model.Fn_k = pe.Var(model.c, model.k, within=pe.NonNegativeReals)
  180. model.Fn = pe.Var(model.c, within=pe.NonNegativeReals)
  181. # EQUATIONS
  182. model_logger.info("Creando Ecuaciones del problema")
  183. # Ecuación Objetivo
  184. # =============================================================================
  185. model_logger.info("Ecuación de Función Objetivo Max Z")
  186. def eq_Z(model):
  187. return sum(model.alpha_k[k]*model.C[k] - model.gamma_k[k]*model.Cper[k] for k in model.k)
  188. model.Z = pe.Objective(rule=eq_Z, sense=pe.maximize)
  189. # Restricciones
  190. # =============================================================================
  191. model_logger.info("Restricciones del Modelo de Optimización")
  192. model_logger.info("Límites de Gamma")
  193. def eq_gamma_k_rule(model, k):
  194. return model.gamma_k[k] <= model.alpha_k[k]
  195. model.eq_gamma_k = pe.Constraint(model.k, rule=eq_gamma_k_rule)
  196. # Factibilidad de derechos firmes
  197. model_logger.info("Factibilidad de derechos Firmes")
  198. def eq_F_k_rule(model, c, k):
  199. return model.F_k[c, k] == model.alpha_k[k]*sum(model.He[c, n]*model.T[n, k] for n in model.n)
  200. model.eq_F_k = pe.Constraint(model.c, model.k, rule=eq_F_k_rule)
  201. # Flujos positivos
  202. model_logger.info("Flujos positivos")
  203. def eq_Fp_k_rule(model, c, k):
  204. return model.Fp_k[c, k] >= model.F_k[c, k]
  205. model.eq_Fp_k = pe.Constraint(model.c, model.k, rule=eq_Fp_k_rule)
  206. def eq_Fp_rule(model, c):
  207. return model.Fp[c] == sum(model.Fp_k[c, k] for k in model.k)
  208. model.eq_Fp = pe.Constraint(model.c, rule=eq_Fp_rule)
  209. def eq_Fpl_rule(model, c):
  210. return sum(model.Fp_k[c, k] for k in model.k) <= model.BFu[c]
  211. model.eq_Fpl = pe.Constraint(model.c, rule=eq_Fpl_rule)
  212. # Flujos negativos
  213. model_logger.info("Flujos negativos")
  214. def eq_Fn_k_rule(model, c, k):
  215. return model.Fn_k[c, k] >= -model.F_k[c, k]
  216. model.eq_Fn_k = pe.Constraint(model.c, model.k, rule=eq_Fn_k_rule)
  217. def eq_Fn_rule(model, c):
  218. return model.Fn[c] == sum(model.Fn_k[c, k] for k in model.k)
  219. model.eq_Fn = pe.Constraint(model.c, rule=eq_Fn_rule)
  220. def eq_Fnl_rule(model, c):
  221. return sum(model.Fn_k[c, k] for k in model.k) <= model.BFl[c]
  222. model.eq_Fnl = pe.Constraint(model.c, rule=eq_Fnl_rule)
  223. # Suficiencia Financiera
  224. model_logger.info("Suficiencia Financiera")
  225. def eq_sf1_rule(model, c):
  226. return model.F[c] <= model.Bu[c]
  227. model.eq_sf1 = pe.Constraint(model.c, rule=eq_sf1_rule)
  228. def eq_sf2_rule(model, c):
  229. return model.F[c] >= -model.Bl[c]
  230. model.eq_sf2 = pe.Constraint(model.c, rule=eq_sf2_rule)
  231. # Balance de Pérdidas
  232. model_logger.info("Balance de Pérdidas")
  233. def eq_PL_rule(model, c):
  234. return model.PL[c] == model.R[c]*(model.F[c]*model.F[c])
  235. model.eq_PL = pe.Constraint(model.c, rule=eq_PL_rule)
  236. def eq_Per_rule(model, n):
  237. return model.Per[n] == sum(model.Inc[c, n]*(model.PL[c]/2) for c in model.c)
  238. model.eq_Per = pe.Constraint(model.n, rule=eq_Per_rule)
  239. # Balance de Flujos
  240. model_logger.info("Balance de Flujos")
  241. def eq_Balance_rule(model, c):
  242. return model.F[c] == sum(model.He[c, n]*(
  243. sum(model.alpha_k[k]*model.T[n, k] for k in model.k) +
  244. sum(model.TE[n, o] for o in model.o) +
  245. sum(model.gamma_k[k]*model.VITX[n, k] for k in model.k) +
  246. sum(model.VITEX[n, o]for o in model.o) -
  247. model.Per[n]) for n in model.n)
  248. model.eq_Balance = pe.Constraint(model.c, rule=eq_Balance_rule)
  249. # Perdidas en estado base
  250. model_logger.info("Perdidas en estado Base")
  251. def eq_perdidas_base_rule(model):
  252. return sum(model.PL[c] for c in model.c) == sum(
  253. sum(model.gamma_k[k]*model.VITX[n, k] for k in model.k) +
  254. sum(model.VITEX[n, o] for o in model.o) for n in model.n)
  255. model.eq_perdidas_base = pe.Constraint(rule=eq_perdidas_base_rule)
  256. # Máximas capacidades de trasferencia entre áreas de control
  257. model_logger.info(
  258. "Máxima Capacidad de Trasnferencia entre áreas de control")
  259. def eq_mct_ns_rule(model, i):
  260. return sum(model.DirF[i]*model.F[c] for c in lin_mct[i]) <= \
  261. model.Mct[i, 'ns']
  262. model.eq_mct_ns = pe.Constraint(model.i, rule=eq_mct_ns_rule)
  263. def eq_mct_sn_rule(model, i):
  264. return sum(model.DirF[i]*model.F[c] for c in lin_mct[i]) >= \
  265. -model.Mct[i, 'sn']
  266. model.eq_mct_sn = pe.Constraint(model.i, rule=eq_mct_sn_rule)
  267. model.dual = pe.Suffix(direction=pe.Suffix.IMPORT)
  268. model_logger.info("Construcción del modelo terminada.")
  269. filename = path.basename(self.file).split('.')
  270. filepath = path.dirname(self.file)
  271. try:
  272. model_logger.info("Enviando modelo algebraico al solver")
  273. opt = SolverFactory(
  274. 'ipopt',
  275. executable=self.solver_path
  276. )
  277. model_logger.info("Buscando una solución optima al problema")
  278. result = opt.solve(
  279. model, tee=True, logfile="log/solver_{}.log".format(filename[0]))
  280. model.solutions.store_to(result)
  281. result.write(filename='results.json', format='json')
  282. model_logger.info("Solucion encontrada")
  283. except Exception as e:
  284. model_logger.info("No se ejecutó el problema.")
  285. model_logger.error("Error: {}".format(e))
  286. return
  287. # Cálculo de Precios Nodales
  288. # =============================================================================
  289. model_logger.info("Calculando Precios Nodales")
  290. Sigma = zeros(nbr)
  291. for c in model.c:
  292. Sigma[c] = model.dual[model.eq_Balance[c]]
  293. Lambda = model.dual[model.eq_perdidas_base]
  294. PON = (H.T @ Sigma) + Lambda
  295. # Cálculo de Pagos por Derechos Firmes
  296. # =============================================================================
  297. model_logger.info("Calculando Pagos Asignados por Derechos Firmes")
  298. alpha = array(list(model.alpha_k.get_values().values()))
  299. psi = array(list(model.gamma_k.get_values().values()))
  300. PDF_k = zeros(nof)
  301. for i in range(0, nof):
  302. PDF_k[i] = -PON.T @ (alpha[i]*t[:, i] + psi[i]*vitx[:, i])
  303. # Construcción de array flujos en interconexiones
  304. # =============================================================================
  305. intercon = {}
  306. for i in model.i:
  307. intercon[i] = [pe.value(model.eq_mct_sn[i].lower),
  308. pe.value(model.eq_mct_ns[i].body),
  309. pe.value(model.eq_mct_ns[i].upper)]
  310. flujos_inter = pd.DataFrame.from_dict(intercon)
  311. flujos_inter = flujos_inter * 100
  312. # Construcción de array para grabar
  313. # =============================================================================
  314. filename = path.basename(self.file).split('.')
  315. filepath = path.dirname(self.file)
  316. model_logger.info(
  317. "Escribiendo resultados en carpeta {0}".format(filepath))
  318. # Resultados de Asignación
  319. resultado = of.copy()
  320. resultado['alpha'] = alpha
  321. resultado['gamma'] = psi
  322. resultado['MW_asign'] = resultado['MWo'] * resultado['alpha']
  323. resultado['per_asign'] = resultado['per'] * resultado['gamma']
  324. resultado['monto_asign'] = PDF_k.round(2)
  325. resultado['precio_mw'] = resultado['monto_asign'] / \
  326. (resultado['MW_asign'] * 24 * 31) # Se toman meses de 31 días
  327. # Flujos de Potencia
  328. flujos = net.copy()
  329. flujos = flujos.drop(['x', 'r', 'max', 'min'], axis=1)
  330. f = array(list(model.F.get_values().values()))
  331. flujos['linea'] = brnames
  332. flujos['flujo'] = f
  333. flujos['perdidas'] = array(list(model.PL.get_values().values()))
  334. flujos['sigma'] = Sigma
  335. # Precios por Nodo
  336. pon = bus.copy()
  337. pon['PON'] = PON
  338. # Grabar en excel
  339. # ================================================================================
  340. # new_filename = filename[0]+'_results.'+filename[1]
  341. #
  342. # new_file = path.join(filepath, new_filename)
  343. #
  344. # book = load_workbook(new_file)
  345. # writer = ExcelWriter(new_file, engine='openpyxl')
  346. # writer.book = book
  347. # writer.sheets = dict((ws.title, ws) for ws in book.worksheets)
  348. #
  349. # resultado.to_excel(writer, sheet_name='asignacion', index=False, header=True)
  350. #
  351. # flujos.to_excel(writer, sheet_name='flujos', index=False, header=True)
  352. #
  353. # pon.to_excel(writer, sheet_name='pon', index=False, header=True)
  354. #
  355. # writer.save()
  356. # writer.close()
  357. # Grabar en csv
  358. # =================================================================================
  359. csv_path = filepath
  360. model_logger.info("Escribiendo archivo de asignaciones")
  361. resultado.to_csv(
  362. path.join(csv_path, filename[0] + '_asignacion.csv'), sep=',', index=False)
  363. model_logger.info("Escribiendo archivo de flujos de potencia")
  364. flujos.to_csv(
  365. path.join(csv_path, filename[0] + '_flujos.csv'), sep=',', index=False)
  366. model_logger.info("Escribiendo archivo de flujos de precios")
  367. pon.to_csv(
  368. path.join(csv_path, filename[0] + '_pon.csv'), sep=',', index=False)
  369. model_logger.info(
  370. "Escribiendo archivo de flujos de potencia entre areas de control")
  371. flujos_inter.to_csv(
  372. path.join(csv_path, filename[0] + '_flujos_inter.csv'), sep=',', index=False)
  373. model_logger.info("Script Finalizado ")