coriolis/mauka/src/BBPlacer.cpp

744 lines
24 KiB
C++

// This file is part of the Coriolis Project.
// Copyright (C) Laboratoire LIP6 - Departement ASIM
// Universite Pierre et Marie Curie
//
// Main contributors :
// Christophe Alexandre <Christophe.Alexandre@lip6.fr>
// Sophie Belloeil <Sophie.Belloeil@lip6.fr>
// Hugo Clément <Hugo.Clement@lip6.fr>
// Jean-Paul Chaput <Jean-Paul.Chaput@lip6.fr>
// Damien Dupuis <Damien.Dupuis@lip6.fr>
// Christian Masson <Christian.Masson@lip6.fr>
// Marek Sroka <Marek.Sroka@lip6.fr>
//
// The Coriolis Project is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// The Coriolis Project is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with the Coriolis Project; if not, write to the Free Software
// Foundation, inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
//
// License-Tag
//
// Date : 29/01/2004
// Author : Christophe Alexandre <Christophe.Alexandre@lip6.fr>
// inspired by Andrew Caldwell's BranchSmallPlacer
// Authors-Tag
/**************************************************************************
***
*** Copyright (c) 1995-2000 Regents of the University of California,
*** Andrew E. Caldwell, Andrew B. Kahng and Igor L. Markov
*** Copyright (c) 2000-2002 Regents of the University of Michigan,
*** Saurabh N. Adya and Igor L. Markov
***
*** Contact author(s): abk@cs.ucsd.edu, imarkov@umich.edu
*** Original Affiliation: UCLA, computer Science Department,
*** Los Angeles, CA 90095-1596 USA
***
*** Permission is hereby granted, free of charge, to any person obtaining
*** a copy of this software and associated documentation files (the
*** "Software"), to deal in the Software without restriction, including
*** without limitation
*** the rights to use, copy, modify, merge, publish, distribute, sublicense,
*** and/or sell copies of the Software, and to permit persons to whom the
*** Software is furnished to do so, subject to the following conditions:
***
*** The above copyright notice and this permission notice shall be included
*** in all copies or substantial portions of the Software.
***
*** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
*** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
*** OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
*** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
*** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
*** OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
*** THE USE OR OTHER DEALINGS IN THE SOFTWARE.
***
***
***************************************************************************/
#include "hurricane/UpdateSession.h"
#include "hurricane/Interval.h"
#include "hurricane/Net.h"
#include "hurricane/Cell.h"
#include "crlcore/ToolBox.h"
#include "crlcore/AllianceFramework.h"
#include "mauka/MaukaEngine.h"
#include "mauka/Surface.h"
#include "mauka/Row.h"
#include "mauka/SubRow.h"
#include "mauka/Bin.h"
#include "mauka/BBPlacer.h"
namespace {
using namespace std;
using namespace Mauka;
using namespace Hurricane;
using namespace CRL;
class CompareInstancePosition
{
private: BBPlacer* _bbPlacer;
public: CompareInstancePosition(BBPlacer* bbplacer)
: _bbPlacer(bbplacer)
{}
public: bool operator()(unsigned id1, unsigned id2) const
{
return _bbPlacer->getInstanceIdX(id1) < _bbPlacer->getInstanceIdX(id2);
}
};
} // End of anonymous namespace.
namespace Mauka {
using namespace std;
using namespace Hurricane;
using namespace CRL;
//#define PLACE_DEBUG 1
BBPlacer::BBPlacer(MaukaEngine* mauka)
: _mauka(mauka)
, _subRowVector()
, _subRowInstances()
, _instanceX()
, _instanceY()
, _instancePlaced()
, _netVector()
, _netBBoxes()
, _netCosts()
, _netCurrCostMark()
, _netFlags()
, _instanceToOptimize()
, _leftEdge(0)
, _rightEdge(0)
, _oldCost(0.0)
, _cost(0.0)
, _bestCost(0.0)
, _costFlag(0)
, _queue()
, _stack()
, _loopVect()
, _bestSolution()
, _idx(0)
{
for (unsigned i = 0; i < _mauka->_instanceOccurrencesVector.size(); i++)
{
_instanceMarginWidth.push_back(_mauka->_instanceWidths[i]);
_instanceX.push_back(0);
_instanceY.push_back(0);
_instancePlaced.push_back(true);
}
for (unsigned netid = 0; netid < _mauka->_nets.size(); netid++)
{
vector<Box> bboxes(2);
_netBBoxes.push_back(bboxes);
vector<double> costs(2);
_netCosts.push_back(costs);
_netFlags.push_back(0);
_netCurrCostMark.push_back(0);
}
Surface::RowVector& rowVector = _mauka->_surface->_rowVector;
for (Surface::RowVector::iterator rvit = rowVector.begin();
rvit != rowVector.end();
rvit++)
{
Row::SubRowVector& subRowVector = (*rvit)->_subRowVector;
for (Row::SubRowVector::iterator srvit = subRowVector.begin();
srvit != subRowVector.end();
srvit++)
{
SubRow* subRow = *srvit;
if (subRow->getSize() > 0)
{
_subRowVector.push_back(subRow);
_subRowInstances.push_back(MaukaEngine::UVector());
DbU::Unit totalInstanceSizeInRow = 0;
for (SubRow::BinVector::iterator bvit = subRow->_binVector.begin();
bvit != subRow->_binVector.end();
bvit++)
{
Bin* bin = *bvit;
totalInstanceSizeInRow += bin->getSize();
for (MaukaEngine::UList::const_iterator ulit = bin->InstanceOccurrenceIdsBegin();
ulit != bin->InstanceOccurrenceIdsEnd();
ulit++)
{
unsigned instanceId = *ulit;
_subRowInstances.back().push_back(instanceId);
}
}
DbU::Unit pitch = _mauka->getPitch();
DbU::Unit whiteSpace = subRow->getWidth() - totalInstanceSizeInRow;
DbU::Unit instanceWhiteSpacePitch = (whiteSpace / _subRowInstances.back().size()) / pitch;
DbU::Unit whiteSpaceRemain = whiteSpace
- (instanceWhiteSpacePitch * _subRowInstances.back().size() * pitch);
assert(!(whiteSpaceRemain%pitch));
if (instanceWhiteSpacePitch > 0)
{
for (MaukaEngine::UVector::const_iterator uvit = _subRowInstances.back().begin();
uvit != _subRowInstances.back().end();
uvit++)
{
_instanceMarginWidth[*uvit] += instanceWhiteSpacePitch * pitch;
}
}
MaukaEngine::UVector::const_iterator uvit = _subRowInstances.back().begin();
while (whiteSpaceRemain > 0)
{
_instanceMarginWidth[*uvit++] += pitch;
whiteSpaceRemain -= pitch;
assert(uvit != _subRowInstances.back().end());
}
DbU::Unit xPos = subRow->getXMin();
DbU::Unit yPos = subRow->getYMin();
for (MaukaEngine::UVector::const_iterator uvit = _subRowInstances.back().begin();
uvit != _subRowInstances.back().end();
uvit++)
{
unsigned instanceId = *uvit;
_instanceX[instanceId] = xPos;
_instanceY[instanceId] = yPos;
xPos += _instanceMarginWidth[instanceId];
}
}
}
}
}
void BBPlacer::Save()
{
UpdateSession::open();
bool autoMaterialize = not Go::autoMaterializationIsDisabled();
Go::enableAutoMaterialization();
for (unsigned i = 0; i < _subRowInstances.size(); i++)
{
SubRow* subRow = _subRowVector[i];
Transformation::Orientation orientation
= (subRow->getRow()->getOrientation()) ? Transformation::Orientation::MY
: Transformation::Orientation::ID;
for (unsigned j = 0; j < _subRowInstances[i].size(); j++)
{
unsigned instanceId = _subRowInstances[i][j];
DbU::Unit x = _instanceX[instanceId];
DbU::Unit y = _instanceY[instanceId];
Occurrence instanceOccurrence = _mauka->_instanceOccurrencesVector[instanceId];
Instance* instance = static_cast<Instance*>(instanceOccurrence.getEntity());
Box masterABox = instance->getMasterCell()->getAbutmentBox();
Transformation instanceTransformation = getTransformation(masterABox, x, y, orientation);
#if 0
cerr << masterABox.getXMin() << "," << masterABox.getYMin()
<< masterABox.getXMax() << "," << masterABox.getYMax() << endl;
cerr << x << "," << y << endl;
cerr << instanceTransformation << endl;
cerr << "occ transfo : " << instanceOccurrence.getPath().getTransformation() << endl;
#endif
instanceOccurrence.getPath().getTransformation().invert().applyOn(instanceTransformation);
instance->setTransformation(instanceTransformation);
instance->setPlacementStatus(Instance::PlacementStatus::PLACED);
//setPlacementStatusRecursivelyToPlaced(instance);
}
}
if ( autoMaterialize ) Go::enableAutoMaterialization ();
else Go::disableAutoMaterialization ();
UpdateSession::close();
}
void BBPlacer::Plot(ofstream& out) const
{
for (unsigned i = 0; i < _mauka->_instanceOccurrencesVector.size(); i++)
{
DbU::Unit x = _instanceX[i];
DbU::Unit y = _instanceY[i];
Occurrence instanceOccurrence = _mauka->_instanceOccurrencesVector[i];
Instance* instance = static_cast<Instance*>(instanceOccurrence.getEntity());
Box masterABox = instance->getMasterCell()->getAbutmentBox();
Box instanceBox = Box(
x,
y,
x + masterABox.getWidth(),
y + masterABox.getHeight());
out << instanceBox.getXMin()+0.4 << " " << instanceBox.getYMin()+0.4 << endl
<< instanceBox.getXMin()+0.4 << " " << instanceBox.getYMax()-0.4 << endl
<< instanceBox.getXMax()-0.4 << " " << instanceBox.getYMax()-0.4 << endl
<< instanceBox.getXMax()-0.4 << " " << instanceBox.getYMin()+0.4 << endl
<< instanceBox.getXMin()+0.4 << " " << instanceBox.getYMin()+0.4 << endl << endl;
}
out << "EOF" << endl << endl;
out << "#nets" << endl;
for (unsigned i = 0; i < _mauka->_netInstances.size(); i++)
{
if (_mauka->_hasInitX[i])
continue;
unsigned nbInstances = 0;
DbU::Unit baryX = 0;
DbU::Unit baryY = 0;
for (unsigned j = 0; j < _mauka->_netInstances[i].size(); j++)
{
unsigned instanceId = _mauka->_netInstances[i][j];
Occurrence instanceOccurrence = _mauka->_instanceOccurrencesVector[instanceId];
Instance* instance = static_cast<Instance*>(instanceOccurrence.getEntity());
Box masterABox = instance->getMasterCell()->getAbutmentBox();
++nbInstances;
baryX += _instanceX[instanceId] + masterABox.getWidth() / 2;
baryY += _instanceY[instanceId] + masterABox.getHeight() / 2;
}
baryX = baryX / nbInstances;
baryY = baryY / nbInstances;
for (unsigned j = 0; j < _mauka->_netInstances[i].size(); j++)
{
unsigned instanceId = _mauka->_netInstances[i][j];
Occurrence instanceOccurrence = _mauka->_instanceOccurrencesVector[instanceId];
Instance* instance = static_cast<Instance*>(instanceOccurrence.getEntity());
Box masterABox = instance->getMasterCell()->getAbutmentBox();
out << baryX << " " << baryY << endl
<< _instanceX[instanceId] + masterABox.getWidth() / 2 << " "
<< _instanceY[instanceId] + masterABox.getHeight() / 2 << endl << endl;
}
}
out << "EOF" << endl << endl;
out << "#nets with fixed point" << endl;
for (unsigned i = 0; i < _mauka->_netInstances.size(); i++)
{
if (!_mauka->_hasInitX[i])
continue;
unsigned nbInstances = 1;
DbU::Unit baryX = 0;
DbU::Unit baryY = 0;
baryX += _mauka->_netInitX[i];
baryY += _mauka->_netInitY[i];
for (unsigned j = 0; j < _mauka->_netInstances[i].size(); j++)
{
unsigned instanceId = _mauka->_netInstances[i][j];
Occurrence instanceOccurrence = _mauka->_instanceOccurrencesVector[instanceId];
Instance* instance = static_cast<Instance*>(instanceOccurrence.getEntity());
Box masterABox = instance->getMasterCell()->getAbutmentBox();
++nbInstances;
baryX += _instanceX[instanceId] + masterABox.getWidth() / 2;
baryY += _instanceY[instanceId] + masterABox.getHeight() / 2;
}
baryX = baryX / nbInstances;
baryY = baryY / nbInstances;
for (unsigned j = 0; j < _mauka->_netInstances[i].size(); j++)
{
unsigned instanceId = _mauka->_netInstances[i][j];
Occurrence instanceOccurrence = _mauka->_instanceOccurrencesVector[instanceId];
Instance* instance = static_cast<Instance*>(instanceOccurrence.getEntity());
Box masterABox = instance->getMasterCell()->getAbutmentBox();
out << baryX << " " << baryY << endl
<< _instanceX[instanceId] + masterABox.getWidth() / 2 << " "
<< _instanceY[instanceId] + masterABox.getHeight() / 2 << endl << endl;
}
out << baryX << " " << baryY << endl
<< _mauka->_netInitX[i] << " "
<< _mauka->_netInitY[i] << endl << endl;
}
out << "EOF" << endl << endl;
}
void BBPlacer::Run()
{
for (unsigned i = 0; i < _subRowInstances.size(); i++)
{
unsigned decal = 2;
unsigned nInstancesToOptimize = 0;
MaukaEngine::UVector::iterator ifirst = _subRowInstances[i].begin();
MaukaEngine::UVector::iterator ilast = _subRowInstances[i].end();
while (1)
{
_instanceToOptimize.clear();
while (1)
{
if (ifirst == ilast)
break;
_instanceToOptimize.push_back(*ifirst++);
++nInstancesToOptimize;
if (nInstancesToOptimize >= 6)
{
nInstancesToOptimize = 0;
break;
}
}
Optimize();
if (ifirst == ilast)
break;
sort(_subRowInstances[i].begin(), _subRowInstances[i].end(), CompareInstancePosition(this));
if ((6 + decal) <= nInstancesToOptimize)
{
ifirst = _subRowInstances[i].begin() + decal;
}
else
{
ifirst = _subRowInstances[i].begin() + decal - 1;
}
decal += 2;
}
}
}
bool BBPlacer::Optimize()
{
bool optimizationResult = false;
const int numberInstances = _instanceToOptimize.size();
// Save initial Solution
_bestSolution.reserve(numberInstances);
for (unsigned i=0; i<_instanceToOptimize.size(); i++)
{
_queue.push_back(_instanceToOptimize[i]);
}
MaukaEngine::UVector::iterator ifirst = _instanceToOptimize.begin();
MaukaEngine::UVector::iterator ilast = _instanceToOptimize.end();
vector<DbU::Unit>::iterator it = _bestSolution.begin();
while (ifirst != ilast)
*it++ = _instanceX[*ifirst++];
// init Best Cost
_bestCost = initCost();
#ifdef PLACE_DEBUG
cout<<" Orig Cost: "<< _bestCost << endl;
cout<<" Actual Cost: " << CurrentCost() << endl;
#endif
// init initial Cost
// init Edges
_leftEdge = _instanceX[*_instanceToOptimize.begin()];
_rightEdge = _instanceX[*_instanceToOptimize.rbegin()]
+ _instanceMarginWidth[*_instanceToOptimize.rbegin()];
UnplaceAll();
_cost = initCost();
#ifdef PLACE_DEBUG
cout << " init Cost: " << _cost << endl;
#endif
_loopVect.reserve(numberInstances + 2);
for (int id = 0; id < numberInstances + 2; id++)
_loopVect.push_back(0);
_loopVect[numberInstances] = numberInstances;
_loopVect[numberInstances + 1] = numberInstances + 1;
_stack.reserve(numberInstances);
_idx = numberInstances - 1;
unsigned numAdds = 0;
while(_idx < numberInstances)
{
addIns();
numAdds++;
if(_loopVect[_idx] == 0 || _cost >= _bestCost)
{
_loopVect[_idx] = 0; //force a bound
if(_cost < _bestCost) //got here if:
// new best complete soln (curWL < best)
// bounded partial soln (curWL > best)
// so there is no need to additionally
// check to ensure this is a complete soln
{
optimizationResult = true;
_bestCost = _cost;
#ifdef PLACE_DEBUG
cout<<" New Best: "<< _cost <<" found after "<< numAdds << endl;
cout << "Cost recalculated: " << initCost() << endl;
cout << "Actual Cost: " << CurrentCost() << endl;
#endif
ifirst = _instanceToOptimize.begin();
it = _bestSolution.begin();
while (ifirst != ilast)
{
*it++ = _instanceX[*ifirst++];
}
#if 0
Save();
for_each_view(view, _mauka->getCell()->getViews())
{
if (CEditor* editor = dynamic_cast<CEditor*>(view))
{
editor->Stop("coucou");
break;
}
end_for;
}
#endif
}
while(_loopVect[_idx] == 0)
{
if(_idx < numberInstances)
removeIns();
_loopVect[++_idx]--;
}
}
--_idx;
}
ifirst = _instanceToOptimize.begin();
it = _bestSolution.begin();
while (ifirst != ilast)
_instanceX[*ifirst++] = *it++;
PlaceAll();
_bestSolution.clear();
#ifdef PLACE_DEBUG
cout<<" Total add Operations: "<< numAdds<< endl;
cout<<" Final solution has cost: "<< _bestCost << endl << endl;
#endif
_loopVect.clear();
_queue.clear();
_stack.clear();
return optimizationResult;
}
void BBPlacer::addIns()
{
_stack.push_back(_queue.front());
_queue.pop_front();
_loopVect[_idx] = _idx;
unsigned instanceId = _stack.back();
//cerr << "adding: " << _instanceOccurrenceVector[instanceId] << " " ;
if(_idx%2)
{
_instanceX[instanceId] = _rightEdge - _instanceMarginWidth[instanceId];
_rightEdge -= _instanceMarginWidth[instanceId];
}
else
{
_instanceX[instanceId] = _leftEdge;
_leftEdge += _instanceMarginWidth[instanceId];
}
//cerr << _instanceX[instanceId] << " ";
_instancePlaced[instanceId] = true;
_cost += UpdateInstanceCost(instanceId);
//cerr << _cost << endl;
//cerr << "verify " << CurrentCost() << endl;
}
void BBPlacer::removeIns()
{
unsigned instanceId = _stack.back();
//cerr << "Removing: " << _instanceOccurrenceVector[instanceId] << " " ;
_queue.push_back(instanceId);
_stack.pop_back();
if(_idx%2)
_rightEdge += _instanceMarginWidth[instanceId];
else
_leftEdge -= _instanceMarginWidth[instanceId];
_instancePlaced[instanceId] = false;
_cost += UpdateInstanceCost(instanceId);
//cerr << _cost << endl;
}
void BBPlacer::PlaceAll()
{
for (MaukaEngine::UVector::iterator uvit = _instanceToOptimize.begin();
uvit != _instanceToOptimize.end();
uvit++)
{
_instancePlaced[*uvit] = true;
}
}
void BBPlacer::UnplaceAll()
{
for (MaukaEngine::UVector::iterator uvit = _instanceToOptimize.begin();
uvit != _instanceToOptimize.end();
uvit++)
{
_instancePlaced[*uvit] = false;
}
}
double BBPlacer::initCost()
{
++_costFlag;
double cost = 0.0;
for (MaukaEngine::UVector::iterator uvit = _instanceToOptimize.begin();
uvit != _instanceToOptimize.end();
uvit++)
{
cost += initInstanceCost(*uvit);
}
return cost;
}
double BBPlacer::CurrentCost()
{
++_costFlag;
double cost = 0.0;
for (MaukaEngine::UVector::iterator uvit = _instanceToOptimize.begin();
uvit != _instanceToOptimize.end();
uvit++)
cost += CurrentInstanceCost(*uvit);
return cost;
}
double BBPlacer::UpdateInstanceCost(unsigned instanceid)
{
double deltaCost = 0.0;
for (MaukaEngine::UVector::const_iterator uvit = _mauka->_instanceNets[instanceid].begin();
uvit != _mauka->_instanceNets[instanceid].end();
uvit++)
{
deltaCost += UpdateNetCost(*uvit);
}
return deltaCost;
}
double BBPlacer::UpdateNetCost(unsigned netid)
{
Box& netBBox = _netBBoxes[netid][_netFlags[netid]];
double& netCost = _netCosts[netid][_netFlags[netid]];
double initCost = netCost;
netBBox.makeEmpty();
if (_mauka->_hasInitX[netid])
netBBox.merge(_mauka->_netInitX[netid], netBBox.getYMin());
if (_mauka->_hasInitY[netid])
netBBox.merge(netBBox.getXMin(), _mauka->_netInitY[netid]);
for (MaukaEngine::UVector::const_iterator uvit = _mauka->_netInstances[netid].begin();
uvit != _mauka->_netInstances[netid].end();
uvit++)
{
if (_instancePlaced[*uvit])
{
//FIXME
netBBox.merge(Point(_instanceX[*uvit], _instanceY[*uvit]));
}
}
netCost = computeCost(netBBox);
return (netCost - initCost);
}
double BBPlacer::initInstanceCost(unsigned instanceid)
{
double cost = 0.0;
for (MaukaEngine::UVector::const_iterator uvit = _mauka->_instanceNets[instanceid].begin();
uvit != _mauka->_instanceNets[instanceid].end();
uvit++)
{
computeNetBBox(*uvit);
//SaveNetTempValue(*uvit);
cost += CurrentNetCost(*uvit);
}
return cost;
}
double BBPlacer::CurrentInstanceCost(unsigned instanceid)
{
double cost = 0.0;
for (MaukaEngine::UVector::const_iterator uvit = _mauka->_instanceNets[instanceid].begin();
uvit != _mauka->_instanceNets[instanceid].end();
uvit++)
{
cost += CurrentNetCost(*uvit);
}
return cost;
}
void BBPlacer::SaveNetTempValue(unsigned netid)
{
_netFlags[netid] = !_netFlags[netid];
}
void BBPlacer::computeNetBBox(unsigned netid)
{
//cerr << _netVector[netid] << endl;
//Box& netTmpBBox = _netBBoxes[netid][!_netFlags[netid]];
Box& netTmpBBox = _netBBoxes[netid][_netFlags[netid]];
//FIXME
netTmpBBox.makeEmpty();
if (_mauka->_hasInitX[netid])
netTmpBBox.merge(_mauka->_netInitX[netid], netTmpBBox.getYMin());
if (_mauka->_hasInitY[netid])
netTmpBBox.merge(netTmpBBox.getXMin(), _mauka->_netInitY[netid]);
for (MaukaEngine::UVector::const_iterator uvit = _mauka->_netInstances[netid].begin();
uvit != _mauka->_netInstances[netid].end();
uvit++)
{
if (_instancePlaced[*uvit])
{
//FIXME
netTmpBBox.merge(Point(_instanceX[*uvit], _instanceY[*uvit]));
//cerr << netTmpBBox << endl;
}
}
_netCosts[netid][_netFlags[netid]] = computeCost(netTmpBBox);
//_netCosts[netid][!_netFlags[netid]] = computeCost(netTmpBBox);
//cerr << _netCosts[netid][!_netFlags[netid]] << endl;
}
double BBPlacer::computeCost(const Box& box) const
{
if (!box.isEmpty())
return DbU::getLambda(box.getYMax() - box.getYMin() + box.getXMax() - box.getXMin());
else
return 0.0;
}
double BBPlacer::CurrentNetCost(unsigned netid)
{
if (_netCurrCostMark[netid] != _costFlag)
{
_netCurrCostMark[netid] = _costFlag;
return _netCosts[netid][_netFlags[netid]];
}
return 0;
}
}