Beispiel #1
0
    def get_i_Jac(self, xVec):
        """
        Calculate total current and Jacobian

        Returns (iVec, Jac)::

            iVec = G' xVec + i'(xVec)
            Jac = G' + (di'/dx)(xVec)

        xVec: input vector of nodal voltages. 

        iVec: output vector of currents

        Jac: system Jacobian
        """
        # Linear contribution
        self.iVec[:] = self.Gp * xVec
        # Nonlinear contribution
        self._mbase = self._mbaseLin
        for elem in self.ckt.nD_nlinElem:
            # first have to retrieve port voltages from xVec
            xin = np.zeros(len(elem.controlPorts))
            set_xin(xin, elem.nD_vpos, elem.nD_vneg, xVec)
            (outV, outJac) = elem.eval_and_deriv(xin)
            # Update iVec and Jacobian now. outV may have extra charge
            # elements but they are not used in the following
            set_i(self.iVec, elem.nD_cpos, elem.nD_cneg, outV)
            set_i(self.iVec, elem.nD_qpos, elem.nD_qneg,
                  self.im.a0 * outV[len(elem.csOutPorts):])
            self._set_Jac(self.Jaccoo, outJac, *elem.nD_csidx)
            qJac = self.im.a0 * outJac[len(elem.csOutPorts):, :]
            self._set_Jac(self.Jaccoo, qJac, *elem.nD_qsidx)

        return (self.iVec, self.Jaccoo)
Beispiel #2
0
 def _create_tape(self, xVec):
     """
     Generate main CppAD tape
 
     Normally there is no need to call this function manually as tapes
     are generated as needed.
     """
     # Create derivative vector
     a_xVec = ad.independent(xVec)
     # perform actual calculation 
     # Linear contribution 
     a_out = np.dot(self.G, a_xVec)
     xin = np.zeros(len(xVec), dtype = type(a_out[0]))
     # Nonlinear contribution
     for elem in self.ckt.nD_nlinElem:
         # first have to retrieve port voltages from a_xVec
         xin[:len(elem.controlPorts)] = 0.
         #import pdb; pdb.set_trace()
         set_xin(xin, elem.nD_vpos, elem.nD_vneg, a_xVec)
         (outV, qVec) = elem.eval_cqs(xin)
         # Update iVec. outV may have extra charge elements but
         # they are not used in the following
         set_i(a_out, elem.nD_cpos, elem.nD_cneg, outV)
     # Save main function tape
     self._func = ad.adfun(a_xVec, a_out)
     # optimize main function tape
     self._func.optimize()
Beispiel #3
0
    def get_i_Jac(self, xVec):
        """
        Calculate total current and Jacobian

        Returns (iVec, Jac)::

            iVec = G xVec + i(xVec)
            Jac = G + (di/dx)(xVec)

        xVec: input vector of nodal voltages. 

        iVec: output vector of currents

        Jac: system Jacobian
        """
        # Erase sparse matrix
        self.Jac.scale(0.)
        # Linear contribution
        self.G.matvec(xVec, self.iVec)
        self.Jac.shift(1., self.Gll)
        # Nonlinear contribution
        for elem in self.ckt.nD_nlinElem:
            # first have to retrieve port voltages from xVec
            xin = np.zeros(len(elem.controlPorts))
            set_xin(xin, elem.nD_vpos, elem.nD_vneg, xVec)
            (outV, outJac) = elem.eval_and_deriv(xin)
            # Update iVec and Jacobian now. outV may have extra charge
            # elements but they are not used in the following
            set_i(self.iVec, elem.nD_cpos, elem.nD_cneg, outV)
            set_Jac(self.Jac, elem.nD_cpos, elem.nD_cneg, 
                    elem.nD_vpos, elem.nD_vneg, outJac)

        return (self.iVec, self.Jac)
Beispiel #4
0
    def get_i(self, xVec):
        """
        Calculate total current

        returns iVec = G' xVec + i'(xVec)

        xVec: input vector of nodal voltages. 

        iVec: output vector of currents
        """
        # Linear contribution
        self.iVec[:] = self.Gp * xVec
        dcounter = 0
        # Nonlinear contribution
        for elem in self.ckt.nD_nlinElem:
            # first have to retrieve port voltages from xVec
            xin = np.zeros(elem.nD_nxin)
            set_xin(xin, elem.nD_vpos, elem.nD_vneg, xVec)
            if elem.nDelays:
                # Apply delay to port voltages
                for i in xrange(-elem.nDelays, 0):
                    (xin[i], dc) = delay_interp(elem.nD_delay[i], xin[i],
                                                self.im.h, self._dpsteps,
                                                self.tdVecList[dcounter][i])
                dcounter += 1
            outV = elem.eval(xin)
            # Update iVec. outV may have extra charge elements but
            # they are not used in the following
            set_i(self.iVec, elem.nD_cpos, elem.nD_cneg, outV)
            set_i(self.iVec, elem.nD_qpos, elem.nD_qneg,
                  self.im.a0 * outV[len(elem.csOutPorts):])
        return self.iVec
