leveled.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002   file : $URL: https://frepple.svn.sourceforge.net/svnroot/frepple/trunk/src/model/leveled.cpp $
00003   version : $LastChangedRevision: 635 $  $LastChangedBy: jdetaeye $
00004   date : $LastChangedDate: 2007-12-30 15:20:00 +0100 (Sun, 30 Dec 2007) $
00005  ***************************************************************************/
00006 
00007 /***************************************************************************
00008  *                                                                         *
00009  * Copyright (C) 2007 by Johan De Taeye                                    *
00010  *                                                                         *
00011  * This library is free software; you can redistribute it and/or modify it *
00012  * under the terms of the GNU Lesser General Public License as published   *
00013  * by the Free Software Foundation; either version 2.1 of the License, or  *
00014  * (at your option) any later version.                                     *
00015  *                                                                         *
00016  * This library is distributed in the hope that it will be useful,         *
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser *
00019  * General Public License for more details.                                *
00020  *                                                                         *
00021  * You should have received a copy of the GNU Lesser General Public        *
00022  * License along with this library; if not, write to the Free Software     *
00023  * Foundation Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA *
00024  *                                                                         *
00025  ***************************************************************************/
00026 
00027 
00028 #define FREPPLE_CORE
00029 #include "frepple/model.h"
00030 #include <climits>
00031 
00032 
00033 // Uncomment the following line to create debugging statements during the
00034 // clustering and leveling algorithm.
00035 //#define CLUSTERDEBUG
00036 
00037 
00038 namespace frepple
00039 {
00040 
00041 
00042 DECLARE_EXPORT bool HasLevel::recomputeLevels = false;
00043 DECLARE_EXPORT bool HasLevel::computationBusy = false;
00044 DECLARE_EXPORT short unsigned HasLevel::numberOfClusters = 0;
00045 DECLARE_EXPORT short unsigned HasLevel::numberOfHangingClusters = 0;
00046 
00047 
00048 DECLARE_EXPORT void HasLevel::computeLevels()
00049 {
00050   computationBusy = true;
00051   // Get exclusive access to this function in a multi-threaded environment.
00052   static Mutex levelcomputationbusy;
00053   ScopeMutexLock l(levelcomputationbusy);
00054 
00055   // Another thread may already have computed the levels while this thread was
00056   // waiting for the lock. In that case the while loop will be skipped.
00057   while (recomputeLevels)
00058   {
00059     // Reset the recomputation flag. Note that during the computation the flag
00060     // could be switched on again by some model change in a different thread.
00061     // In that case, the while loop will be rerun.
00062     recomputeLevels = false;
00063 
00064     // Reset current levels on buffers, resources and operations
00065     for (Buffer::iterator gbuf = Buffer::begin();
00066         gbuf != Buffer::end(); ++gbuf)
00067     {
00068       gbuf->cluster = 0;
00069       gbuf->lvl = -1;
00070     }
00071     for (Resource::iterator gres = Resource::begin();
00072         gres != Resource::end(); ++gres)
00073     {
00074       gres->cluster = 0;
00075       gres->lvl = -1;
00076     }
00077     for (Operation::iterator gop = Operation::begin();
00078         gop != Operation::end(); ++gop)
00079     {
00080       gop->cluster = 0;
00081       gop->lvl = -1;
00082     }
00083 
00084     // Loop through all operations
00085     stack< pair<Operation*,int> > stack;
00086     Operation* cur_oper;
00087     int cur_level;
00088     Buffer *cur_buf;
00089     const Flow* cur_Flow;
00090     bool search_level;
00091     int cur_cluster;
00092     numberOfClusters = 0;
00093     numberOfHangingClusters = 0;
00094     for (Operation::iterator g = Operation::begin();
00095         g != Operation::end(); ++g)
00096     {
00097 
00098 #ifdef CLUSTERDEBUG
00099       logger << "Investigating operation '" << &*g
00100       << "' - current cluster " << g->cluster << endl;
00101 #endif
00102 
00103       // Select a new cluster number
00104       if (g->cluster)
00105         cur_cluster = g->cluster;
00106       else
00107       {
00108         cur_cluster = ++numberOfClusters;
00109         if (numberOfClusters >= USHRT_MAX)
00110           throw LogicException("Too many clusters");
00111 
00112         // Detect hanging operations
00113         if (g->getFlows().empty() && g->getLoads().empty() 
00114           && g->getSuperOperations().empty()
00115           && g->getSubOperations().empty()
00116           )
00117         {
00118           ++numberOfHangingClusters;
00119           g->lvl = 0;
00120           continue;
00121         }
00122       }
00123 
00124       // Do we need to activate the level search?
00125       // Criterion are:
00126       //   - Not used in a super operation
00127       //   - Have at no producing Flow on the Operation itself
00128       //     or on any of its sub operations
00129       search_level = false;
00130       if (g->getSuperOperations().empty())
00131       {
00132         search_level = true;
00133         // Does the operation itself have producing flows?
00134         for (Operation::flowlist::const_iterator fl = g->getFlows().begin();
00135             fl != g->getFlows().end() && search_level; ++fl)
00136           if (fl->isProducer()) search_level = false;
00137         if (search_level)
00138         {
00139           // Do suboperations have a producing flow?
00140           for (Operation::Operationlist::const_reverse_iterator
00141               i = g->getSubOperations().rbegin();
00142               i != g->getSubOperations().rend() && search_level;
00143               ++i)
00144             for (Operation::flowlist::const_iterator
00145                 fl = (*i)->getFlows().begin();
00146                 fl != (*i)->getFlows().end() && search_level;
00147                 ++fl)
00148               if (fl->isProducer()) search_level = false;
00149         }
00150       }
00151 
00152       // If both the level and the cluster are de-activated, then we can move on
00153       if (!search_level && g->cluster) continue;
00154 
00155       // Start recursing
00156       // Note that as soon as push an operation on the stack we set its
00157       // cluster and/or level. This is avoid that operations are needlessly
00158       // pushed a second time on the stack.
00159       stack.push(make_pair(&*g, search_level ? 0 : -1));
00160       g->cluster = cur_cluster;
00161       if (search_level) g->lvl = 0;
00162       while (!stack.empty())
00163       {
00164         // Take the top of the stack
00165         cur_oper = stack.top().first;
00166         cur_level = stack.top().second;
00167         stack.pop();
00168 
00169 #ifdef CLUSTERDEBUG
00170         logger << "    Recursing in Operation '" << *(cur_oper)
00171         << "' - current level " << cur_level << endl;
00172 #endif
00173 
00174         // Push sub operations on the stack
00175         for (Operation::Operationlist::const_reverse_iterator
00176             i = cur_oper->getSubOperations().rbegin();
00177             i != cur_oper->getSubOperations().rend();
00178             ++i)
00179         {
00180           if ((*i)->lvl < cur_level)
00181           {
00182             // Search level and cluster
00183             stack.push(make_pair(*i,cur_level));
00184             (*i)->lvl = cur_level;
00185             (*i)->cluster = cur_cluster;
00186           }
00187           else if (!(*i)->cluster)
00188           {
00189             // Search for clusters information only
00190             stack.push(make_pair(*i,-1));
00191             (*i)->cluster = cur_cluster;
00192           }
00193           // else: no search required
00194         }
00195 
00196         // Push super operations on the stack
00197         for (Operation::Operationlist::const_reverse_iterator
00198             j = cur_oper->getSuperOperations().rbegin();
00199             j != cur_oper->getSuperOperations().rend();
00200             ++j)
00201         {
00202           if ((*j)->lvl < cur_level)
00203           {
00204             // Search level and cluster
00205             stack.push(make_pair(*j,cur_level));
00206             (*j)->lvl = cur_level;
00207             (*j)->cluster = cur_cluster;
00208           }
00209           else if (!(*j)->cluster)
00210           {
00211             // Search for clusters information only
00212             stack.push(make_pair(*j,-1));
00213             (*j)->cluster = cur_cluster;
00214           }
00215           // else: no search required
00216         }
00217 
00218         // Update level of resources linked to current operation
00219         for (Operation::loadlist::const_iterator gres =
00220               cur_oper->getLoads().begin();
00221             gres != cur_oper->getLoads().end(); ++gres)
00222         {
00223           Resource *resptr = gres->getResource();
00224           // Update the level of the resource
00225           if (resptr->lvl < cur_level) resptr->lvl = cur_level;
00226           // Update the cluster of the resource and operations using it
00227           if (!resptr->cluster)
00228           {
00229             resptr->cluster = cur_cluster;
00230             // Find more operations connected to this cluster by the Resource
00231             for (Resource::loadlist::const_iterator resops =
00232                   resptr->getLoads().begin();
00233                 resops != resptr->getLoads().end(); ++resops)
00234               if (!resops->getOperation()->cluster)
00235               {
00236                 stack.push(make_pair(resops->getOperation(),-1));
00237                 resops->getOperation()->cluster = cur_cluster;
00238               }
00239           }
00240         }
00241 
00242         // Now loop through all flows of the operation
00243         for (Operation::flowlist::const_iterator
00244             gflow = cur_oper->getFlows().begin();
00245             gflow != cur_oper->getFlows().end();
00246             ++gflow)
00247         {
00248           cur_Flow = &*gflow;
00249           cur_buf = cur_Flow->getBuffer();
00250 
00251           // Check whether the level search needs to continue
00252           search_level = cur_level!=-1 && cur_buf->lvl<cur_level+1;
00253 
00254           // Check if the Buffer needs processing
00255           if (search_level || !cur_buf->cluster)
00256           {
00257             // Update the cluster of the current buffer
00258             cur_buf->cluster = cur_cluster;
00259 
00260             // Loop through all flows of the buffer
00261             for (Buffer::flowlist::const_iterator
00262                 buffl = cur_buf->getFlows().begin();
00263                 buffl != cur_buf->getFlows().end();
00264                 ++buffl)
00265             {
00266               // Check level recursion
00267               if (cur_Flow->isConsumer() && search_level)
00268               {
00269                 if (buffl->getOperation()->lvl < cur_level+1
00270                     && &*buffl != cur_Flow && buffl->isProducer())
00271                 {
00272                   stack.push(make_pair(buffl->getOperation(),cur_level+1));
00273                   buffl->getOperation()->lvl = cur_level+1;
00274                   buffl->getOperation()->cluster = cur_cluster;
00275                 }
00276                 else if (!buffl->getOperation()->cluster)
00277                 {
00278                   stack.push(make_pair(buffl->getOperation(),-1));
00279                   buffl->getOperation()->cluster = cur_cluster;
00280                 }
00281                 cur_buf->lvl = cur_level+1;
00282               }
00283               // Check cluster recursion
00284               else if (!buffl->getOperation()->cluster)
00285               {
00286                 stack.push(make_pair(buffl->getOperation(),-1));
00287                 buffl->getOperation()->cluster = cur_cluster;
00288               }
00289             }
00290           }  // End of needs-procssing if statement
00291         } // End of flow loop
00292 
00293       }     // End while stack not empty
00294 
00295     } // End of Operation loop
00296 
00297     // The above loop will visit ALL operations and recurse through the
00298     // buffers and resources connected to them.
00299     // Missing from the loop are buffers and resources that have no flows or
00300     // loads at all. We catch those poor lonely fellows now...
00301     for (Buffer::iterator gbuf2 = Buffer::begin();
00302         gbuf2 != Buffer::end(); ++gbuf2)
00303       if (gbuf2->getFlows().empty())
00304       {
00305         gbuf2->cluster = ++numberOfClusters;
00306         if (numberOfClusters >= USHRT_MAX)
00307           throw LogicException("Too many clusters");
00308         ++numberOfHangingClusters;
00309       }
00310     for (Resource::iterator gres2 = Resource::begin();
00311         gres2 != Resource::end(); ++gres2)
00312       if (gres2->getLoads().empty())
00313       {
00314         gres2->cluster = ++numberOfClusters;
00315         if (numberOfClusters >= USHRT_MAX)
00316           throw LogicException("Too many clusters");
00317         ++numberOfHangingClusters;
00318       }
00319 
00320   } // End of while recomputeLevels. The loop will be repeated as long as model
00321   // changes are done during the recomputation.
00322 
00323   // Unlock the exclusive access to this function
00324   computationBusy = false;
00325 }
00326 
00327 } // End Namespace

Documentation generated by  doxygen