#!/usr/bin/python

import sys                
from   Hurricane     import *
from   CRL           import *
from   math          import sqrt, ceil
from   helpers.io    import ErrorMessage as Error
from   helpers       import trace
from   capacitorunit import CapacitorUnit
import helpers
import oroshi
import numpy

def toPhY    ( l ): return DbU.toPhysical  ( l, DbU.UnitPowerMicro )


## Draws the layout of a compact capacitor or a matrix of adjacent identical capacitors. The matrix can be composed of one type of capacitors, either Poly-Poly or Metal-Metal in 350 nm AMS CMOS technology. 
#  When matching mode is off, every adjacent plates of any consecutive elementary capacitors are connected to each other using vertical routing layers. 
#  Otherwise, when matching mode is on, any of elementary capacitors can belong to, \f$ C_1 \f$ or \f$ C_2 \f$ according to the entered matching scheme. Thus, routing is not done in this class.
#  In both modes, the complete routing process is done using the \c RoutCapacitor class.

class CapacitorStack( CapacitorUnit ):
    
    rules = oroshi.getRules()


    ## This is the class constructor. Basically, the class there are three categories of attributes. There are the ones related to the capacitor caracteristics, its type, dimensions. Also, there are attributes to parametrize the class into matching mode or not and there are other attributes realted to the layout varibales. The class has defaut input values, thus, in this constructor, there are two "sub-constructors" according to the entered input parameters. The class attributes are :
    #
    #  \param   device             The Hurricane AMS device into which the layout is drawn. 
    #  \param   capacitance        The value of the capacitor, expressed in femto Farad (fF).
    #  \param   capacitorType      Can be MIM or PIP type capacitor.
    #  \param   abutmentPosition   Refers to the abscissa (XMin) of the bottom left corner of the abutment Box. 
    #  \param   abutmentBoxYMin    Refers to the ordinate (YMin) of the bottom left corner of the abutment Box.
    #
    # Except the two last arguments, all the parameters are common with the CapacitorUnit class because the \c CapacitorStack constructor calls the mother class constructor to create either a compact capacitor of \c capacitance value or \c rowNumber* \c columnNumber unity capacitors.
    #
    #  \param   rowNumber          Number of rows in the matrix of capacitors.
    #  \param   columnNumber       Number of columns in the matrix of capacitors.

    def __init__( self, device
                      , capacitance
                      , capacitorType
                      , abutmentBoxPosition
                      , nets
                      , unitCap        = 0
                      , matrixDim      = [1,1]
                      , matchingMode   = False
                      , matchingScheme = []
                      , dummyRing      = False
                      , dummyElement   = False ):
        self.device               =  device
        self.capacitorType        =  capacitorType
        self.matrixDim            =  { "columns" : matrixDim[1], "rows" : matrixDim[0] } 
        self.unitCapDim           =  self.__computeCapDim__( unitCap, capacitorType )

        self.doMatrix             =  False
        self.abutmentBox          =  Box()
        self.abutmentBoxPosition  =  { "XMin" : abutmentBoxPosition[0], "YMin" : abutmentBoxPosition[1] }
        self.nets                 =  nets
        self.matchingMode         =  matchingMode
        self.dummyRing            =  dummyRing
        self.dummyElement         =  dummyElement        

        self.capacitorsNumber     =  len(capacitance)

        self.matchingScheme       =  matchingScheme
        self.dummyRingPosition    =  {}
        self.abutmentBox_spacing  =  0
        self.vRoutingTrack_width  =  0

        if self.__areInputDataOK__(capacitance):
          if not self.matchingMode:                
            self.compactCapDim = self.__computeCapDim__( capacitance[0] , capacitorType ) 

            if unitCap == 0:
              self.__initGivenZeroUnitCap__( capacitance[0] )
            elif unitCap <> 0 and CapacitorUnit.__isCapacitorUnitOK__( self, self.unitCapDim ):
              self.__initGivenNonZeroUnitCap__( capacitance[0], unitCap )
            else:
              raise Error( 1, [ 'CapacitorStack.__init__(): Impossible to draw the unit capacitor, dimensions are either too large or too small.'
                              , '(width:{0} height:{1})'.format( DbU.getValueString(self.unitCapDim[width ])
                                                               , DbU.getValueString(self.unitCapDim[height]) ) ] )

          else:
            if unitCap == 0:
              self.__initGivenZeroUnitCapInMatchingMode__( capacitance )

            elif unitCap <> 0 and CapacitorUnit.__isCapacitorUnitOK__( self, self.unitCapDim ):
              self.__initGivenNonZeroUnitCapInMatchingMode__( capacitance, unitCap )
            else:
              raise Error( 1, [ 'CapacitorStack.__init__(): Impossible to draw the unit capacitor, dimensions are either too large or too small.'
                              , '(width:{0} height:{1})'.format( DbU.getValueString(self.unitCapDim[width ])
                                                               , DbU.getValueString(self.unitCapDim[height]) ) ] )


        return


    def setRules( self ) :

        CapacitorUnit.setRules( self )

        self.minWidth_vRoutingTrack        = CapacitorStack.rules.minWidth_metal3
        self.minSpacing_vRoutingTrack      = CapacitorStack.rules.minSpacingWide1_metal3
        self.minWidth_vRoutingTrackCut     = CapacitorStack.rules.minWidth_cut2
        self.minSpacing_vRoutingTrackCut   = CapacitorStack.rules.minSpacing_cut2
        self.minEnclosure_vRoutingTrackCut = CapacitorStack.rules.minEnclosure_metal3_cut2

        if self.capacitorType == 'MIMCap':
          self.minWidth_hRoutingLayer_topPlate_cut     = CapacitorStack.rules.minWidth_cut2
          self.minEnclosure_hRoutingLayer_topPlate_cut = CapacitorStack.rules.minEnclosure_metal2_cut2
        elif self.capacitorType == 'PIPCap': 
          self.minWidth_hRoutingLayer_topPlate_cut     = CapacitorStack.rules.minWidth_cut1
          self.minEnclosure_hRoutingLayer_topPlate_cut = CapacitorStack.rules.minEnclosure_metal2_cut1

        return 




    def __initMatchingMode__( self ) :

        self.vRoutingTrack_width =   max( self.minWidth_vRoutingTrack, 2*self.minEnclosure_vRoutingTrackCut + self.minWidth_vRoutingTrackCut,self.minWidth_hRoutingLayer_topPlate_cut + 2*self.minEnclosure_hRoutingLayer_topPlate_cut )
        if ( self.capacitorsNumber  % 2 == 0 ) : [factor1 , factor2 ] = [ self.capacitorsNumber    , (self.capacitorsNumber +1) ]  
        if ( self.capacitorsNumber  % 2 != 0 ) : [factor1 , factor2 ] = [ self.capacitorsNumber +1 ,  self.capacitorsNumber +2  ] 
        self.abutmentBox_spacing         =   factor1*self.vRoutingTrack_width + factor2*self.minSpacing_vRoutingTrack 

        return



    def __initMatrixMode__( self, capacitance, unitCap ) :

        [ self.capacitance, self.unitCapacitance , self.doMatrix ] = [ capacitance , unitCap , True ]

        return


    def __initGivenZeroUnitCap__( self, capacitance ):
        if not self.matrixDim['columns']:
          raise Error( 1, 'CapacitorStack.__initGivenZeroUnitCap__(): Requested matrix of *zero* columns.' )
        if not self.matrixDim['rows']:
          raise Error( 1, 'CapacitorStack.__initGivenZeroUnitCap__(): Requested matrix of *zero* rows.' )

        if self.matrixDim.values() == [1,1]:
          self.__isCapacitorUnitOK__( self.compactCapDim )
          self.capacitance = capacitance
          self.unitCapDim  = self.compactCapDim
        else:
          unitCapacitance = capacitance / (self.matrixDim['columns'] * self.matrixDim['rows'])
          unitCapDim      = self.__computeCapDim__( unitCapacitance, self.capacitorType )

          self.__isCapacitorUnitOK__( unitCapDim )
          self.unitCapDim      = unitCapDim
          self.unitCapacitance = unitCapacitance
          self.capacitance     = capacitance
          self.doMatrix        = True
                        
        return



    def __initGivenNonZeroUnitCap__( self, capacitance, unitCap ):

        if ( self.matrixDim["columns"]>1 or self.matrixDim["rows"]>1 ) : # jai donne les dim de la  matrice
            if self.matrixDim.values()[0]*self.matrixDim.values()[1] == capacitance/unitCap : 
                self.__initMatrixMode__( capacitance, unitCap )

            else : raise Error( 1, '__init__() : Matrix dimensions and unit capacitance are not compatible : "capacitance %d divides by unit capacitance %s <> columns %d * rows %d ".' %( capacitance, unitCap, self.matrixDim["columns"], self.matrixDim["rows"] ) ) 

        else : # self.matrixDim.values() == [1,1]  : # jai donne ou jai ps donne

            if capacitance == unitCap : #compact
                [ self.capacitance , self.unitCapDim  ] = [ capacitance , self.compactCapDim ]
            elif capacitance <> unitCap : #matrice
                self.__initMatrixMode__( capacitance, unitCap )
                self.matrixDim  = {"columns" : int(sqrt(capacitance/unitCap)), "rows" : int(sqrt(capacitance/unitCap)) } # ici mettre toutes les combi si matching mode = [] sinon utiliser la meme combi que matching scheme

            else :  raise Error( 1,'__initGivenNonZeroUnitCap__ : capacitance must be multiple of unit capacitance' )
 
        return



    def __initGivenZeroUnitCapInMatchingMode__( self, capacitance ):
       #print '__initGivenZeroUnitCapInMatchingMode__'

        if self.matrixDim.values() == [1,1] or (self.matrixDim["columns"] == len(self.matchingScheme[0]) and self.matrixDim["rows"] == len(self.matchingScheme)) :

            unitCapList = self.computeUnitCap(capacitance) 

            if len( list(numpy.unique(unitCapList)) ) == 1 : 

                unitCapDim = self.__computeCapDim__( unitCapList[0], self.capacitorType )

                if CapacitorUnit.__isCapacitorUnitOK__(self, unitCapDim) == True : 
                    
                    self.unitCapDim = unitCapDim
                    self.__initMatrixMode__( capacitance, unitCapList[0] )
                    if self.matrixDim.values() == [1,1] : self.matrixDim  = {"columns" : len(self.matchingScheme[0]) , "rows" : len(self.matchingScheme) }

                else : raise Error(1,'__initGivenZeroUnitCapInMatchingMode__() : Impossible to draw unit capacitor, dimensions are either too large or too small, "%s".' % unitCapDim)

            else : raise Error(1,'__initGivenZeroUnitCapInMatchingMode__() : Not all capacitances are multiple of the unit capacitor.')

        else : raise Error(1,'__initGivenZeroUnitCapInMatchingMode__() : Please check compatibility between matrix dimensions and matching scheme dimensions. Both must be equal.')

        return




    def __initGivenNonZeroUnitCapInMatchingMode__( self, capacitance, unitCap ):

        if CapacitorUnit.__isCapacitorUnitOK__(self, self.unitCapDim) == True : 

            if self.matrixDim.values() == [1,1] or (self.matrixDim["columns"] == len(self.matchingScheme[0]) and self.matrixDim["rows"] == len(self.matchingScheme)) :
 
                if self.evaluateUnitCap( capacitance, unitCap ) == True :
                    self.__initMatrixMode__( capacitance, unitCap )
                    if self.matrixDim.values() == [1,1] : self.matrixDim  = {"columns" : len(self.matchingScheme[0]) , "rows" : len(self.matchingScheme) }

                else: raise Error(1,'__initGivenNonZeroUnitCapInMatchingMode__() : Non valid unit capacitor value considering the entered matching scheme. Please make sure that capacitors values are all multiples of unit capacitor.')

            else: raise Error(1,'__initGivenNonZeroUnitCapInMatchingMode__() : Please check compatibility between matrix dimensions and matching scheme dimensions. Both must be equal.')

        else: raise Error(1,'__initGivenNonZeroUnitCapInMatchingMode__() : Impossible to draw unit capacitor, dimensions are either too large or too small, "%s".' % self.unitCapDim)

        return



    def __areMatrixDimOK__( self ): return True if self.matrixDim.values() > 0 else False



    def computeUnitCap( self, capacitance ):

        unitCapList = []
        for k in range(0, self.capacitorsNumber):
            unitCapList.append( capacitance[k]/self.capacitorIdOccurence(k) )

       #print self.capacitorsNumber
       #print 'capacitance', capacitance
       #print 'unitCapList', unitCapList
       #print '============='
        return unitCapList



    def evaluateUnitCap( self, capacitance, unitCap ):

        state = True
        for k in range(0, self.capacitorsNumber):
            #print('self.capacitorIdOccurence( k )',self.capacitorIdOccurence( k ))
            factor = capacitance[k]/unitCap
            if factor != self.capacitorIdOccurence( k ) : state = False 

        return state



    ## \return True if the drawn capacitor is a compact one. This function is useful when an instance is called in another class. \b Example : when the matrix or the compact capacitors are to be fully routed.
 
    def __isUnitCap__( self ): return True if not self.doMatrix else False


    ## \return \c True if the matching scheme specifications are correct. Specifications are : 
    #  -  Similar number of elements as total number of elementary capacitor in the matrix.  
    #  -  Equal number of affected capacitors to C1 as to C2.
    #  -  Capacitor identifiers equal to '1' or '2' only.
    #  -  Otherwise, the function returns \c False. 

    def __isMatchingSchemeOK__ ( self ):

        state = True
        columsElementsNumber = [ len(self.matchingScheme[k]) for k in range(0,len(self.matchingScheme)) ] 

        if len( list(numpy.unique(columsElementsNumber)) ) > 1 : 
            state = False  
        else :    
            [ matrixDim , matchingSchemeDim ] = [ self.matrixDim["columns"]*self.matrixDim["rows"] , len(self.matchingScheme)*len(self.matchingScheme[0]) ]
            comparaison = [ self.matrixDim[key]>1 for key in self.matrixDim.keys() ]

            if ( True in comparaison ) and ( matchingSchemeDim != matrixDim ) : state = False

        return state 

 
 
    ## \return occurence of capacitor identifier in the entered matching scheme. This is useful to verify that \c self.matchingScheme is correct.

    def capacitorIdOccurence ( self, capacitorIdentifier  ):

        occurence  = sum( element.count(capacitorIdentifier) for element in self.matchingScheme  )        

        return occurence



    def __areInputDataOK__( self, capacitance ) :

        state = False

        if  ( self.__areMatrixDimOK__() == True ) :

            if self.matchingMode in [False, True] and self.dummyRing in [False,True] and self.dummyElement in [False,True]:

                [ matchingSchemeCapIds , capacitanceIds ] = [ list( numpy.unique(self.matchingScheme) ) , range(0,self.capacitorsNumber) ]
                if (self.matchingScheme != [] and set(matchingSchemeCapIds) == set(capacitanceIds) ) or (self.matchingScheme == [] and len(capacitance) == 1) :
                    if    (len(self.nets) == self.capacitorsNumber + 1 and self.dummyElement == False and self.dummyRing == True ) \
                       or (len(self.nets) == self.capacitorsNumber     and self.dummyElement == False and self.dummyRing == False) \
                       or (len(self.nets) == self.capacitorsNumber     and self.dummyElement == True  and self.dummyRing == True ) \
                       or (len(self.nets) == self.capacitorsNumber     and self.dummyElement == True  and self.dummyRing == False):

                        if ( self.matchingMode == True and self.__isMatchingSchemeOK__() ) or ( self.matchingMode == False and self.matchingScheme == [] ): 
                            state = True  
                        else: raise Error(1, '__areInputDataOK__(): Please check compatibility of the entered parameters (Matching mode, matching scheme, capacitance). It must be either equal to (False, [], one capacitance value) or ( True, matching scheme, capacitance values as much as there are capacitor ids in matching scheme ). The entered parameters are (%s, %s, %s).' %(self.matchingMode, self.matchingScheme, capacitance) ) #com2 : tester 

                    else : raise Error(1,'__areInputDataOK__() : Nets number, %s, is incompatible with number of capacitors to be drawn, %s.' %(len(self.nets), self.capacitorsNumber))

                else : raise Error(1, '__areInputDataOK__() : Please check compatibility between matching scheme elements, %s, and capacitance indexes, %s. They must be identical. Otherwise, when matching scheme is "False", capacitance indexes must be [0].' %(matchingSchemeCapIds, capacitanceIds) )

            else : raise Error(1,'__areInputDataOK__() : Matching mode, %s, dummy ring, %s, and dummy element, %s, must be all either "True" or "False".' %(self.matchingMode, self.dummyRing, self.dummyElement))

        else : raise Error(1,'__areInputDataOK__(): Both matrix dimensions "%s" must be positive.' % self.matrixDim.keys())

        return state




    ## Draw the compact or matrix of capacitors. First, . Second, . Finally, .

    def create( self, bbMode = False ):

        UpdateSession.open()           
        drawnCapacitor = {}

        self.setRules()

        if self.matchingMode == True : 
            self.__initMatchingMode__()

        self.drawAbutmentBox( self.abutmentBox_spacing )

        if    bbMode == True:
            output =  self.computeBondingBoxDimensions()
        elif  bbMode == False : 
            drawnCapacitor = self.drawCapacitorStack( )
            output = drawnCapacitor
        else:
            raise Error(1, 'create(): The bonding box mode parameter, "bbMode" must be either True or False : %s.' %bbMode )

        UpdateSession.close  ()
   
        return output


    def drawCapacitorStack( self ):

        drawnCapacitor     = []
        bottomPlateRLayer  = CapacitorUnit.getLayers( self )["bottomPlateRLayer"]
        topPlateRLayer     = CapacitorUnit.getLayers( self )["topPlateRLayer"   ]

        if self.doMatrix == True :          
            drawnCapacitor = self.capacitorMatrix ( self.abutmentBox_spacing )      

            if  self.matchingMode == False : 
                if self.dummyRing == True:
                    drawnActiveCapacitor = [drawnCapacitor[1][1:len(drawnCapacitor[1])-1]]
                    for i in range(2,self.matrixDim["rows"]+1):
                        drawnActiveCapacitor.append(drawnCapacitor[i][1:len(drawnCapacitor[1])-1])

                else : drawnActiveCapacitor = drawnCapacitor

                self.drawBottomPlatesRLayers( bottomPlateRLayer, drawnActiveCapacitor )
                self.drawTopPlatesRLayers   ( topPlateRLayer   , drawnActiveCapacitor )                

        else:
            drawnCapacitor = CapacitorUnit( self.device, self.capacitorType, [self.abutmentBoxPosition["XMin"], self.abutmentBoxPosition["YMin"]], self.capacitance ) 
            drawnCapacitor.create( self.nets[0][0], self.nets[0][1] )

        return drawnCapacitor





    ## Iteratively draws a horizontal or vertical line of capacitors according to the \c direction parameter. An exception is raised if the specified direction is different from \c {'horizontal','vertical'}. At every iteration, an instance of the CapacitorUnit class is created and its layout is drawn.
    # \return a list containing the drawn capacitors.
    #  \param dy the vertical position of the first cut in cut line. 
    # \remarks An exception is raised if the specified direction is different from \c {'horizontal','vertical'}

    def capacitorLine( self, dy, abutmentBox_spacing , matchingSchemeRowIndex = 0  ):

        line = [ CapacitorUnit( self.device, self.capacitorType, [self.abutmentBoxPosition["XMin"], dy], capacitance = self.unitCapacitance ) ]
        self.createElementInCapacitorLine( line, matchingSchemeRowIndex,0 )                 
        limit  =   self.matrixDim["columns"] + 2 if self.dummyRing == True else self.matrixDim["columns"]
        for j in range(1, limit ) :
            line.append( CapacitorUnit( self.device, self.capacitorType, [line[j-1].abutmentBox.getXMax() + abutmentBox_spacing, dy], capacitance = self.unitCapacitance ) )
            self.createElementInCapacitorLine( line, matchingSchemeRowIndex,j )                 

        return line


    def createElementInCapacitorLine( self, capacitorList, matchingSchemeRowIndex,capListIndex ):
        
        if self.matchingMode == False :
           capacitorList[capListIndex].create( self.nets[0][0], self.nets[0][1] )
        else :

            if self.dummyRing == True:
                if (matchingSchemeRowIndex == 0 or matchingSchemeRowIndex == self.matrixDim["rows"] + 1 or capListIndex == 0 or capListIndex == self.matrixDim["columns"] + 1)  : 
                    [ t , b ] = [ self.nets[-1][0] , self.nets[-1][1] ]
                else :
                    k = self.matchingScheme[matchingSchemeRowIndex-1][capListIndex-1]
                    [ t , b ] = [ self.nets[k][0] , self.nets[k][1] ]

            else : 
                k = self.matchingScheme[matchingSchemeRowIndex][capListIndex]
                [ t , b ] = [ self.nets[k][0] , self.nets[k][1] ]
            capacitorList[capListIndex].create( t, b )         

        return 

    ## Draws a matrix of identical capacitors. The matrix is iterativelly constructed. At every iteration, a new horizontal line of capacitors is drawn. 
    # \return a nested list of elementary capacitors.

    def capacitorMatrix( self, abutmentBox_spacing = 0 ):

        matrix = [ self.capacitorLine( self.abutmentBoxPosition["YMin"], abutmentBox_spacing,0 ) ]
        limit  =   self.matrixDim["rows"] + 2 if self.dummyRing == True else self.matrixDim["rows"]
        for i in range( 1, limit ):
            matrix.append( self.capacitorLine( matrix[i-1][-1].abutmentBox.getYMax() + abutmentBox_spacing, abutmentBox_spacing, i) )

        return matrix


    def dummyLine( self, direction, dx, dy ):

        dummyList = [ CapacitorUnit( self.device, self.capacitorType, [dx, dy], capacitance = self.unitCapacitance ) ]
        dummyList[0].create( self.nets[-1][0], self.nets[-1][1] )

        if direction == 'vertical':
            for i in range(1, self.matrixDim["rows"] + 2):
                dummyList.append( CapacitorUnit( self.device, self.capacitorType, [dx, dummyList[i-1].abutmentBox.getYMax() + self.abutmentBox_spacing], capacitance = self.unitCapacitance ) )
                dummyList[i].create(self.nets[-1][0], self.nets[-1][1]) 

        elif direction == 'horizontal':
            for j in range(1, self.matrixDim["columns"] + 2):
                dummyList.append( CapacitorUnit( self.device, self.capacitorType, [dummyList[j-1].abutmentBox.getXMax() + self.abutmentBox_spacing, dy], capacitance = self.unitCapacitance ) )
                dummyList[j].create(self.nets[-1][0], self.nets[-1][1]) 

        else : raise Error(1,'dummyLine() : Direction must be either "horizontal" or "vertical".' %direction)

        return dummyList




    def computeAbutmentBoxDimensions( self, abutmentBox_spacing ):

        abutmentBoxDimensions  = {}
        capDim                 = self.getCapDim() 
        
        [widthFactor1 , widthFactor2 ] = [self.matrixDim["columns"] , (self.matrixDim["columns"] - 1)] if self.dummyRing == False else [self.matrixDim["columns"] + 2, (self.matrixDim["columns"] + 1)]
        [heightFactor1, heightFactor2] = [self.matrixDim["rows"   ] , (self.matrixDim["rows"   ] - 1)] if self.dummyRing == False else [self.matrixDim["rows"   ] + 2, (self.matrixDim["rows"   ] + 1)]
        abutmentBoxDimElement  = CapacitorUnit.computeAbutmentBoxDimensions(self, capDim) 
        abutmentBoxWidth       =  widthFactor1 *abutmentBoxDimElement["width" ] + widthFactor2 *abutmentBox_spacing
        abutmentBoxHeight      =  heightFactor1*abutmentBoxDimElement["height"] + heightFactor2*abutmentBox_spacing        

        abutmentBoxDimensions  = { "XMin" : self.abutmentBoxPosition["XMin"], "YMin" : self.abutmentBoxPosition["YMin"], "width" : abutmentBoxWidth , "height" : abutmentBoxHeight,  "surface" :  abutmentBoxWidth*abutmentBoxHeight}

        return abutmentBoxDimensions


    ## Draws the abutment box of the matrix or campact capacitor. 

    def drawAbutmentBox( self, abutmentBox_spacing = 0 ):

        abutmentBoxDimensions = self.computeAbutmentBoxDimensions(abutmentBox_spacing)

        self.abutmentBox      = Box(self.abutmentBoxPosition["XMin"],self.abutmentBoxPosition["YMin"],abutmentBoxDimensions["width"]+self.abutmentBoxPosition["XMin"],abutmentBoxDimensions["height"]+self.abutmentBoxPosition["YMin"])
        self.device.setAbutmentBox( self.abutmentBox )

        return 

    def computeBondingBoxDimensions( self ):

        bondingBoxDimensions  = {}
        abutmentBoxDimensions = self.computeAbutmentBoxDimensions( self.abutmentBox_spacing )
        for key in abutmentBoxDimensions:
            if key != "XMin" and key != "YMin" : bondingBoxDimensions[key] = abutmentBoxDimensions[key]

        return bondingBoxDimensions

    ## Draws the routing layers connecting the bottom plate in the matrix of capacitors. First, the relative positions of the routing layer is of the is extracted from the elementary capacitor instance. Then, its width is computed in a way to connect adjacent plates. Then, the routing layers are iterativelly drawn.
    #  The two borders are . 

    def drawBottomPlatesRLayers( self, bottomPlateRLayer, drawnCapacitor ):

        [ dySourceBottom, dyTargetBottom ] = [ drawnCapacitor[0][0].getBotPlateRLayerYMin   (), drawnCapacitor[-1][0].getBotPlateRLayerYMax() ]        
        if ( self.matrixDim["columns"] > 1 ) :
            bottomPlateRLayer_width = ( drawnCapacitor[0][1].getBotPlateLeftRLayerXMax() - drawnCapacitor[0][0].getBotPlateRightRLayerXMin() ) 
            bottomMetalXCenters     = []

            for j in range( 0,self.matrixDim["columns"]-1 ):

                bottomMetalXCenters.append( drawnCapacitor[0][j].getBotPlateRightRLayerXMin() + bottomPlateRLayer_width/2 )
                Vertical.create ( self.nets[0][1], bottomPlateRLayer, bottomMetalXCenters[j], bottomPlateRLayer_width, dySourceBottom, dyTargetBottom ) 
       
        bordersXMin = [ drawnCapacitor[0][0].getBottomPlateLeftCutXMin(), drawnCapacitor[0][-1].getBottomPlateRightCutXMin() ]
      
        for j in range( 0,2):
            Vertical.create ( self.nets[0][1], bottomPlateRLayer , bordersXMin[j], drawnCapacitor[0][0].getBotPlateRLayerWidth(), dySourceBottom, dyTargetBottom ) 
      
        return
 
     
    ## Draws the routing layers connecting the top plates in the matrix of capacitors. First, the relative positions of the routing layers is of the is extracted from the elementary capacitor instance. Then, its width is computed in a way to connect adjacent plates. Then, the routing layers are iterativelly drawn. 
    #  The two borders are . 
    # \remarks An exception is raised if the number of rows in the matrix is lower than 2.

    def drawTopPlatesRLayers( self, topPlateRLayer, drawnCapacitor):
      
        if ( self.matrixDim["rows"] > 1 ) :
            for j in range( 0,self.matrixDim["columns"] ):
                Vertical.create ( self.nets[0][0], topPlateRLayer , drawnCapacitor[0][j].getTopPlateRLayerXCenter(), drawnCapacitor[0][j].getTopPlateRLayerWidth() , drawnCapacitor[0][0].getTopPlateRLayerYMin(), drawnCapacitor[-1][0].getTopPlateRLayerYMax() ) 

        #else : print('The matrix does not contain enough rows')    #com4 verify if this else is needed  

        return

    ## \return The width of the vertical routing tracks in matching mode. 
    #  \remark This function is useful in matching mode, ie., in \C RoutCapacitor class, when routing the two capacitors.
    def getVerticalRoutingTrack_width ( self ) : return self.vRoutingTrack_width
      

    def getAbutmentBox_spacing        ( self ) : return self.abutmentBox_spacing

    ## \return A dictionary contaning capacitor matrix's dimensions
    def getMatrixDim                  ( self ) : return self.matrixDim      


    def getCapDim                     ( self ) : return self.unitCapDim if self.doMatrix == True else self.compactCapDim

 #   def getMatchingMode               ( self ) : return self.matchingMode


    def getVRoutingTrack_spacing      ( self ) : return self.minSpacing_vRoutingTrack

    def getvRoutingTrack_width        ( self ) : return self.vRoutingTrack_width

    ## \return the matching scheme. The function is useful in  \c RoutMatchedCapacitor class to load \c self.matchingScheme attribute.
    def getMatchingScheme             ( self ) : return self.matchingScheme 