Beispiel #5
0
    def get_i(self, xVec):
        """
        Calculate total current

        returns iVec = G' xVec + i'(xVec)

        xVec: input vector of nodal voltages. 

        iVec: output vector of currents
        """
        # Linear contribution
        self.iVec[:] = self.Gp * xVec
        dcounter = 0
        # Nonlinear contribution
        for elem in self.ckt.nD_nlinElem:
            # first have to retrieve port voltages from xVec
            xin = np.zeros(elem.nD_nxin)
            set_xin(xin, elem.nD_vpos, elem.nD_vneg, xVec)
            if elem.nDelays:
                # Apply delay to port voltages
                for i in xrange(-elem.nDelays, 0):
                    (xin[i], dc) = delay_interp(elem.nD_delay[i], 
                                                xin[i], self.im.h, 
                                                self._dpsteps, 
                                                self.tdVecList[dcounter][i])
                dcounter += 1
            outV = elem.eval(xin)
            # Update iVec. outV may have extra charge elements but
            # they are not used in the following
            set_i(self.iVec, elem.nD_cpos, elem.nD_cneg, outV)
            set_i(self.iVec, elem.nD_qpos, elem.nD_qneg, 
                  self.im.a0 * outV[len(elem.csOutPorts):])
        return self.iVec
Beispiel #6
0
    def get_i_Jac(self, xVec):
        """
        Calculate total current and Jacobian

        Returns (iVec, Jac)::

            iVec = G xVec + i(xVec)
            Jac = G + (di/dx)(xVec)

        xVec: input vector of nodal voltages. 

        iVec: output vector of currents

        Jac: system Jacobian
        """
        # Linear contribution
        self.iVec[:] = self.G * xVec
        # Nonlinear contribution
        self._mbase = self._mbaseLin
        for elem in self.ckt.nD_nlinElem:
            # first have to retrieve port voltages from xVec
            xin = np.zeros(elem.nD_nxin)
            set_xin(xin, elem.nD_vpos, elem.nD_vneg, xVec)
            (outV, outJac) = elem.eval_and_deriv(xin)
            # Update iVec and Jacobian now. outV may have extra charge
            # elements but they are not used in the following
            set_i(self.iVec, elem.nD_cpos, elem.nD_cneg, outV)
            self.set_Jac(self.Jaccoo, outJac, *elem.nD_csidx)

        return (self.iVec, self.Jaccoo)
Beispiel #7
0
    def get_i_Jac(self, xVec):
        """
        Calculate total current and Jacobian

        Returns (iVec, Jac)::

            iVec = G xVec + i(xVec)
            Jac = G + (di/dx)(xVec)

        xVec: input vector of nodal voltages. 

        iVec: output vector of currents

        Jac: system Jacobian
        """
        # Erase sparse matrix
        self.Jac.scale(0.)
        # Linear contribution
        self.G.matvec(xVec, self.iVec)
        self.Jac.shift(1., self.Gll)
        # Nonlinear contribution
        for elem in self.ckt.nD_nlinElem:
            # first have to retrieve port voltages from xVec
            xin = np.zeros(len(elem.controlPorts))
            set_xin(xin, elem.nD_vpos, elem.nD_vneg, xVec)
            (outV, outJac) = elem.eval_and_deriv(xin)
            # Update iVec and Jacobian now. outV may have extra charge
            # elements but they are not used in the following
            set_i(self.iVec, elem.nD_cpos, elem.nD_cneg, outV)
            set_Jac(self.Jac, elem.nD_cpos, elem.nD_cneg, elem.nD_vpos,
                    elem.nD_vneg, outJac)

        return (self.iVec, self.Jac)
