
from __future__ import division

import logging
import traceback
from os import path
import sys
import pandas as pd

import pyutilib.subprocess.GlobalData
from numpy import array, zeros
from pandas import DataFrame, ExcelWriter
from pyomo.environ import *
from pyomo.environ import SolverFactory
from pyomo.kernel import value

from common.data import APPDIRS

from mct.makeMCT import linmct, readmct, set_dir_flujo
from red.orderC import nodec
from red.create import *
from red.create1 import *
from red.makeBdc import makeBdc
from red.read import excel2net
from utils.arr2dict import arr2dict

pyutilib.subprocess.GlobalData.DEFINE_SIGNAL_HANDLERS_DEFAULT = False

logger = logging.getLogger('spr.run.modelo')


def setmodel(file, p_exec, all_day=True):

    # Determinar si se ejecuta todo el día o solo un período en específico
    if all_day:
        min = 0
        max = p_exec + 1

        logger.info("Se ejecuta el Modelo de Predespacho para las 24 horas")

    else:
        min = p_exec
        max = p_exec + 1

        logger.info(
            "Se ejecuta el Modelo de Predespacho para la hora {}".format(p_exec))

    logger.info("Modelo de Predespacho Regional")
    logger.info("Mercados Eléctricos de Centroamérica (c) 2020")

    # ============================================================================
    # Importación de datos desde archivo Excel
    # ============================================================================

    # Parametros de la linea
    logger.info("Inicio Predespacho Regional")
    logger.info("Leyendo información de RTR...")
    net_t = excel2net(file)
    dirf = set_dir_flujo()
    mct = readmct(file)

    # Informacion de los despachos nacionales
    dg_t = read_D_G(file)

    # Ofertas de todos los periodos
    ex_cnfff_t = readofertas_cnfffs(file)
    ex_ooi_t = readofertas_oois(file)
    ex_oor_t = readofertas_oors(file)
    ex_cf_t = readofertas_cfs(file)

    # Dataframe para almacenar la informacion de cada predespacho
    flujos_t = DataFrame()
    iep_t = DataFrame()
    pon_t = DataFrame()
    result_foo_i_t = DataFrame()
    result_foo_r_t = DataFrame()
    result_fof_t = DataFrame()
    result_foff_t = DataFrame()

    # El range del for se modifica dependiendo los periodos que se quieren correr.
    for PERIODO in range(min, max):
        logger.info("Inicio de la ejecución para el periodo {}".format(PERIODO))

        net=net_t[net_t.periodo.isin([PERIODO])].copy()
        net.reset_index(drop=True, inplace=True)
        net.drop(['periodo'], axis='columns', inplace=True)
        bus = setbus(net)#Nodos
        branch = setbranch(net, bus)#Set lineas
        bu = branch[:, 5]#potenica max de la linea
        bl = branch[:, 6]#potenica min de la linea
        xc = branch[:,3]#Reactancia de la linea
        rc = branch[:,4]#Resistencia de la linea
        nb = bus.shape[0] #Numero de nodos
        nbr = branch.shape[0]#Numero de lineas
        A = makeBdc(bus, branch)#Matriz incidente
        brnames = branchnames(bus, branch)#Nombre de las lineas
        inc = A.toarray()*-1

        # Lineas para límites de MCT
        br_t = brnames['Total'].to_numpy()
        lin_mct = linmct(br_t)

        logger.info("Leyendo información de los despacho nacionales.")
        # Dependiendo de los valores de la columna periodo del df son los valores que toma
        dg = dg_t[dg_t.periodo.isin([PERIODO])]
        # Resetea valores de ordenamiento del DF
        dg.reset_index(drop=True, inplace=True)
        # Ordena los valores que tiene segun los nodos de rtr
        dg_n = set_dgnacional(bus, dg)

        logger.info("Leyendo información de ofertas.")
        # Funcion leer contratos no firmes fisicos flexibles
        # Dependiendo de los valores de la columna periodo del df son los valores que toma
        ex_cnfff = ex_cnfff_t[ex_cnfff_t.periodo.isin([PERIODO])]
        # Resetea valores de ordenamiento del DF
        ex_cnfff.reset_index(drop=True, inplace=True)
        # Energia declarada con respecto al nodo
        vnfff_ed = setvariable(ex_cnfff['energía_dec'])
        # Magnitud de energia ofertada -flexibilizacion
        vnfff_m_i = setvariable_s(
            ex_cnfff[['magnitud_i1', 'magnitud_i2', 'magnitud_i3', 'magnitud_i4', 'magnitud_i5']])
        vnfff_m_r = setvariable_s(
            ex_cnfff[['magnitud_r1', 'magnitud_r2', 'magnitud_r3', 'magnitud_r4', 'magnitud_r5']])
        vnfff_m_cvt = setvariable_s(
            ex_cnfff[['magnitud_cvt1', 'magnitud_cvt2', 'magnitud_cvt3', 'magnitud_cvt4', 'magnitud_cvt5']])
        # Precio de la ofertas de inyeccion
        p_cnfffi = setvariable_s(
            ex_cnfff[['precio_i1', 'precio_i2', 'precio_i3', 'precio_i4', 'precio_i5']])
        # Precio de la ofertas de retiro
        p_cnfffr = setvariable_s(
            ex_cnfff[['precio_r1', 'precio_r2', 'precio_r3', 'precio_r4', 'precio_r5']])
        # Precio de la ofertas de inyeccion
        p_cnfffcvt = setvariable_s(
            ex_cnfff[['precio_cvt1', 'precio_cvt2', 'precio_cvt3', 'precio_cvt4', 'precio_cvt5']])
        # Precio de la ofertas de inyeccion
        k_cnfffcvt = setvariable(ex_cnfff['k'])
        NCFF = ex_cnfff.shape[0]  # Numero de contratos firmes
        var_bin_cnfffr = MATRIZ_VNFFF_R(bus, ex_cnfff)
        var_bin_cnfffi = MATRIZ_VNFFF_I(bus, ex_cnfff)
        dem = setvariable(dg_n['Demanda'])
        gen = setvariable(dg_n['Generacion'])

        # Funcion leer parametros oferta de oportunidad inyeccion
        # Dependiendo de los valores de la columna periodo del df son los valores que toma
        ex_ooi = ex_ooi_t[ex_ooi_t.periodo.isin([PERIODO])]
        # Resetea valores de ordenamiento del DF
        ex_ooi.reset_index(drop=True, inplace=True)
        vooi_m = setvariable_s(ex_ooi[['magnitud_ooi1', 'magnitud_ooi2', 'magnitud_ooi3',
                                       'magnitud_ooi4', 'magnitud_ooi5']])  # Ofertas con respecto al nodo
        p_ooi = setvariable_s(ex_ooi[['precio_ooi1', 'precio_ooi2', 'precio_ooi3',
                                      'precio_ooi4', 'precio_ooi5']])  # Precio de la ofertas
        NOI = ex_ooi.shape[0]  # Numero de ofertas de inyeccion
        var_bin_ooi = MATRIZ_OOI(bus, ex_ooi)

        # Funcion leer parametros oferta de oportunidad retiro
        ex_oor = ex_oor_t[ex_oor_t.periodo.isin([PERIODO])]
        # Resetea valores de ordenamiento del DF
        ex_oor.reset_index(drop=True, inplace=True)
        voor_m = setvariable_s(ex_oor[['magnitud_oor1', 'magnitud_oor2', 'magnitud_oor3',
                                       'magnitud_oor4', 'magnitud_oor5']])  # Ofertas con respecto al nodo
        # Precio de la ofertas - flexibilizacion
        p_oor = setvariable_s(
            ex_oor[['precio_oor1', 'precio_oor2', 'precio_oor3', 'precio_oor4', 'precio_oor5']])
        NOR = ex_oor.shape[0]  # Numero de ofertas de retiro
        var_bin_oor = MATRIZ_OOR(bus, ex_oor)

        # Funcion leer parametros contratos firmes
        # Dependiendo de los valores de la columna periodo del df son los valores que toma
        ex_cf = ex_cf_t[ex_cf_t.periodo.isin([PERIODO])]
        # Resetea valores de ordenamiento del DF
        ex_cf.reset_index(drop=True, inplace=True)
        # Energia declarada con respecto al nodo
        vcf_ed = setvariable(ex_cf['energía_dec'])
        # Potencia requerida con respecto al nodo
        vcf_pr = setvariable(ex_cf['potencia_req'])
        # Magnitud de energia ofertada -flexibilizacion
        vcf_m = setvariable_s(
            ex_cf[['magnitu_cf1', 'magnitu_cf2', 'magnitu_cf3', 'magnitu_cf4', 'magnitu_cf5']])
        # Precio de la ofertas
        vcf_p = setvariable_s(
            ex_cf[['precio_cf1', 'precio_cf2', 'precio_cf3', 'precio_cf4', 'precio_cf5']])
        NCF = ex_cf.shape[0]  # Numero de contratos firmes
        var_bin_cfr = MATRIZ_CFR(bus, ex_cf)
        var_bin_cfi = MATRIZ_CFI(bus, ex_cf)

        # Inicio del modelo de optimización
        model = ConcreteModel()

        # sets
        model.i = Set(initialize=range(0, nb))  # numero de nodos
        model.c = Set(initialize=range(0, nbr))  # Numero de lineas
        # numero de ofertas de oportuniddad retiro
        model.OR = Set(initialize=range(0, NOR))
        # numero de ofertas de oportunidad inyeccion
        model.OI = Set(initialize=range(0, NOI))
        # numero de ofertas de CNFFF
        model.CFF = Set(initialize=range(0, NCFF))
        # numero de ofertas de contratos firmes
        model.CF = Set(initialize=range(0, NCF))
        model.s = Set(initialize=range(0, 5))  # Numero de bloques
        model.inter = Set(initialize=lin_mct.keys())  # Interconexiones
        # Sentidos de interconexiones
        model.sen = Set(initialize=['sn', 'ns'])
        model.per = Set(initialize=range(0, 24))  # numero de periodos

        # Parametros
        # Parametros de la red
        model.rtmw_min = Param(model.c, initialize=dict(enumerate(bl)))
        model.rtmw_max = Param(model.c, initialize=dict(enumerate(bu)))
        model.Inc = Param(model.c, model.i, initialize=arr2dict(inc))
        model.Xc = Param(model.c, initialize=dict(enumerate(xc)))
        model.Rc = Param(model.c, initialize=dict(enumerate(rc)))

        # Parametros de los predespachos nacionales
        model.D = Param(model.i, initialize=dict(enumerate(dem)))
        model.G = Param(model.i, initialize=dict(enumerate(gen)))

        # Ofertas de oportunidad
        # Oferta de oportunidad de retiro
        # Oferta bloques 1
        model.fr = Param(model.OR, model.s, initialize=arr2dict(p_oor))
        model.pr_ofertado = Param(model.OR, model.s, initialize=arr2dict(
            voor_m))  # Magnitud de la oferta MW-h
        model.bin_pr = Param(
            model.i, model.OR, initialize=arr2dict(var_bin_oor))

        # Oferta de oportunidad de inyeccion
        # Precio de bloques - Oferta de oportunidad de inyeccion
        model.fi = Param(model.OI, model.s, initialize=arr2dict(p_ooi))
        model.pi_ofertado = Param(model.OI, model.s, initialize=arr2dict(
            vooi_m))  # Magnitud de la oferta MW-h
        model.bin_pi = Param(
            model.i, model.OI, initialize=arr2dict(var_bin_ooi))

        # Contratos firmes
        model.pf_declarada = Param(model.CF, initialize=dict(
            enumerate(vcf_ed)))  # Energia declarada
        # Potencia requerida - Si no se flexbiliza deberian de ser igual la energia y la potencia
        model.pf_req = Param(model.CF, initialize=dict(enumerate(vcf_pr)))
        # Precio de flexibilidad de contrato
        # Precio de bloques - Contrato firme - Oferta de flexibilidad
        model.ffi = Param(model.CF, model.s, initialize=arr2dict(vcf_p))
        # Magnitud de la oferta - tiene que ser igual a la suma de la energia declarada
        model.pfi_ofertado = Param(
            model.CF, model.s, initialize=arr2dict(vcf_m))
        model.bin_cfi = Param(
            model.i, model.CF, initialize=arr2dict(var_bin_cfi))
        model.bin_cfr = Param(
            model.i, model.CF, initialize=arr2dict(var_bin_cfr))

        # Ofertas de flexibilidad de contratos fisicos flexibles
        # Ofertas de inyeccion
        model.pff_declarada = Param(
            model.CFF, initialize=dict(enumerate(vnfff_ed)))
        model.pffi_ofertado = Param(
            model.CFF, model.s, initialize=arr2dict(vnfff_m_i))  # Magnitud del bloque
        model.fffi = Param(model.CFF, model.s, initialize=arr2dict(
            p_cnfffi))  # Precio de inyeccion
        model.bin_pffi = Param(
            model.i, model.CFF, initialize=arr2dict(var_bin_cnfffi))

        # Oferta de retiro
        model.pffr_ofertado = Param(model.CFF, model.s, initialize=arr2dict(
            vnfff_m_r))  # Magnitud de bloque de retiro ofertado
        # Precio de bloques - Contrato no firme fisico flexible
        model.fffr = Param(model.CFF, model.s, initialize=arr2dict(p_cnfffr))
        model.bin_pffr = Param(
            model.i, model.CFF, initialize=arr2dict(var_bin_cnfffr))

        # Ofertad de pago maximo por CVT
        model.k = Param(model.CFF, initialize=dict(
            enumerate(k_cnfffcvt)))  # Indicador de oferta
        model.pfft_ofertado = Param(model.CFF, model.s, initialize=arr2dict(
            vnfff_m_cvt))  # Magnitud del bloque
        model.ffft = Param(model.CFF, model.s, initialize=arr2dict(
            p_cnfffcvt))  # Precio de pago maximo CVT

        # Variabeles
        # Variable ofertas de oportunidad
        # Parte aceptada de cada bloques de las ofertas de oportunidad de retiro
        model.pr = Var(model.OR, model.s, domain=NonNegativeReals)
        # Parte aceptada de cada bloques de las ofertas de oportunidad de inyeccion
        model.pi = Var(model.OI, model.s, domain=NonNegativeReals)

        # Variables CF
        # Parte aceptada de cada bloques de las ofertas de flexibilidad de contratos firmes
        model.pfi = Var(model.CF, model.s, domain=NonNegativeReals)

        # Variables CNFFF
        # Parte aceptada de cada bloques de las oferta de flexibilidad de retiro
        model.pffr = Var(model.CFF, model.s, domain=NonNegativeReals)
        # Parte aceptada de cada bloques de las ofertas de flexibilidad de inyección de los contratos físicos flexibles
        model.pffi = Var(model.CFF, model.s, domain=NonNegativeReals)
        # Parte aceptada de cada bloques de las oferta de pago máximo de CVT de los contratos físicos flexibles
        model.pfft = Var(model.CFF, model.s, domain=NonNegativeReals)
        # Componente fisica de energia PERIODOria de inyeecion
        model.pff_iny_fisico = Var(model.CFF, domain=NonNegativeReals)
        # Componente fisica de energia PERIODOria de retiro
        model.pff_ret_fisico = Var(model.CFF, domain=NonNegativeReals)

        # Variables FOENS
        # Energia firme de lo CF
        model.pf_cortada = Var(model.CF, domain=NonNegativeReals)
        model.pf_pre_cortada = Var(model.CF, domain=NonNegativeReals)
        # Energia firme de los CNFFF
        model.pff_cortada = Var(model.CFF, domain=NonNegativeReals)

        model.fens = Var()

        # Variable problema de optimizacion
        # Inyeccion por nodo
        model.inyeccion = Var(model.i, domain=NonNegativeReals)
        model.retiro = Var(model.i, domain=NonNegativeReals)  # Retiro por nodo
        model.ref_angular = Var(model.i)  # Fase del voltaje en el nodo
        model.rtmw_c = Var(model.c)  # Flujo de potencia actica por linea

        # Máximas Capacidades de Trasferencia
        # Maxima transferenica(interconexion,sentido,periodo)
        model.Mct = Param(model.per, model.inter, model.sen,  initialize=mct.MCT.to_dict(), default=300, mutable=True)
        model.DirF = Param(model.inter, initialize=dirf)

        logger.info("Ecuación de Función Objetivo Max.")

        def objfunc(model):
            return ((sum(model.fr[OR, s]*model.pr[OR, s] for OR in model.OR for s in model.s) -  # ┌ FOO
                     # └
                     sum(model.fi[OI, s]*model.pi[OI, s] for OI in model.OI for s in model.s) -
                     # [ FOF
                     sum(model.ffi[CF, s]*model.pfi[CF, s] for CF in model.CF for s in model.s) +
                     # ┌
                     sum(model.fffr[CFF, s]*model.pffr[CFF, s] for CFF in model.CFF for s in model.s) -
                     # │ FOFF
                     sum(model.fffi[CFF, s]*model.pffi[CFF, s] for CFF in model.CFF for s in model.s) +
                     # └
                     sum(model.ffft[CFF, s]*model.pfft[CFF, s]
                         for CFF in model.CFF for s in model.s)))  # -
            # ┌ FOENS
            #  0*model.fens*sum(model.pf_cortada[CF] for CF in model.CF) -
            #  0*model.fens*0.5*sum(model.pff_cortada[CFF] for CFF in model.CFF)))               # └
        model.OBJ = Objective(rule=objfunc, sense=maximize)

        logger.info("Restricciones del Modelo de Optimización. ")

        # Esta restricción no es utilizada y se encarga de efectuar la reducción
        # de los contratos firmes (idem FOENS línea 327)

        # def fens_restriccion(model):
        #     if NOR == 0 & NCF == 0 & NCFF > 0:
        #         return model.fens == 3*max(model.fffr[CFF, s] for CFF in model.CFF for s in model.s)
        #     elif NOR == 0 & NCF > 0 & NCFF == 0:
        #         return model.fens == 3*max(model.ffi[CF, s] for CF in model.CF for s in model.s)
        #     elif NOR == 0 & NCF > 0 & NCFF > 0:
        #         return model.fens == 3*max(max(model.ffi[CF, s] for CF in model.CF for s in model.s),
        #                                    max(model.fffr[CFF, s] for CFF in model.CFF for s in model.s))
        #     elif NOR > 0 & NCF == 0 & NCFF == 0:
        #         return model.fens == 3*max(model.fr[OR, s] for OR in model.OR for s in model.s)
        #     elif NOR > 0 & NCF == 0 & NCFF > 0:
        #         return model.fens == 3*max(max(model.fr[OR, s] for OR in model.OR for s in model.s),
        #                                    max(model.fffr[CFF, s] for CFF in model.CFF for s in model.s))
        #     elif NOR > 0 & NCF > 0 & NCFF == 0:
        #         return model.fens == 3*max(max(model.fr[OR, s] for OR in model.OR for s in model.s),
        #                                    max(model.ffi[CF, s] for CF in model.CF for s in model.s))
        #     elif NOR > 0 & NCF > 0 & NCFF > 0:
        #         return model.fens == 3*max(max(model.fr[OR, s] for OR in model.OR for s in model.s),
        #                                    max(model.fffr[CFF, s]
        #                                        for CFF in model.CFF for s in model.s),
        #                                    max(model.ffi[CF, s] for CF in model.CF for s in model.s))
        # model.fens_constraint = Constraint(rule=fens_restriccion)

        # Restrecciones FOO

        def pi_restriccion(model, OI, s):
            return ((model.pi[OI, s] <= model.pi_ofertado[OI, s]))
        model.pi_constraint = Constraint(
            model.OI, model.s, rule=pi_restriccion)

        def pr_restriccion(model, OR, s):
            return ((model.pr[OR, s] <= model.pr_ofertado[OR, s]))
        model.pr_constraint = Constraint(
            model.OR, model.s, rule=pr_restriccion)

        # Restricciones FOF
        def pfi_restriccion(model, CF, s):
            return (model.pfi[CF, s] <= model.pfi_ofertado[CF, s])
        model.pfi_constraint = Constraint(
            model.CF, model.s, rule=pfi_restriccion)

        # Restricciones FOFF
        def pffr_restriccion(model, CFF, s):
            return (model.pffr[CFF, s] <= model.pffr_ofertado[CFF, s])
        model.pffr_constraint = Constraint(
            model.CFF, model.s, rule=pffr_restriccion)

        def pffi_restriccion(model, CFF, s):
            return (model.pffi[CFF, s] <= model.pffi_ofertado[CFF, s])
        model.pffi_constraint = Constraint(
            model.CFF, model.s, rule=pffi_restriccion)

        if (model.k[CFF] == 0 for CFF in model.CFF):
            def pfft_restriccion(model, CFF, s):
                # if  model.k[CFF] ==0:
                return (model.pfft[CFF, s] <= model.pfft_ofertado[CFF, s])
            model.pfft_constraint = Constraint(
                model.CFF, model.s, rule=pfft_restriccion)

        # K(cff) vale 0 si hay oferta de pago maximo por CVT
        def pff_iny_fisico_restriccion(model, CFF):
            if model.k[CFF] == 0:
                return (model.pff_iny_fisico[CFF] == sum(model.pfft[CFF, s] for s in model.s) - sum(model.pffr[CFF, s] for s in model.s))
            elif model.k[CFF] == 1:
                return (model.pff_iny_fisico[CFF] == model.pff_declarada[CFF] - sum(model.pffr[CFF, s] for s in model.s))
        model.pff_iny_fisico_constraint = Constraint(
            model.CFF, rule=pff_iny_fisico_restriccion)

        def pff_ret_fisico_restriccion(model, CFF):
            if model.k[CFF] == 0:
                return (model.pff_ret_fisico[CFF] == sum(model.pfft[CFF, s] for s in model.s) - sum(model.pffi[CFF, s] for s in model.s))
            elif model.k[CFF] == 1:
                return (model.pff_ret_fisico[CFF] == model.pff_declarada[CFF] - model.pff_cortada[CFF] - sum(model.pffi[CFF, s] for s in model.s))
        model.pff_ret_fisico_constraint = Constraint(
            model.CFF, rule=pff_ret_fisico_restriccion)

        # Restriccion FOENS
        def pff_cortada_restriccion(model, CFF):
            return (model.pff_cortada[CFF] <= model.pff_declarada[CFF])
        model.pff_cortada_constraint = Constraint(
            model.CFF, rule=pff_cortada_restriccion)

        def pf_cortada_restriccion(model, CF):
            return (model.pf_cortada[CF] <= model.pf_req[CF])
        model.pf_cortada_constraint = Constraint(
            model.CF, rule=pf_cortada_restriccion)

        logger.info('Restricciones de transmision.')
        # Restricciones de transmision

        def inyec(model, i):
            return (model.inyeccion[i] == model.G[i] +
                    sum(model.pi[OI, s]*model.bin_pi[i, OI] for OI in model.OI for s in model.s) +
                    sum(model.pfi[CF, s]*model.bin_cfi[i, CF] for CF in model.CF for s in model.s) +
                    sum(model.pff_iny_fisico[CFF]*model.bin_pffi[i, CFF] for CFF in model.CFF))
        model.inyec_constraint = Constraint(model.i, rule=inyec)

        def retiro(model, i):
            return (model.retiro[i] == model.D[i] +
                    sum(model.pr[OR, s]*model.bin_pr[i, OR] for OR in model.OR for s in model.s) +
                    sum(model.pf_req[CF]*model.bin_cfr[i, CF] for CF in model.CF) +
                    sum(model.pff_ret_fisico[CFF]*model.bin_pffr[i, CFF] for CFF in model.CFF) -
                    0*sum(model.pf_cortada[CF]*model.bin_cfr[i, CF] for CF in model.CF) -
                    0*sum(model.pf_pre_cortada[CF]*model.bin_cfr[i, CF] for CF in model.CF))
        model.retiro_constraint = Constraint(model.i, rule=retiro)

        # Los multiplicadore o variable duales de esta restriccion son los precios nodales
        def balance_inyeccion_retiro(model, i):
            return (model.inyeccion[i] + sum(model.Inc[c, i]*model.rtmw_c[c] for c in model.c) -  # Al dividir entre 100 Rc
                    # toda la ec. se pasa a p.u.
                    0.5*sum(((model.Inc[c, i]*model.rtmw_c[c])**2)
                            * (model.Rc[c]/100) for c in model.c)
                    == model.retiro[i])
        model.balance_inyeccion_retiro_constraint = Constraint(
            model.i, rule=balance_inyeccion_retiro)

        def rtmw_min_restriccion(model, c):
            return (model.rtmw_c[c] >= -model.rtmw_min[c])
        model.rtmw_min_constraint = Constraint(
            model.c, rule=rtmw_min_restriccion)

        def rtmw_max_restriccion(model, c):
            return (model.rtmw_c[c] <= model.rtmw_max[c])
        model.rtmw_max_constraint = Constraint(
            model.c, rule=rtmw_max_restriccion)

        def flujo_potencia_actica(model, c):
            return ((model.Xc[c])*model.rtmw_c[c]+sum(model.Inc[c, i]*model.ref_angular[i] for i in model.i) == 0)
        model.flujo_potencia_actica_constraint = Constraint(
            model.c, rule=flujo_potencia_actica)

        # Máximas capacidades de trasferencia entre áreas de control
        def eq_mct_ns_rule(model, inter):
            if inter=='IMP_TOT_SAL':#Maxima exportacion de El Salvador
                return ((sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['ELSHON']))-
                (sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['GUAELS']))) <= model.Mct[PERIODO, inter, 'ns']
            
            elif inter=='EXP_TOT_GUA_NS':
                return ((sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['GUAHON'])) +
                (sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['GUAELS']))) <= model.Mct[PERIODO, inter, 'ns'] 

            elif inter=='IMP_TOT_HON':#Maxima importacion de Honduras
                return (-(sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['ELSHON'])) -
                (sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['GUAHON'])) + 
                (sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['HONNIC']))) <= model.Mct[PERIODO, inter, 'sn'] 
            
            elif inter=='IMP_TOT_NIC':#Maxima exportacion de Nicaragua
                return ((sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['NICCRI'])) -
                (sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['HONNIC']))) <= model.Mct[PERIODO, inter, 'ns']

            elif inter=='IMP_TOT_CRI':#Maxima exportacion de Costa Rica
                return ((sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['NICCRI'])) +
                (sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['CRIPAN']))) <= model.Mct[PERIODO, inter, 'ns'] 
            
            elif inter=='IMP_TOT_PAN':
                return sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['CRIPAN']) <= model.Mct[PERIODO, inter, 'ns'] 

            else:
                return (sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct[inter]))  <= model.Mct[PERIODO, inter, 'ns'] 
        model.eq_mct_ns = Constraint(model.inter, rule=eq_mct_ns_rule)

        def eq_mct_sn_rule(model, inter):
            if inter=='IMP_TOT_SAL':#Maxima importacion de El Salvador
                return ((sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['ELSHON'])) -
                (sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['GUAELS'])))  >= -model.Mct[PERIODO, inter, 'sn'] 
            
            elif inter=='EXP_TOT_GUA_NS':
                return ((sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['GUAHON'])) +
                (sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['GUAELS']))) >= -model.Mct[PERIODO, inter, 'sn'] 
            
            elif inter=='IMP_TOT_HON':#Maxima importacion de Honduras
                return (-((sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['ELSHON'])) +
                (sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['GUAHON']))) + 
                (sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['HONNIC']))) >= -model.Mct[PERIODO, inter, 'ns'] 
            
            elif inter=='IMP_TOT_NIC':#Maxima exportacion de Nicaragua
                return ((sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['NICCRI'])) -
                (sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['HONNIC']))) >= -model.Mct[PERIODO, inter, 'sn'] 

            elif inter=='IMP_TOT_CRI':#Maxima exportacion de Costa Rica
                return ((sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['NICCRI'])) +
                (sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['CRIPAN'])))>= -model.Mct[PERIODO, inter, 'sn'] 
            
            elif inter=='IMP_TOT_PAN':
                return sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct['CRIPAN'])  >= -model.Mct[PERIODO, inter, 'sn'] 

            else:
                return (sum(model.DirF[inter]*model.rtmw_c[c] for c in lin_mct[inter])) >= -model.Mct[PERIODO, inter, 'sn'] 
        model.eq_mct_sn = Constraint(model.inter, rule=eq_mct_sn_rule)

        model.dual = Suffix(direction=Suffix.IMPORT)

        print("Construcción del modelo terminada. Periodo: "+str(PERIODO))

        opt = SolverFactory('ipopt')
        #opt.options['max_iter']= 10000
        result = opt.solve(model)
        model.solutions.store_to(result)

        # Cálculo de Precios Nodales
        # =============================================================================
        print("Calculando Precios Nodales. Periodo:" + str(PERIODO))
        Sigma = zeros(nb)
        for i in model.i:
            Sigma[i] = model.dual[model.balance_inyeccion_retiro_constraint[i]]

        # Construcción de array para grabar
        # =============================================================================
        flujos=DataFrame()
        flujos['Periodo']=set_periodo(nbr,PERIODO)
        flujos['BUS I'] = brnames['BUS I']
        flujos['BUS J'] = brnames['BUS J']
        flujos['CKT'] = brnames['CKT']
        f = array(list(model.rtmw_c.get_values().values()))
        perdidas=zeros(nbr)
        for c in model.c:
            perdidas[c]=(f[c]**2)*rc[c]/100
        flujos['Flujo'] = f
        flujos['Perdidas'] = perdidas
        flujos=flujos.round({'Flujo':3,'Perdidas':3})
        flujos_t=concat([flujos_t,flujos].copy())#Guarda los resultados con los anteriores

        pon = DataFrame()
        result_inyeccion = array(list(model.inyeccion.get_values().values()))
        result_retiro = array(list(model.retiro.get_values().values()))
        pon['Periodo']=set_periodo(nb,PERIODO)
        pon['Nodo']= bus
        pon['Precio Exante'] = Sigma*-1
        pon=pon.round({'Precio Exante':2})
        pon_t=concat([pon_t,pon]).copy()#Guarda los resultados con los anteriores
        #print(pon)
        
        iep=DataFrame()
        iep['Periodo']=set_periodo(nb,PERIODO)
        iep['nodo']= bus
        iep['Inyeccion'] = result_inyeccion
        iep['Retiro'] = result_retiro
        iep=iep.round({'Inyeccion':3,'Retiro':3})
        iep_t=concat([iep_t,iep]).copy()#Guarda los resultados con los anteriores 

        result_pff_iny=array(list(model.pff_iny_fisico.get_values().values()))
        result_pff_ret=array(list(model.pff_ret_fisico.get_values().values()))
        result_pr = setvariable_p(array(list(model.pr.get_values().values())),NOR)
        result_pi = setvariable_p(array(list(model.pi.get_values().values())),NOI)
        result_pfi = setvariable_p(array(list(model.pfi.get_values().values())),NCF)
        result_pffr = setvariable_p(array(list(model.pffr.get_values().values())),NCFF)
        result_pffi = setvariable_p(array(list(model.pffi.get_values().values())),NCFF)
        result_pfft = setvariable_p(array(list(model.pfft.get_values().values())),NCFF)

        result_foo_r=DataFrame()
        result_foo_r['ID']=ex_oor['N°']
        result_foo_r['Fecha']=ex_oor['Fecha'].apply( lambda d : d.date() )
        result_foo_r['Periodo']=ex_oor['periodo']
        result_foo_r['Nodo']=ex_oor['nodo_oor']
        pais_foo_r=pd.concat([result_foo_r.set_index('Nodo'),nodec.set_index('ID_NODO')], axis=1, join='inner').reset_index()
        pex_foo_r=pd.concat([result_foo_r.set_index('Nodo'),pon.set_index('Nodo')], axis=1, join='inner').reset_index()
        result_foo_r.drop(['Nodo'], axis='columns', inplace=True)
        result_foo_r['Pais']=pais_foo_r['ID_AREA']
        result_foo_r['Nodo']=ex_oor['nodo_oor']
        result_foo_r['Precio Exante']=pex_foo_r['Precio Exante']
        result_foo_r['Agente']=ex_oor['Agente']
        result_foo_r['MW Predespachados']=result_pr[:,0] + result_pr[:,1] + result_pr[:,2]+result_pr[:,3]+result_pr[:,4]
        result_foo_r=result_foo_r.round({'MW Predespachados':3})
        result_foo_r_t=concat([result_foo_r_t,result_foo_r]).copy()#Guarda los resultados con los anteriores

        result_foo_i=DataFrame()
        result_foo_i['ID']=ex_ooi['N°']
        result_foo_i['Fecha']=ex_ooi['Fecha'].apply( lambda d : d.date() )
        result_foo_i['Periodo']=ex_ooi['periodo']
        result_foo_i['Nodo']=ex_ooi['nodo_i']
        pex_foo_i=pd.concat([result_foo_i.set_index('Nodo'),pon.set_index('Nodo')], axis=1, join='inner').reset_index()
        pais_foo_i=pd.concat([result_foo_i.set_index('Nodo'),nodec.set_index('ID_NODO')], axis=1, join='inner').reset_index()
        result_foo_i.drop(['Nodo'], axis='columns', inplace=True)
        result_foo_i['Pais']=pais_foo_i['ID_AREA']
        result_foo_i['Nodo']=ex_ooi['nodo_i']
        result_foo_i['Precio Exante']=pex_foo_i['Precio Exante']
        result_foo_i['Agente']=ex_ooi['Agente']
        result_foo_i['MW Predespachados']=result_pi[:,0]+result_pi[:,1]+result_pi[:,2]+result_pi[:,3]+result_pi[:,4]
        result_foo_i=result_foo_i.round({'MW Predespachados':3})
        result_foo_i_t=concat([result_foo_i_t,result_foo_i]).copy()#Guarda los resultados con los anteriores
        
        result_fof=DataFrame()
        result_fof['ID']=ex_cf['N°']
        result_fof['Fecha']=ex_cf['Fecha'].apply( lambda d : d.date() )
        result_fof['Periodo']=ex_cf['periodo']
        result_fof['Nodo Inyeccion']=ex_cf['nodo_cfi']
        pex_fof_i=pd.concat([result_fof.set_index('Nodo Inyeccion'),pon.set_index('Nodo')], axis=1, join='inner').reset_index()
        pais_fof_i=pd.concat([result_fof.set_index('Nodo Inyeccion'),nodec.set_index('ID_NODO')], axis=1, join='inner').reset_index()
        result_fof.drop(['Nodo Inyeccion'], axis='columns', inplace=True)
        result_fof['Pais Inyeccion']=pais_fof_i['ID_AREA']
        result_fof['Nodo Inyeccion']=ex_cf['nodo_cfi']
        result_fof['Precio Exante Inyeccion']=pex_fof_i['Precio Exante']
        result_fof['Agente Inyeccion']=ex_cf['agente_cfi']
        result_fof['Nodo Retiro']=ex_cf['nodo_cfr']
        pais_fof_r=pd.concat([result_fof.set_index('Nodo Retiro'),nodec.set_index('ID_NODO')], axis=1, join='inner').reset_index()
        pex_fof_r=pd.concat([result_fof.set_index('Nodo Retiro'),pon.set_index('Nodo')], axis=1, join='inner').reset_index()
        result_fof.drop(['Nodo Retiro'], axis='columns', inplace=True)
        result_fof['Pais Retiro']=pais_fof_r['ID_AREA']
        result_fof['Nodo Retiro']=ex_cf['nodo_cfr']
        result_fof['Precio Exante Retiro']=pex_fof_r['Precio Exante']
        result_fof['Agente Retiro']=ex_cf['agente_cfr']
        result_fof['MW Predespachados O.I. Flexibilidad']=result_pfi[:,0]+result_pfi[:,1]+result_pfi[:,2]+result_pfi[:,3]+result_pfi[:,4]
        result_fof['MW Predespachados Ret. Requerido']=ex_cf['potencia_req']
        result_fof=result_fof.round({'MW Predespachados O.I. Flexibilidad':3,'MW Predespachados Ret. Requerido':3})
        result_fof_t=concat([result_fof_t,result_fof]).copy()#Guarda los resultados con los anteriores

        result_foff=DataFrame()
        result_foff['ID']=ex_cnfff['N°']
        result_foff['Fecha']=ex_cnfff['Fecha'].apply( lambda d : d.date() )
        result_foff['Periodo']=ex_cnfff['periodo']
        result_foff['Nodo Inyeccion']=ex_cnfff['nodo_cnfffi']
        pex_foff_i=pd.concat([result_foff.set_index('Nodo Inyeccion'),pon.set_index('Nodo')], axis=1, join='inner').reset_index()
        pais_foff_i=pd.concat([result_foff.set_index('Nodo Inyeccion'),nodec.set_index('ID_NODO')], axis=1, join='inner').reset_index()
        result_foff.drop(['Nodo Inyeccion'], axis='columns', inplace=True)
        result_foff['Pais Inyeccion']=pais_foff_i['ID_AREA']
        result_foff['Nodo Inyeccion']=ex_cnfff['nodo_cnfffi']
        result_foff['Precio Exante Inyeccion']=pex_foff_i['Precio Exante']
        result_foff['Agente Inyeccion']=ex_cnfff['agente_cnfffi']
        result_foff['Nodo Retiro']=ex_cnfff['nodo_cnfffr']
        pais_foff_r=pd.concat([result_foff.set_index('Nodo Retiro'),nodec.set_index('ID_NODO')], axis=1, join='inner').reset_index()
        pex_foff_r=pd.concat([result_foff.set_index('Nodo Retiro'),pon.set_index('Nodo')], axis=1, join='inner').reset_index()
        result_foff.drop(['Nodo Retiro'], axis='columns', inplace=True)
        result_foff['Pais Retiro']=pais_foff_r['ID_AREA']
        result_foff['Nodo Retiro']=ex_cnfff['nodo_cnfffr']
        result_foff['Precio Exante Retiro']=pex_foff_r['Precio Exante']
        result_foff['Agente Retiro']=ex_cnfff['agente_cnfffr']
        result_foff['CVT']=result_foff['Precio Exante Retiro'] - result_foff['Precio Exante Inyeccion']
        result_foff['MW Predespachados CNFFF']=result_pfft[:,0]+result_pfft[:,1]+result_pfft[:,2]+result_pfft[:,3]+result_pfft[:,4]
        result_foff['MW Predespachados O.I. Flexibilidad']=result_pffi[:,0]+result_pffi[:,1]+result_pffi[:,2]+result_pffi[:,3]+result_pffi[:,4]
        result_foff['MW Predespachados O.R. Flexibilidad']=result_pffr[:,0]+result_pffr[:,1]+result_pffr[:,2]+result_pffr[:,3]+result_pffr[:,4]
        result_foff=result_foff.round({'CVT':2,'MW Predespachados CNFFF':3,'MW Predespachados O.R. Flexibilidad':3,'MW Predespachados O.I. Flexibilidad':3})
        result_foff_t=concat([result_foff_t,result_foff])#Guarda los resultados con los anteriores

    logger.info("Escribiendo resultados en carpeta.")
    filename = path.basename(file).split('.')
    filepath = path.dirname(file)

    if all_day:

        new_filename = filename[0]+'_results.'+filename[1]
        new_file = path.join(filepath, new_filename)
    else:
        new_filename = filename[0]+'_results_{}.'.format(p_exec)+filename[1]
        new_file = path.join(filepath, new_filename)

    writer=ExcelWriter(new_file)
    flujos_t.to_excel(writer,'IEP-RTR',index=False)
    iep_t.to_excel(writer,'IEP-TOTAL',index=False)
    pon_t.to_excel(writer,'PEXANTE',index=False)
    result_foo_i_t.to_excel(writer,'TOP-I',index=False)
    result_foo_r_t.to_excel(writer,'TOP-R',index=False)
    result_fof_t.to_excel(writer,'TCP-CF',index=False)
    result_foff_t.to_excel(writer,'TCP-CNFFF',index=False)
    writer.save()

    print("{:=^100}".format(""))
    print("{:^100}".format(" Script Finalizado "))
    print("{:^100}".format(" Mercados Eléctricos de Centroamérica (c) 2020 "))
    print("{:=^100}".format(""))
        
    return 0


if __name__ == "__main__":
    setmodel()  # faltan argumentos
