Exemplo n.º 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
        """
        # 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)
Exemplo n.º 2
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)
Exemplo n.º 3
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()
Exemplo n.º 4
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)
Exemplo n.º 5
0
    def set_IC(self, h):
        """
        Set initial conditions (ICs)

        h: (initial) time step

        Retrieves ICs from DC operating point info in elements
        """
        # Nodal variables
        xVec = np.zeros(self.ckt.nD_dimension)
        # Get nodal voltages for tstart
        for i, term in enumerate(self.ckt.nD_termList):
            xVec[i] = term.nD_vOP
        # Calculate total charges
        self.update_q(xVec)
        # initialize integration element
        self.im.init(h, self.qVec)
        # Generate Gp
        self.update_Gp()
        # Initialize time delay structures
        nsteps = np.ceil(self.ckt.nD_maxDelay / h)
        self._dpsteps = nsteps * [h]
        for elem in self.ckt.nD_nlinElem:
            if elem.nDelays:
                self.tdVecList.append([])
                # first have to retrieve port voltages from xVec
                xin = np.zeros(elem.nD_nxin)
                set_xin(xin, elem.nD_vpos, elem.nD_vneg, xVec)
                # Extract delayed ports and append to element list
                for i in xrange(-elem.nDelays, 0):
                    self.tdVecList[-1].append(nsteps * [xin[i]])
Exemplo n.º 6
0
    def save_OP(self, xVec):
        """
        Save nodal voltages in terminals and set OP in elements

        The following information is saved:

          * The nodal voltage vector for the circuit (self.xop)

          * The nodal voltage in each terminal (term.nD_vOP)

          * The port voltages in each nonlinear device (elem.nD_xOP)

          * The operating point (OP) information in nonlinear devices

        """
        # Set nodal voltage of reference to zero
        self.ckt.nD_ref.nD_vOP = 0.
        # Save nodal vector
        self.xop = xVec
        for v,term in zip(xVec, self.ckt.nD_termList):
            term.nD_vOP = v
        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)
            # Set OP in element (discard return value)
            elem.nD_xOP = xin
            elem.get_OP(xin)
Exemplo n.º 7
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
Exemplo n.º 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()
Exemplo n.º 9
0
    def save_OP(self, xVec):
        """
        Save nodal voltages in terminals and set OP in elements

        The following information is saved:

          * The nodal voltage vector for the circuit (self.xop)

          * The nodal voltage in each terminal (term.nD_vOP)

          * The port voltages in each nonlinear device (elem.nD_xOP)

          * The operating point (OP) information in nonlinear devices

        """
        # Set nodal voltage of reference to zero
        self.ckt.nD_ref.nD_vOP = 0.
        # Save nodal vector
        self.xop = xVec
        for v, term in zip(xVec, self.ckt.nD_termList):
            term.nD_vOP = v
        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)
            # Set OP in element (discard return value)
            elem.nD_xOP = xin
            elem.get_OP(xin)
Exemplo n.º 10
0
    def set_IC(self, h):
        """
        Set initial conditions (ICs)

        h: (initial) time step

        Retrieves ICs from DC operating point info in elements
        """
        # Nodal variables
        xVec = np.zeros(self.ckt.nD_dimension)
        # Get nodal voltages for tstart
        for i,term in enumerate(self.ckt.nD_termList):
            xVec[i] = term.nD_vOP
        # Calculate total charges
        self.update_q(xVec)
        # initialize integration element
        self.im.init(h, self.qVec)
        # Generate Gp 
        self.update_Gp()
        # Initialize time delay structures
        nsteps = np.ceil(self.ckt.nD_maxDelay / h)
        self._dpsteps = nsteps * [h]
        for elem in self.ckt.nD_nlinElem:
            if elem.nDelays:
                self.tdVecList.append([])
                # first have to retrieve port voltages from xVec
                xin = np.zeros(elem.nD_nxin)
                set_xin(xin, elem.nD_vpos, elem.nD_vneg, xVec) 
                # Extract delayed ports and append to element list
                for i in xrange(-elem.nDelays, 0):
                    self.tdVecList[-1].append(nsteps * [xin[i]])
Exemplo n.º 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
        """
        # 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)
Exemplo n.º 12
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
Exemplo n.º 13
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)
Exemplo n.º 14
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
Exemplo n.º 15
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)
Exemplo n.º 16
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
Exemplo n.º 17
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
Exemplo n.º 18
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
Exemplo n.º 19
0
 def accept(self, xVec):
     """
     Accept xVec for current time step and store state
     """
     self.im.accept(self.update_q(xVec))
     # Append current time step to list
     self._dpsteps.append(self.im.h)
     # Check if we can safely remove last sample
     flag = False
     if sum(self._dpsteps[1:]) > self.ckt.nD_maxDelay:
         flag = True
         del self._dpsteps[0]
     # Store here xVec components. Optimally this would be
     # performed in the same loop as update_q, but kept here for
     # clarity.
     for tdVec, elem in zip(self.tdVecList, self.ckt.nD_delayElem):
         # first have to retrieve port voltages from xVec
         xin = np.zeros(elem.nD_nxin)
         set_xin(xin, elem.nD_vpos, elem.nD_vneg, xVec)
         # Extract delayed ports and append to element lists
         for i in xrange(-elem.nDelays, 0):
             tdVec[i].append(xin[i])
             if flag:
                 del tdVec[i][0]
Exemplo n.º 20
0
 def accept(self, xVec):
     """
     Accept xVec for current time step and store state
     """
     self.im.accept(self.update_q(xVec))
     # Append current time step to list
     self._dpsteps.append(self.im.h)
     # Check if we can safely remove last sample
     flag = False
     if sum(self._dpsteps[1:]) > self.ckt.nD_maxDelay:
         flag = True
         del self._dpsteps[0]
     # Store here xVec components. Optimally this would be
     # performed in the same loop as update_q, but kept here for
     # clarity.
     for tdVec,elem in zip(self.tdVecList, self.ckt.nD_delayElem):
         # first have to retrieve port voltages from xVec
         xin = np.zeros(elem.nD_nxin)
         set_xin(xin, elem.nD_vpos, elem.nD_vneg, xVec) 
         # Extract delayed ports and append to element lists
         for i in xrange(-elem.nDelays, 0):
             tdVec[i].append(xin[i])
             if flag:
                 del tdVec[i][0]
Exemplo n.º 21
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
Exemplo n.º 22
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