Beispiel #8
0
 def _create_tape(self, xVec):
     """
     Generate main CppAD tape
 
     Normally there is no need to call this function manually as tapes
     are generated as needed.
     """
     # Create derivative vector
     a_xVec = ad.independent(xVec)
     # perform actual calculation
     # Linear contribution
     a_out = np.dot(self.G, a_xVec)
     xin = np.zeros(len(xVec), dtype=type(a_out[0]))
     # Nonlinear contribution
     for elem in self.ckt.nD_nlinElem:
         # first have to retrieve port voltages from a_xVec
         xin[:len(elem.controlPorts)] = 0.
         #import pdb; pdb.set_trace()
         set_xin(xin, elem.nD_vpos, elem.nD_vneg, a_xVec)
         (outV, qVec) = elem.eval_cqs(xin)
         # Update iVec. outV may have extra charge elements but
         # they are not used in the following
         set_i(a_out, elem.nD_cpos, elem.nD_cneg, outV)
     # Save main function tape
     self._func = ad.adfun(a_xVec, a_out)
     # optimize main function tape
     self._func.optimize()
Beispiel #9
0
    def get_i_Jac(self, xVec):
        """
        Calculate total current and Jacobian

        Returns (iVec, Jac)::

            iVec = G' xVec + i'(xVec)
            Jac = G' + (di'/dx)(xVec)

        xVec: input vector of nodal voltages. 

        iVec: output vector of currents

        Jac: system Jacobian
        """
        # Linear contribution
        self.iVec[:] = self.Gp * xVec
        dcounter = 0
        # Nonlinear contribution
        self._mbase = self._mbaseLin
        for elem in self.ckt.nD_nlinElem:
            # first have to retrieve port voltages from xVec
            xin = np.zeros(elem.nD_nxin)
            set_xin(xin, elem.nD_vpos, elem.nD_vneg, xVec)
            if elem.nDelays:
                # Derivative coefficients
                dc = np.empty_like(elem.nD_delay)
                # Apply delay to port voltages
                for i in xrange(-elem.nDelays, 0):
                    (xin[i], dc[i]) = delay_interp(elem.nD_delay[i], 
                                                   xin[i], self.im.h, 
                                                   self._dpsteps, 
                                                   self.tdVecList[dcounter][i])
                dcounter += 1
                (outV, outJac) = elem.eval_and_deriv(xin)
                # Multiply Jacobian columns by derivative factors
                for i in xrange(-elem.nDelays, 0):
                    outJac[:,i] *= dc[i]
            else:
                (outV, outJac) = elem.eval_and_deriv(xin)
            # Update iVec and Jacobian now. outV may have extra charge
            # elements but they are not used in the following
            set_i(self.iVec, elem.nD_cpos, elem.nD_cneg, outV)
            set_i(self.iVec, elem.nD_qpos, elem.nD_qneg, 
                  self.im.a0 * outV[len(elem.csOutPorts):])
            self.set_Jac(self.Jaccoo, outJac, *elem.nD_csidx)
            qJac = self.im.a0 * outJac[len(elem.csOutPorts):,:]
            self.set_Jac(self.Jaccoo, qJac, *elem.nD_qsidx)

        return (self.iVec, self.Jaccoo)
Beispiel #10
0
 def update_q(self, xVec):
     """ 
     Recalculate qVec for a given value of xVec
     """
     # Calculate total q vector
     # Calculate linear charges first
     self.qVec[:] = self.C * xVec
     for elem in self.ckt.nD_nlinElem:
         # first have to retrieve port voltages from xVec
         xin = np.zeros(elem.nD_nxin)
         set_xin(xin, elem.nD_vpos, elem.nD_vneg, xVec)
         outV = elem.eval(xin)
         set_i(self.qVec, elem.nD_qpos, elem.nD_qneg, outV[len(elem.csOutPorts) :])
     return self.qVec
