#include "cgraphe.h" #include "../instance/cinstance.h" #include "../instance/cinstancereduite.h" #include "viterbi.h" #include "cplan.h" #include "cdualcplexscenario.h" #include #include #include #include #include using namespace std; //using std::pair ; const euros_par_kWh CGraphe::V(powerplants2_t i) { return (*m_u)[H()+i]; } /** * perte de fuel lors d'un refuel * @param i index de la centrale * @param kmoins1 campagne en cours avant l'outage * @param k campagne de l'outage * @param x niveau de fuel avant l'outage * @return */ inline kWh perte_refuel( powerplants2_t i, campaign_t k, kWh x ) { return BO(i,(campaign_t)(k-1)) * ((Q(i,k)-(coeff_t)1)/Q(i,k)) - BO(i,k) + x / Q(i,k) ; } inline kWh fuel_depart( powerplants2_t i, campaign_t k, kWh fuel_avant_refuel ) { return BO(i,(campaign_t)(k-1)) + (fuel_avant_refuel - BO(i,k)) * (Q(i,k)/(Q(i,k)-(coeff_t)1)) ; } /** * niveaux possibles après refuel * @param i centrale * @param k campagne de l'outage * @param scourant niveau avant le refuel, auquel on a déjà ôté la perte * @param smin niveau minimum après refuel * @param smax niveau maximum après refuel */ inline void refuel(powerplants2_t i, campaign_t k, kWh scourant, kWh & smin, kWh & smax) { smin = scourant + RMIN(i, k) ; smax = std::min(scourant + RMAX(i, k), SMAX(i,k)); if (smin > smax) throw ExceptionRefuelImpossible(i,k,scourant,smin,smax,RMIN(i,k),RMAX(i,k),SMAX(i,k)) ; } ExceptionRefuelImpossible::ExceptionRefuelImpossible(powerplants2_t i, campaign_t k, kWh s, kWh smin, kWh smax, kWh rmin, kWh rmax, kWh ssmax) { std::ostringstream oss; oss << "ExceptionRefuelImpossible (i=" << (int)i << ", k=" << (int)k << ", s=" << s << ", rmin=" << rmin << ", rmax=" << rmax << ", smin=" << smin << ", smax=" << smax << ", smax(i,k)=" << ssmax << ")" ; append(oss.str()) ; } /** * calcul de la production pendant une semaine * @param i index de la centrale * @param k campagne de production * @param h semaine * @param type type du noeud de départ (TYPE_PREMIER, TYPE_OUTAGE ou TYPE_BO) * @param smin stock minimal au début de la semaine * @param smax stock maximal au début de la semaine * @param modul modulation pouvant encore être utilisée sur la campagne * @param emin production minimale sur la campagne (début de semaine : entrée, fin de semaine : sortie) * @param emax production maximale sur la campagne (début de semaine : entrée, fin de semaine : sortie) * @param smin_d stock minimal au début de la campagne * @param smax_d stock maximal au début de la campagne * @param smin_n stock minimal à la fin de la semaine (sortie) * @param smax_n stock maximal à la fin de la semaine (sortie) * @param modul_n modulation pouvant encore être utilisée sur la campagne à la fin de la semaine (sortie) * @return */ inline CIntervallesBO consommation_semaine_BO( powerplants2_t i, campaign_t k, week_t h, CIntervallesBO intervalles) { CIntervallesBO reponse ; for (timestep_t t=week2timestep(h); t * CGraphe::m_u ; std::vector CGraphe::U ; bool CGraphe::s_contraintes_cliques = false ; bool CGraphe::s_nouvelles_duales = false ; vector> CGraphe::s_dates_ref; vector> CGraphe::s_recharges_ref; /* constructeur */ CGraphe::CGraphe(powerplants2_t i) { m_i = i ; /* création du vecteur m_somme_EMAX */ prepareCreation() ; /* création des noeuds */ creerNoeuds(); /* création des arcs */ creerArcs(); /* initialisation du plan de production */ m_plan = nullptr; m_dual_dates = make_pair(vector(K(),0),vector(K(),0)); m_dual_recharges = make_pair(vector(K(),0),vector(K(),0)); } void CGraphe::init() { m_plan = nullptr; } CGraphe::~CGraphe() { if (m_plan) delete m_plan ; m_plan = NULL ; // std::for_each(m_noeuds.begin(), m_noeuds.end(), Delete()); for (CNoeud* n: m_noeuds) delete n; } /** * Prépare la création du graphe */ void CGraphe::prepareCreation() { m_somme_EMAX.resize(H()+1, (kWh)0); kWh somme = (kWh)0 ; for (week_t h=(week_t)0; hsecond.begin(); it!=ik->second.end(); it++) for( niveaux_t::iterator in=it->second.begin(); in!=it->second.end(); in++) // pour chaque noeud if (in->second!=NULL) #ifdef DEBUG_CREATION_NOEUDS { std::cout << "faisceau (centrale " << m_i << ", sem." << h << ", campagne " << ik->first << ", type " << TYPE(it->first) << ", niveau " << in->first << ") : " << m_noeuds.size() << " noeuds avant" << std::endl; std::cout.flush(); #endif faisceau(*in->second) ; #ifdef DEBUG_CREATION_NOEUDS std::cout << "faisceau (centrale " << m_i << ", sem." << h << ", campagne " << ik->first << ", type " << TYPE(it->first) << ", niveau " << in->first << ") : " << m_noeuds.size() << " noeuds après." << std::endl; } #endif // // Création du dernier noeud // creer_noeud( H(), TYPE_DERNIER, (campaign_t)-1, (kWh)0 ); } /** * création des noeuds auxquels un noeud de départ peut se brancher * * @param depart */ void CGraphe::faisceau( const CNoeud & depart ) { // début de la production week_t debut_production = depart.m_date_campaign ; // niveaux de fuel possibles au départ de la production : kWh _smax_d ; kWh _smin_d ; if (depart.m_type == TYPE_OUTAGE) { refuel( m_i, depart.m_campaign, depart.m_fuel_avant_refuel, _smin_d, _smax_d) ; } else { // TYPE_BO et TYPE_PREMIER _smin_d = _smax_d = depart.m_fuel ; } if (depart.m_type == TYPE_BO) { faisceau_suite_BO( depart, debut_production, CIntervallesBO((kWh)-1,_smin_d, _smax_d) ) ; } else if (depart.m_type == TYPE_PREMIER) { faisceau_suite_modul( depart, (week_t) 1, _smin_d, _smax_d ) ; } else { // depart.m_type == TYPE_OUTAGE faisceau_suite_modul( depart, debut_production, _smin_d, _smax_d ) ; } } /** * faisceau_suite_BO * @param depart * @param h * @param smin * @param smax */ void CGraphe::faisceau_suite_BO( const CNoeud & depart, week_t h, CIntervallesBO limits ) { // production de la semaine // on évite les croisements, même temporaires, pour que les arcs soient gérables dans le filtre // aval avec la méthode CType2::back_level limits = consommation_semaine_BO( m_i, depart.m_campaign, h, limits ); // création éventuelle de noeuds ++h ; // test sur la date if (h>=H()) return ; noeuds_eventuels_outage( depart, h, (kWh)0, limits.f0 ) ; noeuds_eventuels_outage( depart, h, limits.f1, limits.f2 ) ; // suite faisceau_suite_BO( depart, h, limits ) ; } #define _EMAX(h0,h1) (m_somme_EMAX[h1]-m_somme_EMAX[h0]) /** * faisceau_suite_modul * @param depart * @param h * @param smin_d * @param smax_d */ void CGraphe::faisceau_suite_modul( const CNoeud & depart, week_t h, kWh smin_d, kWh smax_d ) { if (h>H()) return ; // contrainte 13bis if ((campaign_t) (depart.m_campaign+1)TA(m_i,(campaign_t)(depart.m_campaign+1))) return ; // stock restant min et max : kWh smin = smin_d - _EMAX(depart.m_date_campaign, h) ; kWh smax = smax_d - std::max( (kWh) 0, _EMAX(depart.m_date_campaign, h) - MMAX(m_i, depart.m_campaign) ) ; #ifdef DEBUG_CREATION_NOEUDS std::cout << "\tfaisceau h=" << h << "\tsmin=" << smin << "\tsmax=" << smax << "\tEMAX=" << _EMAX(h+(wlen_t)(-1), h) << std::endl ; #endif // création de noeuds de type BO if (smin=H() or smax<=BO(m_i,depart.m_campaign)) return ; // création de noeuds de type Outage noeuds_eventuels_outage( depart, h, std::max(smin, sup(BO(m_i,depart.m_campaign))), smax ) ; // suite faisceau_suite_modul( depart, ++h, smin_d, smax_d ) ; } void CGraphe::noeuds_eventuels_outage( const CNoeud & depart, week_t h, kWh smin, kWh smax ) { kWh _smax_avant_refuel ; kWh _smax_depart ; kWh _smax1 ; kWh _smax2 ; kWh _smax ; // création de noeuds de type OUTAGE for (vector::iterator kit=m_campaigns[h].begin(); kit!=m_campaigns[h].end(); kit++) { if ( *kit #ifdef SAUT_CAMPAGNE_AUTORISE > #else == 1 + #endif depart.m_campaign ) // l'indice de campagne k doit croître strictement d'un outage à l'autre { _smax_avant_refuel = SMAX(m_i,*kit)-RMIN(m_i,*kit) ; _smax_depart = fuel_depart(m_i, *kit, _smax_avant_refuel); _smax1 = smax > AMAX(m_i,*kit) ? AMAX(m_i,*kit) : smax; _smax2 = _smax1 > _smax_depart ? _smax_depart : _smax1 ; _smax = _smax2 ; // std::cout << _smax_avant_refuel << smax << _smax1 << _smax2 << _smax << std::endl ; //(type==TYPE_OUTAGE ? fuel - perte_refuel(m_i,k,fuel) : fuel) creer_noeuds_intervalle( h, TYPE_OUTAGE, *kit, smin, _smax, smin==(kWh)0 or smin==sup(BO(m_i,depart.m_campaign)), _smax==(kWh)0 or _smax==AMAX(m_i,*kit) or _smax==_smax_depart); } } } /** * Création éventuelle de noeuds une semaine donnée. * La création dépend des bornes du fuel de la semaine courante et précédente (pour les noeuds BO) * et des dates d'outage autorisées (pour les noeuds OUTAGE) * @param depart noeud de départ * @param h semaine courante * @param smin_p min fuel de la semaine précédente * @param smax_p max fuel de la semaine précédente * @param smin min fuel de la semaine courante * @param smax max fuel de la semaine courante */ void CGraphe::noeuds_eventuels_BO( const CNoeud & depart, week_t h, kWh smin, kWh smax ) { if (smax= m_min) and (x <= m_max);} }; struct PairDansIntervalle { kWh m_min, m_max; PairDansIntervalle(kWh min, kWh max) {m_min=min; m_max=max;} bool operator()(const pair & x) {return (x.first >= m_min) and (x.first <= m_max);} }; struct HorsIntervalle { kWh m_min, m_max; HorsIntervalle(kWh min, kWh max) {m_min=min; m_max=max;} bool operator()(const kWh & x) {return (x > m_min) or (x > m_max);} }; template T1 First( const pair & p ) { return p.first; } /** * Création de noeuds une semaine donnée à l'intérieur d'un intervalle donné de fuel * @param h semaine de la création * @param type type des noeuds à créer * @param k campagne de production des noeuds à créer * @param min fuel min * @param max fuel max * @param force_min forcer la création d'un noeud au niveau de fuel min * @param force_max forcer la création d'un noeud au niveau de fuel max */ void CGraphe::creer_noeuds_intervalle( week_t h, noeud_t type, campaign_t k, kWh min, kWh max, bool force_min, bool force_max) { if (min>max) return ; // création des noeuds imposés if (force_min) creer_noeud( h, type, k, min ); if (force_max) creer_noeud( h, type, k, max ); // création d'un noeud entre min et max si aucun des deux n'est imposé et qu'il n'en existe aucun if (not force_min and not force_max and count_if(m_map_noeuds[h][k][type].begin(), m_map_noeuds[h][k][type].end(), PairDansIntervalle(min,max))==0) creer_noeud( h, type, k, (min+max)/(coeff_t)2 ); // liste des niveaux existants compris entre min et max vector niveaux_existants ; for (std::map::iterator it=m_map_noeuds[h][k][type].begin(); it!=m_map_noeuds[h][k][type].end(); it++) { if (DansIntervalle(min,max)(it->first)) niveaux_existants.push_back(it->first) ; } // création de noeuds intermédiaires entre min et le premier existant kWh premier = niveaux_existants.front() ; for (premier -= pavage(h); premier>=min; premier -= pavage(h)) { creer_noeud( h, type, k, premier ) ; } // création de noeuds intermédiaires entre le dernier existant et max kWh dernier = niveaux_existants.back() ; for (dernier += pavage(h); dernier<=max; dernier += pavage(h)) { creer_noeud( h, type, k, dernier ) ; } // création de noeuds intermédiaires entre les noeuds existants if (niveaux_existants.size()>=2) { premier = niveaux_existants.front() ; for ( vector::iterator it = niveaux_existants.begin()+1; it!=niveaux_existants.end(); it++ ) { kWh second = *it ; coeff_t nb_noeuds = (coeff_t) floor(((second-premier) / pavage(h)).v) ; kWh pas = (second-premier) / (nb_noeuds+(coeff_t)1) ; for ( premier+=pas; premierm_date << "\tk=" << noeud_ptr->m_campaign << "\t" << TYPE(noeud_ptr->m_type) << "\ts=" << noeud_ptr->m_fuel << "\ts'=" << noeud_ptr->m_fuel_avant_refuel << std::endl ; } #endif // initialisation de la liste des arcs m_arcs.resize(0,CArc(NULL,-1,NULL,-1)); // parcours des noeuds de départ #ifdef VERBOSE_CREATION_ARCS size_t nb_arcs_precedent = 0 ; #endif for (std::vector::const_iterator dep=m_noeuds.begin(); dep!=m_noeuds.end() and (*dep)->m_type!=TYPE_DERNIER; dep++) { #ifdef VERBOSE_CREATION_ARCS /* if ( completion != (dep * 100) / m_noeuds.size() ) { completion = (dep * 100) / m_noeuds.size() ;*/ std::cout << "Parcours des " << m_noeuds.size() << " noeuds :\t" << dep - m_noeuds.begin() << " (avant : " << m_arcs.size() << " arcs : + " << m_arcs.size() - nb_arcs_precedent << ")" << std::endl ; // } if (m_arcs.size()==nb_arcs_precedent and dep!=m_noeuds.begin()) // n'est plus une erreur avec la contrainte 13bis std::cout << string("\tAucun arc créé partant du noeud précédent") << std::endl; nb_arcs_precedent = m_arcs.size() ; #endif CNoeud & depart = **dep; // niveaux de fuel possibles au départ de la production kWh _smax_d ; kWh _smin_d ; if (depart.m_type == TYPE_OUTAGE) { refuel( m_i, depart.m_campaign, depart.m_fuel_avant_refuel, _smin_d, _smax_d) ; } else { // TYPE_BO et TYPE_PREMIER _smin_d = _smax_d = depart.m_fuel ; } if (depart.m_type == TYPE_BO) { CIntervallesBO limits((kWh)-1, _smin_d, _smax_d ) ; // semaine du noeud d'arrivée courant week_t _semaine_atteinte = depart.m_date_campaign ; for (std::vector::const_iterator arr=dep+1; arr!=m_noeuds.end() and (*arr)->m_type!=TYPE_DERNIER; arr++) { CNoeud & arrivee = **arr; // vérification des types et des numéros de campagne if ( (arrivee.m_type!=TYPE_OUTAGE) or not (arrivee.m_campaign #ifdef SAUT_CAMPAGNE_AUTORISE > #else == 1 + #endif depart.m_campaign) ) continue ; // on ne branche pas un noeud BO vers un noeud de fuel>=BO if (arrivee.m_fuel>=BO(m_i,depart.m_campaign)) continue ; // productions possibles sur la période : for (week_t h = _semaine_atteinte ; h < arrivee.m_date; ++ h ) { limits = consommation_semaine_BO( m_i, depart.m_campaign, h, limits ); } _semaine_atteinte = arrivee.m_date ; // ajout de l'arc, s'il est réalisable if ( limits.contient(arrivee.m_fuel) ) { // vérification m_arcs.push_back( CArc( *dep, dep-m_noeuds.begin(), *arr, arr-m_noeuds.begin() ) ) ; } } // dernier_noeud if ( // contrainte 13bis (depart.m_campaign+1==K() or (depart.m_campaign+1::const_iterator arr=dep+1; arr!=m_noeuds.end() and (*arr)->m_type!=TYPE_DERNIER; arr++) { CNoeud & arrivee = **arr; // vérification de la date d'arrivée if ( depart.m_date_campaign>arrivee.m_date ) continue ; // vérification du stock d'arrivée if ( arrivee.m_fuel #else == 1 + #endif depart.m_campaign)) ) continue ; // stocks possibles à l'arrivée : kWh _smin = _smin_d - _EMAX(depart.m_date_campaign, arrivee.m_date) ; kWh _smax = _smax_d - std::max( (kWh) 0, _EMAX(depart.m_date_campaign, arrivee.m_date) - MMAX(m_i, depart.m_campaign) ) ; // arrêt du parcours des noeuds d'arrivée si on est passé sous BO if (_smax < sup(BO(m_i,depart.m_campaign))) break ; // ajout de l'arc, s'il est réalisable if (_smax >= arrivee.m_fuel and _smin <= arrivee.m_fuel) { m_arcs.push_back( CArc(*dep, dep-m_noeuds.begin(), *arr, arr-m_noeuds.begin()) ) ; } } // dernier noeud kWh _smax = _smax_d - std::max( (kWh) 0, _EMAX(std::min(H(),depart.m_date_campaign), H()) - MMAX(m_i, depart.m_campaign) ) ; if (_smax>=sup(BO(m_i,depart.m_campaign)) and // contrainte 13bis (depart.m_campaign+1==K() or (depart.m_campaign+1::const_iterator arr=dep+1; arr!=m_noeuds.end() and (*arr)->m_type!=TYPE_DERNIER; arr++) { CNoeud & arrivee = **arr; // vérification du stock d'arrivée if ( arrivee.m_date>0 and arrivee.m_fuel #else == 1 + #endif depart.m_campaign)) ) continue ; // stocks possibles à l'arrivée : kWh _smin = _smin_d - _EMAX(depart.m_date_campaign, arrivee.m_date) ; kWh _smax = _smax_d - std::max( (kWh) 0, _EMAX(depart.m_date_campaign, arrivee.m_date) - MMAX(m_i, depart.m_campaign) ) ; // arrêt du parcours des noeuds d'arrivée si on est passé sous BO if (_smax < sup(BO(m_i,depart.m_campaign)) and arrivee.m_date>0) break ; // ajout de l'arc, s'il est réalisable if (_smax >= arrivee.m_fuel and _smin <= arrivee.m_fuel) { m_arcs.push_back( CArc(*dep, dep-m_noeuds.begin(), *arr, arr-m_noeuds.begin()) ) ; } } // dernier noeud kWh _smax = _smax_d - std::max( (kWh) 0, _EMAX(std::min(H(),depart.m_date_campaign), H()) - MMAX(m_i, depart.m_campaign) ) ; if (_smax>=sup(BO(m_i,depart.m_campaign)) and // contrainte 13bis (depart.m_campaign+1==K() or (depart.m_campaign+1m_type)<<","<m_date<<")" << " vers " << a.ivers << " ("<m_type)<<","<m_date<<")" << std::endl; } std::cout << "********* fin dump *********" << std::endl << std::endl ; } void CGraphe::creer_colonnes( CGraphe * graphe ) { graphe->creer_colonne() ; } void CGraphe::collecte_duales_dates(CDualCplexScenario* cplex) { m_dual_dates = cplex->getDualsDates(m_i); } void CGraphe::collecte_duales_recharges(CDualCplexScenario* cplex) { m_dual_recharges = cplex->getDualsRecharges(m_i); } void CGraphe::creer_colonne() { if (s_nouvelles_duales) { // création de somme_ui_EMAX for (campaign_t k = (campaign_t)(-1); k >( K()+2, vector(H()+1, (euros)0 ) ); m_somme_ui_EMAX_cout_positif = vector >( K()+2, vector(H()+1, (euros)0 ) ); m_somme_EMAX_cout_positif = vector >( K()+2, vector (H()+1, (kWh) 0 ) ); for (campaign_t k = (campaign_t)(-1); k=0) { Cur += euros_par_kWh(m_dual_recharges.first[k] - m_dual_recharges.second[k]); } } else Cur = CiTp1(m_i); m_somme_ui_EMAX_cout_positif[k+1].resize( H() + 1 ); m_somme_EMAX_cout_positif [k+1].resize( H() + 1 ); m_somme_ui_EMAX_cout_positif[k+1][0] = (euros)0 ; m_somme_EMAX_cout_positif [k+1][0] = (kWh)0 ; euros somme_ui_EMAX = (euros)0 ; euros somme_ui_pos_EMAX = (euros) 0 ; kWh somme_EMAX_pos = (kWh) 0 ; for ( week_t h = ( week_t ) 1; h <= H(); ++h ) { somme_ui_EMAX += u(h) * EMAX (m_i, h); if (k>=0 and k= (euros_par_kWh) 0) { somme_ui_pos_EMAX += u( h - (wlen_t) 1 ) * EMAX ( m_i, h - (wlen_t)1 ) ; somme_EMAX_pos += EMAX ( m_i, h - (wlen_t)1 ) ; } m_somme_ui_EMAX_cout_positif[k+1][h] = somme_ui_pos_EMAX ; m_somme_EMAX_cout_positif [k+1][h] = somme_EMAX_pos ; } } #endif } if (s_nouvelles_duales or not s_contraintes_cliques or is_contraintes_dernier_plan_augmentees()) { /* calculer les coûts de chaque arc */ calcule_couts_des_arcs(); // for (unsigned int i=0; im_i==m_i) // std::cout << CPlan::getPlan(i)->str_couts_arcs(this) << std::endl; /* viterbi pour extraire le meilleur chemin */ viterbise(); /* création d'une nouvelle colonne en fonction du meilleur chemin */ // plan de production plan_de_production_apres_viterbi(); } else std::cout << "pas de recalcul du meilleur chemin pour la centrale " << m_i << std::endl ; } /** * calcule_couts_des_arcs */ // création d'un vecteur ordonné des u_i bool ordreUi(const Ui & a, const Ui & b) { return a.u < b.u ; } void CGraphe::prepare_viterbi(const std::vector< euros_par_kWh >* u, bool nouvelles_duales, bool contraintes_cliques) { s_contraintes_cliques = contraintes_cliques ; s_nouvelles_duales = nouvelles_duales ; if (nouvelles_duales) { // conservation d'une référence à ui,vi,xi_recharge m_u = u ; // création du vecteur ordonné des Ui U.resize( H() ); for (week_t w; wat(w), w); std::sort(U.begin(), U.end(), ordreUi); } } void CGraphe::calcule_couts_des_arcs( ) { //#define DEBUG_COUT_NOEUDS #ifdef DEBUG_COUT_NOEUDS std::cout << "centrale n°" << m_i << "\tcout nœuds "; #endif for (size_t noeud=0; noeudm_type == TYPE_OUTAGE) std::cout << m_noeuds[noeud]->m_date << ":" << m_noeuds[noeud]->m_cout_contraintes << " "; #endif } #ifdef DEBUG_COUT_NOEUDS std::cout << std::endl; #endif if (s_nouvelles_duales) for (size_t arc=0; arc( arc ); m_cout[arc] += euros(m_arcs[arc].m_de->m_cout_contraintes); #ifdef DEBUG_COUT_ARCS std::cout << " = " << m_cout[arc].contraintes << ", " << m_cout[arc].prix << std::endl ; #endif } else for (size_t arc=0; arcm_type != TYPE_OUTAGE) { noeud->m_cout_contraintes = 0; } else { noeud->m_cout_contraintes = CConstraint::cout_noeud_contraintes_14_a_21(m_i, noeud).v; double xi_dates = m_dual_dates.first[noeud->m_campaign] - m_dual_dates.second[noeud->m_campaign]; noeud->m_cout_contraintes += xi_dates * ( noeud->m_date_outage - get_date_ref(m_i, noeud->m_campaign)); // noeud->m_cout_contraintes -= (get_date_ref(m_i, noeud->m_campaign) - noeud->m_date_outage) * m_dual_dates.first[noeud->m_campaign] ; //modified + en - // noeud->m_cout_contraintes += (get_date_ref(m_i, noeud->m_campaign) - noeud->m_date_outage) * m_dual_dates.second[noeud->m_campaign] ; //modified - en + /* // if (noeud->m_date_outage < get_date_ref(m_i, noeud->m_campaign)) { //noeud->m_cout_contraintes += (get_date_ref(m_i, noeud->m_campaign) - noeud->m_date_outage) * type2(m_i).Cpre(noeud->m_campaign).v; std::cout << "décalage " << get_date_ref(m_i, noeud->m_campaign) << " - " << noeud->m_date_outage << " (semaines) + ==> coût réel : " << (get_date_ref(m_i, noeud->m_campaign) - noeud->m_date_outage) * type2(m_i).Cpre(noeud->m_campaign).v << ", coût dual : " << (get_date_ref(m_i, noeud->m_campaign) - noeud->m_date_outage) * m_dual_dates.first[noeud->m_campaign] << std::endl; // } // else { // noeud->m_cout_contraintes -= (get_date_ref(m_i, noeud->m_campaign) - noeud->m_date_outage) * type2(m_i).Cpost(noeud->m_campaign).v; std::cout << "décalage " << get_date_ref(m_i, noeud->m_campaign) << " - " << noeud->m_date_outage << " (semaines) - ==> coût réel : " << - (get_date_ref(m_i, noeud->m_campaign) - noeud->m_date_outage) * type2(m_i).Cpost(noeud->m_campaign).v << ", coût dual : " << - (get_date_ref(m_i, noeud->m_campaign) - noeud->m_date_outage) * m_dual_dates.second[noeud->m_campaign] << std::endl; // } */ } } bool CGraphe::is_contraintes_dernier_plan_augmentees() const { if (not m_plan) return true ; for (std::vector::const_iterator it_arc=m_plan->m_arcs.begin(); it_arc!=m_plan->m_arcs.end(); it_arc++) { const CNoeud * noeud = it_arc->m_depart ; if (noeud->m_type==TYPE_OUTAGE and noeud->m_cout_contraintes != CConstraint::cout_blocage_contraintes_13_a_21(m_i, noeud)) return true ; } return false ; } /** * appel à Viterbi */ void CGraphe::viterbise() { m_meilleur_chemin = Viterbi().minimise(m_premier_arc, m_noeud_fin, m_cout, &m_cout_minimal); } /** * plan_de_production_apres_viterbi */ void CGraphe::plan_de_production_apres_viterbi() { // création d'un plan de production vide if (m_plan) delete m_plan ; // m_plan vaut NULL si le plan a été accepté quelque part m_plan = new CPlan(m_i, H(),I()); // appel à la fonction calcule_cout_arc pour les campagnes de production m_cout_minimal = cout_t() ; for (std::list::const_iterator it_arc = m_meilleur_chemin.begin(); it_arc!=m_meilleur_chemin.end(); it_arc++) { calcule_cout_arc(*it_arc); } } /** * * calcul du coût d'un arc * effet de bord : remplit le plan de production m_plan du graphe * */ struct EstDans{ EstDans(size_t i, size_t j) {m_i=i; m_j=j;} bool operator()(const Ui & ui) { return (size_t)ui.i>=m_i and (size_t)ui.i cout_t CGraphe::calcule_cout_arc( size_t arc_idx ) { // noeud de départ const CNoeud & depart = *m_arcs[arc_idx].m_de ; const CNoeud & arrivee = *m_arcs[arc_idx].m_vers ; // euros_par_kWh xi_recharge(0); euros cout_recharge_ref(0); euros_par_kWh Cur(0); if (depart.m_campaign != -1) { xi_recharge = euros_par_kWh(m_dual_recharges.first[depart.m_campaign.v] - m_dual_recharges.second[depart.m_campaign.v]); cout_recharge_ref = euros(xi_recharge * get_recharge_ref(m_i,depart.m_campaign)); Cur = C(m_i, depart.m_campaign); } euros _ui_prod = (euros) 0 ; kWh _r = (kWh)0; // recharge kWh _fuel = depart.m_fuel_avant_refuel ; /**************************************/ /* cas facile du type BO */ /**************************************/ if ( depart.m_type == TYPE_BO ) { // facile : la production est imposée for (week_t h=depart.m_date_campaign; hset_prod(h,(kWh)0); else { kWh prod = PB( m_i, depart.m_campaign, _fuel, h, (wlen_t) 1 ); _fuel -= prod ; _ui_prod += u(h) * prod ; // cout reduit <- (-somme(u_h*p_h)) if (enregistrer_plan) { m_plan->set_prod(h,prod) ; } } } if (enregistrer_plan) { m_plan->push_back(arc_idx, depart, arrivee, _r, arrivee.m_type!=TYPE_DERNIER ? arrivee.m_fuel : _fuel, _ui_prod, Cur *_r); // std::cout << "nœud: " << std::setw(11)<< depart.m_cout_contraintes << ", arc " << arc_idx << ": " << std::setw(11) << (Cur*_r - _ui_prod) << ", viterbi: " << m_cout[arc_idx] << std::endl; } return cout_t( Cur * _r - _ui_prod ) ; } /*****************************************************/ /* cas des campagnes de productions non BO */ /*****************************************************/ if (arrivee.m_type!=TYPE_DERNIER) if ( arrivee.m_fuel < BO(m_i, depart.m_campaign) and not (depart.m_type==TYPE_PREMIER and arrivee.m_date==0) ) throw string("Arrivée de l'arc avec un fuel inférieur à BO"); // stock min et max kWh _smin_d, _smax_d ; if (depart.m_type == TYPE_OUTAGE) { refuel( m_i, depart.m_campaign, depart.m_fuel_avant_refuel, _smin_d, _smax_d) ; } else { // TYPE_PREMIER _smin_d = _smax_d = depart.m_fuel ; } if (enregistrer_plan) { if (depart.m_type == TYPE_OUTAGE) { for (week_t h=depart.m_date; hset_prod(h,(kWh)0) ; } } /*****************************************************/ /* cas des campagnes de productions non finales */ /*****************************************************/ // calcul des productions et du coût if ( arrivee.m_type != TYPE_DERNIER ) { // prod min et max sur la durée kWh _emax = _EMAX(depart.m_date_campaign, arrivee.m_date) ; // prod min et max pour l'arc kWh _emax_arc = _smax_d - arrivee.m_fuel ; // forcément positif car l'arc a été créé kWh _emin_arc = std::max( (kWh) 0, _smin_d - arrivee.m_fuel ) ; // modulation min et max kWh _mmax = std::min(MMAX(m_i,depart.m_campaign), _emax - _emin_arc) ; kWh _mmin = std::max((kWh)0, _emax - _emax_arc) ; // somme(u(h)*EMAX(h) - xi*EMAX(h)) : euros _somme_ui_EMAX = somme_ui_EMAX(depart.m_campaign, depart.m_date_campaign, arrivee.m_date) ; // energie disponible sur les périodes de cout réduit positif kWh _e_cout_reduit_positif = somme_EMAX_cout_positif(depart.m_campaign, depart.m_date_campaign, arrivee.m_date) ; // coefficient à appliquer pour rammener cette énergie à _mmax coeff_t _coeff_cout_reduit_positif = (_e_cout_reduit_positif==(kWh) 0 ? (coeff_t)1 : std::min((coeff_t) 1, _mmax / _e_cout_reduit_positif)) ; // coût réduit de la modulation sur les périodes de coût réduit positif euros _cout_reduit_modul_positif = somme_ui_EMAX_cout_positif(depart.m_campaign, depart.m_date_campaign, arrivee.m_date) * _coeff_cout_reduit_positif; // modulation restante à répartir sur les périodes de coût réduit négatif kWh _modul_restante = std::max((kWh)0, _mmin - _e_cout_reduit_positif); // energie disponible sur les périodes de cout réduit négatif kWh _e_cout_reduit_negatif = _emax - _e_cout_reduit_positif ; // coefficient à appliquer sur ces périodes pour atteindre la modulation restante coeff_t _coeff_cout_reduit_negatif = (_e_cout_reduit_negatif==(kWh) 0 ? (coeff_t)1 : _modul_restante / _e_cout_reduit_negatif) ; // coût réduit disponible sur les périodes de coût réduit négatif euros _cout_reduit_dispo_negatif = _somme_ui_EMAX - somme_ui_EMAX_cout_positif(depart.m_campaign, depart.m_date_campaign, arrivee.m_date) ; // coût réduit de la modulation sur les périodes de coût réduit négatif euros _cout_reduit_modul_negatif = _cout_reduit_dispo_negatif * _coeff_cout_reduit_negatif ; // production sur l'arc kWh _prod = _e_cout_reduit_positif * ((coeff_t)1-_coeff_cout_reduit_positif) + _e_cout_reduit_negatif * ((coeff_t)1-_coeff_cout_reduit_negatif) ; // recharge _r = arrivee.m_fuel + _prod - depart.m_fuel_avant_refuel ; // coût réduit de l'arc _ui_prod = (_somme_ui_EMAX - (_cout_reduit_modul_positif + _cout_reduit_modul_negatif) ); if (enregistrer_plan) { for (week_t h=depart.m_date_campaign; h= (euros_par_kWh) 0 ? _coeff_cout_reduit_positif_tp1 : Cur + xi_recharge - (*m_u)[h] >= (euros_par_kWh) 0 ? _coeff_cout_reduit_positif_cur : _coeff_cout_reduit_negatif ); m_plan->set_prod ( h, EMAX ( m_i, h ) * ( coeff ) ) ; // std::cout << h // << ":k="<< depart.m_campaign // << ":ref=" << get_recharge_ref(m_i,depart.m_campaign) // << ",cur=" << Cur // << ",xi=" << xi_recharge << ":p=" << EMAX(m_i, h) * coeff << std::endl; } m_plan->push_back ( arc_idx, depart, arrivee, _r, _revente, _ui_prod - cout_recharge_ref, Cur*_r ); // std::cout << "nœud: " << std::setw(11)<< depart.m_cout_contraintes << ", arc " << arc_idx << ": " << std::setw(11) << (Cur*_r - _ui_prod - C_tp1 * _revente) << ", viterbi: " << m_cout[arc_idx] << std::endl; } return cout_t( Cur *_r - C_tp1 * _revente - _ui_prod + cout_recharge_ref); }