def scriptMain( **kw ):

   editor = None
   if  kw.has_key('editor') and kw['editor']:
       editor = kw['editor']

   UpdateSession.open()
   device = AllianceFramework.get().createCell( 'capacitor' )
   device.setTerminal( True )

   bottomPlate_net0 = Net.create( device, 'b0' )
   bottomPlate_net1 = Net.create( device, 'b1' )
   bottomPlate_net2 = Net.create( device, 'b2' )
   bottomPlate_net3 = Net.create( device, 'b3' )
   bottomPlate_net0.setExternal( True )  
   bottomPlate_net1.setExternal( True )  
   bottomPlate_net2.setExternal( True )  
   bottomPlate_net3.setExternal( True )  
   b0  = device.getNet("b0")
   b1  = device.getNet("b1")
   b2  = device.getNet("b2")
   b3  = device.getNet("b3")

   topPlate_net0    = Net.create( device, 't0' )
   topPlate_net1    = Net.create( device, 't1' )
   topPlate_net2    = Net.create( device, 't2' )
   topPlate_net3    = Net.create( device, 't3' )
   topPlate_net0.setExternal( True )  
   topPlate_net1.setExternal( True )  
   topPlate_net2.setExternal( True )  
   topPlate_net3.setExternal( True )  
   t0      = device.getNet("t0")
   t1     = device.getNet("t1")
   t2     = device.getNet("t2")
   t3     = device.getNet("t3")
   if editor:
       UpdateSession.close()
       editor.setCell( device )
       editor.fit()  
       UpdateSession.open()
   nets   = [[t0, b0] , [t1, b1] , [t2, b2] ] # [t3, b3] ]
   capacitorInstance = CapacitorStack( device, [750,750], 'MIMCap', [0,0], nets,unitCap = 93, matrixDim = [4,4], matchingMode = True, matchingScheme =  [ [1,0,1,0] , [0,1,0,1] , [1,0,1,0] , [0,1,0,1] ], dummyRing = True)
  #capacitorInstance = CapacitorStack( device, [1488], 'MIMCap', [0,0], nets,unitCap = 93, matrixDim = [4,4], dummyRing = True)
  #capacitorInstance = CapacitorStack( device, {"C1" : 558, "C2" : 558, "C3" : 372}, 'MIMCap', [0,0], nets, unitCap = 93, matrixDim = [4,4], matchingMode = True, matchingScheme =  [ ['C2','C1','C2','C1'] , ['C1','C2','C1','C2'] , ['C2','C1','C2','C1'] , ['C3','C3','C3','C3'] ])
  #capacitorInstance = CapacitorStack( device, {"C1" : 558, "C2" : 558, "C3" : 186, "C4" : 186}, 'MIMCap', [0,0], nets, unitCap = 93, matrixDim = [4,4], matchingMode = True, matchingScheme =  [ ['C2','C1','C2','C1'] , ['C1','C2','C1','C2'] , ['C2','C1','C2','C1'] , ['C3','C3','C4','C4'] ])

   capacitor = capacitorInstance.create()
   #print(toPhY(capacitor["width"])) 
   #print(toPhY(capacitor["height"])) 

   AllianceFramework.get().saveCell( device, Catalog.State.Views )

   return True