Beispiel #11
0
    def get_i_Jac(self, xVec):
        """
        Calculate total current and Jacobian

        Returns (iVec, Jac)::

            iVec = G' xVec + i'(xVec)
            Jac = G' + (di'/dx)(xVec)

        xVec: input vector of nodal voltages. 

        iVec: output vector of currents

        Jac: system Jacobian
        """
        # Linear contribution
        self.iVec[:] = self.Gp * xVec
        dcounter = 0
        # Nonlinear contribution
        self._mbase = self._mbaseLin
        for elem in self.ckt.nD_nlinElem:
            # first have to retrieve port voltages from xVec
            xin = np.zeros(elem.nD_nxin)
            set_xin(xin, elem.nD_vpos, elem.nD_vneg, xVec)
            if elem.nDelays:
                # Derivative coefficients
                dc = np.empty_like(elem.nD_delay)
                # Apply delay to port voltages
                for i in xrange(-elem.nDelays, 0):
                    (xin[i], dc[i]) = delay_interp(elem.nD_delay[i], xin[i],
                                                   self.im.h, self._dpsteps,
                                                   self.tdVecList[dcounter][i])
                dcounter += 1
                (outV, outJac) = elem.eval_and_deriv(xin)
                # Multiply Jacobian columns by derivative factors
                for i in xrange(-elem.nDelays, 0):
                    outJac[:, i] *= dc[i]
            else:
                (outV, outJac) = elem.eval_and_deriv(xin)
            # Update iVec and Jacobian now. outV may have extra charge
            # elements but they are not used in the following
            set_i(self.iVec, elem.nD_cpos, elem.nD_cneg, outV)
            set_i(self.iVec, elem.nD_qpos, elem.nD_qneg,
                  self.im.a0 * outV[len(elem.csOutPorts):])
            self.set_Jac(self.Jaccoo, outJac, *elem.nD_csidx)
            qJac = self.im.a0 * outJac[len(elem.csOutPorts):, :]
            self.set_Jac(self.Jaccoo, qJac, *elem.nD_qsidx)

        return (self.iVec, self.Jaccoo)
Beispiel #12
0
 def update_q(self, xVec):
     """ 
     Recalculate qVec for a given value of xVec
     """
     # Calculate total q vector
     # Calculate linear charges first
     self.qVec[:] = self.C * xVec
     for elem in self.ckt.nD_nlinElem:
         # first have to retrieve port voltages from xVec
         xin = np.zeros(len(elem.controlPorts))
         set_xin(xin, elem.nD_vpos, elem.nD_vneg, xVec)
         outV = elem.eval(xin)
         set_i(self.qVec, elem.nD_qpos, elem.nD_qneg,
               outV[len(elem.csOutPorts):])
     return self.qVec
Beispiel #13
0
    def get_i(self, xVec):
        """
        Calculate total current

        returns iVec = G xVec + i(xVec)

        xVec: input vector of nodal voltages. 

        iVec: output vector of currents
        """
        # Linear contribution
        self.iVec[:] = self.G * xVec
        # Nonlinear contribution
        for elem in self.ckt.nD_nlinElem:
            # first have to retrieve port voltages from xVec
            xin = np.zeros(len(elem.controlPorts))
            set_xin(xin, elem.nD_vpos, elem.nD_vneg, xVec)
            outV = elem.eval(xin)
            # Update iVec. outV may have extra charge elements but
            # they are not used in the following
            set_i(self.iVec, elem.nD_cpos, elem.nD_cneg, outV)
        return self.iVec
Beispiel #14
0
    def get_i(self, xVec):
        """
        Calculate total current

        returns iVec = G xVec + i(xVec)

        xVec: input vector of nodal voltages. 

        iVec: output vector of currents
        """
        # Linear contribution
        self.G.matvec(xVec, self.iVec)
        # Nonlinear contribution
        for elem in self.ckt.nD_nlinElem:
            # first have to retrieve port voltages from xVec
            xin = np.zeros(len(elem.controlPorts))
            set_xin(xin, elem.nD_vpos, elem.nD_vneg, xVec)
            outV = elem.eval(xin)
            # Update iVec. outV may have extra charge elements but
            # they are not used in the following
            set_i(self.iVec, elem.nD_cpos, elem.nD_cneg, outV)
        return self.iVec
