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)
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()
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)
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
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)
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()
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)
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
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)
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
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
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
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
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