Beispiel #15
0
def get_i_derivatives(device, parList, xVec, force=False):
    """
    Returns DC current derivatives with respect to given parameters

    Inputs: 

       device (linear or nonlinear)
       parList: list of parameters as returned by param.get_float_attributes()
       xVec: nodal vector
       force: if True, force tape re-generation

    Outputs: 

       output currents Jacobian

    Possible side effect: operating point attributes lost in internal
    terminals if tape is created
    """
    # Create input vector from parameter values
    pVec = np.array([val for name, val in parList])
    inVec = pVec

    if device.isNonlinear:
        # Add controlling voltages to input: AD tape can be reused for
        # different operating points
        xin = np.zeros(device.nD_nxin)
        nd.set_xin(xin, device.nD_vpos, device.nD_vneg, xVec)
        #assert(np.max(xin - device.nD_xOP) == 0)
        inVec = np.concatenate((pVec, xin), axis=0)

    # Use taped function if it exists
    if (not hasattr(device, '_sensF')) or force:
        device._sensF = create_sensitivity_tape(device, parList, inVec)

    f = device._sensF

    # Generate current derivatives: must use forward call only because
    # reverse creates problems with calculations using condassign in
    # the wrong way (no longer needed, but forward still OK because no
    # need to calculate derivatives respect to nonlinear control
    # voltages.
    f.forward(0, inVec)
    w = np.zeros_like(inVec)
    Ji = np.zeros((len(xVec), len(pVec)), dtype=float)
    for i in range(len(pVec)):
        # In the future w[i] can be set equal to delta_p then forward
        # returns the delta_i
        w[i] = 1.
        doutdp = f.forward(1, w)
        #print(f.forward(1,w))
        # Combine derivatives in output Jacobian
        if device.linearVCCS:
            for k, vccs in enumerate(device.nD_linVCCS):
                # Controlling voltage: xVec[col1] - xVec[col2]
                vc = 0.
                col = vccs[1]
                if col >= 0:
                    vc = xVec[col]
                col = vccs[3]
                if col >= 0:
                    vc -= xVec[col]
                # Output current derivative = dg/dp * vc
                di = vc * doutdp[k]
                row = vccs[0]
                if row != -1:
                    Ji[row, i] += di
                row = vccs[2]
                if row != -1:
                    Ji[row, i] -= di
        if device.isDCSource:
            row = device.sourceOutput
            k = len(device.nD_linVCCS)
            if row[0] >= 0:
                Ji[row[0], i] -= doutdp[k]
            if row[1] >= 0:
                Ji[row[1], i] += doutdp[k]
        if device.isNonlinear:
            diNL = doutdp[-len(device.csOutPorts):]
            nd.set_i(Ji[:, i], device.nD_cpos, device.nD_cneg, diNL)
        # Unselect column
        w[i] = 0.

    #print(f.jacobian(inVec))
    return Ji
Beispiel #16
0
def get_i_derivatives(device, parList, xVec, force=False):
    """
    Returns DC current derivatives with respect to given parameters

    Inputs: 

       device (linear or nonlinear)
       parList: list of parameters as returned by param.get_float_attributes()
       xVec: nodal vector
       force: if True, force tape re-generation

    Outputs: 

       output currents Jacobian

    Possible side effect: operating point attributes lost in internal
    terminals if tape is created
    """
    # Create input vector from parameter values
    pVec = np.array([val for name, val in parList])
    inVec = pVec

    if device.isNonlinear:
        # Add controlling voltages to input: AD tape can be reused for
        # different operating points
        xin = np.zeros(device.nD_nxin)
        nd.set_xin(xin, device.nD_vpos, device.nD_vneg, xVec)
        #assert(np.max(xin - device.nD_xOP) == 0)
        inVec = np.concatenate((pVec, xin), axis=0)

    # Use taped function if it exists
    if (not hasattr(device, '_sensF')) or force:
        device._sensF = create_sensitivity_tape(device, parList, inVec)

    f = device._sensF

    # Generate current derivatives: must use forward call only because
    # reverse creates problems with calculations using condassign in
    # the wrong way (no longer needed, but forward still OK because no
    # need to calculate derivatives respect to nonlinear control
    # voltages.
    f.forward(0, inVec)
    w = np.zeros_like(inVec)
    Ji = np.zeros((len(xVec), len(pVec)), dtype = float)
    for i in range(len(pVec)):
        # In the future w[i] can be set equal to delta_p then forward
        # returns the delta_i
        w[i] = 1.
        doutdp = f.forward(1, w)
        #print(f.forward(1,w))
        # Combine derivatives in output Jacobian
        if device.linearVCCS:
            for k,vccs in enumerate(device.nD_linVCCS):
                # Controlling voltage: xVec[col1] - xVec[col2]
                vc = 0.
                col = vccs[1]
                if col >= 0:
                    vc = xVec[col]
                col = vccs[3]
                if col >= 0:
                    vc -= xVec[col]
                # Output current derivative = dg/dp * vc
                di = vc * doutdp[k]
                row = vccs[0]
                if row != -1:
                    Ji[row, i] += di
                row = vccs[2]
                if row != -1:
                    Ji[row, i] -= di
        if device.isDCSource:
            row = device.sourceOutput
            k = len(device.nD_linVCCS)
            if row[0] >= 0:
                Ji[row[0], i] -= doutdp[k]
            if row[1] >= 0:
                Ji[row[1], i] += doutdp[k]
        if device.isNonlinear:
            diNL = doutdp[-len(device.csOutPorts):]
            nd.set_i(Ji[:,i], device.nD_cpos, device.nD_cneg, diNL)
        # Unselect column
        w[i] = 0.

    #print(f.jacobian(inVec))
    return Ji