def ac_analysis(circ, start, nsteps, stop, step_type, xop=None, mna=None,\ AC=None, Nac=None, J=None, data_filename="stdout", verbose=3): """Performs an AC analysis of the circuit (described by circ). """ if data_filename == 'stdout': verbose = 0 #check step/start/stop parameters if start == 0: printing.print_general_error("AC analysis has start frequency = 0") sys.exit(5) if start > stop: printing.print_general_error("AC analysis has start > stop") sys.exit(1) if nsteps < 1: printing.print_general_error("AC analysis has number of steps <= 1") sys.exit(1) if step_type == options.ac_log_step: omega_iter = utilities.log_axis_iterator(stop, start, nsteps) elif step_type == options.ac_lin_step: omega_iter = utilities.lin_axis_iterator(stop, start, nsteps) else: printing.print_general_error("Unknown sweep type.") sys.exit(1) tmpstr = "Vea =", options.vea, "Ver =", options.ver, "Iea =", options.iea, "Ier =", \ options.ier, "max_ac_nr_iter =", options.ac_max_nr_iter printing.print_info_line((tmpstr, 5), verbose) del tmpstr printing.print_info_line(("Starting AC analysis: ", 1), verbose) tmpstr = "w: start = %g Hz, stop = %g Hz, %d steps" % (start, stop, nsteps) printing.print_info_line((tmpstr, 3), verbose) del tmpstr #It's a good idea to call AC with prebuilt MNA matrix if the circuit is big if mna is None: (mna, N) = dc_analysis.generate_mna_and_N(circ) del N mna = utilities.remove_row_and_col(mna) if Nac is None: Nac = generate_Nac(circ) Nac = utilities.remove_row(Nac, rrow=0) if AC is None: AC = generate_AC(circ, [mna.shape[0], mna.shape[0]]) AC = utilities.remove_row_and_col(AC) if circ.is_nonlinear(): if J is not None: pass # we used the supplied linearization matrix else: if xop is None: printing.print_info_line( ("Starting OP analysis to get a linearization point...", 3), verbose, print_nl=False) #silent OP xop = dc_analysis.op_analysis(circ, verbose=0) if xop is None: #still! Then op_analysis has failed! printing.print_info_line(("failed.", 3), verbose) printing.print_general_error( "OP analysis failed, no linearization point available. Quitting." ) sys.exit(3) else: printing.print_info_line(("done.", 3), verbose) printing.print_info_line(("Linearization point (xop):", 5), verbose) if verbose > 4: xop.print_short() printing.print_info_line(("Linearizing the circuit...", 5), verbose, print_nl=False) J = generate_J(xop=xop.asmatrix(), circ=circ, mna=mna, Nac=Nac, data_filename=data_filename, verbose=verbose) printing.print_info_line((" done.", 5), verbose) # we have J, continue else: #not circ.is_nonlinear() # no J matrix is required. J = 0 printing.print_info_line(("MNA (reduced):", 5), verbose) printing.print_info_line((str(mna), 5), verbose) printing.print_info_line(("AC (reduced):", 5), verbose) printing.print_info_line((str(AC), 5), verbose) printing.print_info_line(("J (reduced):", 5), verbose) printing.print_info_line((str(J), 5), verbose) printing.print_info_line(("Nac (reduced):", 5), verbose) printing.print_info_line((str(Nac), 5), verbose) sol = results.ac_solution(circ, ostart=start, ostop=stop, opoints=nsteps, stype=step_type, op=xop, outfile=data_filename) # setup the initial values to start the iteration: nv = len(circ.nodes_dict) j = numpy.complex('j') Gmin_matrix = dc_analysis.build_gmin_matrix(circ, options.gmin, mna.shape[0], verbose) iter_n = 0 # contatore d'iterazione #printing.print_results_header(circ, fdata, print_int_nodes=options.print_int_nodes, print_omega=True) printing.print_info_line(("Solving... ", 3), verbose, print_nl=False) tick = ticker.ticker(increments_for_step=1) tick.display(verbose > 1) x = xop for omega in omega_iter: (x, error, solved, n_iter) = dc_analysis.dc_solve(mna=(mna + numpy.multiply(j*omega, AC) + J), \ Ndc=Nac, Ntran=0, circ=circuit.circuit(title="Dummy circuit for AC", filename=None), Gmin=Gmin_matrix, x0=x, \ time=None, locked_nodes=None, MAXIT=options.ac_max_nr_iter, skip_Tt=True, verbose=0) if solved: tick.step(verbose > 1) iter_n = iter_n + 1 # hooray! sol.add_line(omega, x) else: break tick.hide(verbose > 1) if solved: printing.print_info_line(("done.", 1), verbose) ret_value = sol else: printing.print_info_line(("failed.", 1), verbose) ret_value = None return ret_value
def transient_analysis(circ, tstart, tstep, tstop, method=TRAP, x0=None, mna=None, N=None, \ D=None, data_filename="stdout", use_step_control=True, return_req_dict=None, verbose=3): """Performs a transient analysis of the circuit described by circ. Important parameters: - tstep is the maximum step to be allowed during simulation. - print_step_and_lte is a boolean value. Is set to true, the step and the LTE of the first element of x will be printed out to step_and_lte.graph in the current directory. """ if data_filename == "stdout": verbose = 0 _debug = False if _debug: print_step_and_lte = True else: print_step_and_lte = False HMAX = tstep #check parameters if tstart > tstop: printing.print_general_error("tstart > tstop") sys.exit(1) if tstep < 0: printing.print_general_error("tstep < 0") sys.exit(1) if verbose > 4: tmpstr = "Vea = %g Ver = %g Iea = %g Ier = %g max_time_iter = %g HMIN = %g" % \ (options.vea, options.ver, options.iea, options.ier, options.transient_max_time_iter, options.hmin) printing.print_info_line((tmpstr, 5), verbose) locked_nodes = circ.get_locked_nodes() if print_step_and_lte: flte = open("step_and_lte.graph", "w") flte.write("#T\tStep\tLTE\n") printing.print_info_line(("Starting transient analysis: ", 3), verbose) printing.print_info_line(("Selected method: %s" % (method,), 3), verbose) #It's a good idea to call transient with prebuilt MNA and N matrix #the analysis will be slightly faster (long netlists). if mna is None or N is None: (mna, N) = dc_analysis.generate_mna_and_N(circ) mna = utilities.remove_row_and_col(mna) N = utilities.remove_row(N, rrow=0) elif not mna.shape[0] == N.shape[0]: printing.print_general_error("mna matrix and N vector have different number of columns.") sys.exit(0) if D is None: # if you do more than one tran analysis, output streams should be changed... # this needs to be fixed D = generate_D(circ, [mna.shape[0], mna.shape[0]]) D = utilities.remove_row_and_col(D) # setup x0 if x0 is None: printing.print_info_line(("Generating x(t=%g) = 0" % (tstart,), 5), verbose) x0 = numpy.matrix(numpy.zeros((mna.shape[0], 1))) opsol = results.op_solution(x=x0, error=x0, circ=circ, outfile=None) else: if isinstance(x0, results.op_solution): opsol = x0 x0 = x0.asmatrix() else: opsol = results.op_solution(x=x0, error=numpy.matrix(numpy.zeros((mna.shape[0], 1))), circ=circ, outfile=None) printing.print_info_line(("Using the supplied op as x(t=%g)." % (tstart,), 5), verbose) if verbose > 4: print "x0:" opsol.print_short() # setup the df method printing.print_info_line(("Selecting the appropriate DF ("+method+")... ", 5), verbose, print_nl=False) if method == IMPLICIT_EULER: import implicit_euler as df elif method == TRAP: import trap as df elif method == GEAR1: import gear as df df.order = 1 elif method == GEAR2: import gear as df df.order = 2 elif method == GEAR3: import gear as df df.order = 3 elif method == GEAR4: import gear as df df.order = 4 elif method == GEAR5: import gear as df df.order = 5 elif method == GEAR6: import gear as df df.order = 6 else: df = import_custom_df_module(method, print_out=(data_filename != "stdout")) # df is none if module is not found if df is None: sys.exit(23) if not df.has_ff() and use_step_control: printing.print_warning("The chosen DF does not support step control. Turning off the feature.") use_step_control = False #use_aposteriori_step_control = False printing.print_info_line(("done.", 5), verbose) # setup the data buffer # if you use the step control, the buffer has to be one point longer. # That's because the excess point is used by a FF in the df module to predict the next value. printing.print_info_line(("Setting up the buffer... ", 5), verbose, print_nl=False) ((max_x, max_dx), (pmax_x, pmax_dx)) = df.get_required_values() if max_x is None and max_dx is None: printing.print_general_error("df doesn't need any value?") sys.exit(1) if use_step_control: thebuffer = dfbuffer(length=max(max_x, max_dx, pmax_x, pmax_dx) + 1, width=3) else: thebuffer = dfbuffer(length=max(max_x, max_dx) + 1, width=3) thebuffer.add((tstart, x0, None)) #setup the first values printing.print_info_line(("done.", 5), verbose) #FIXME #setup the output buffer if return_req_dict: output_buffer = dfbuffer(length=return_req_dict["points"], width=1) output_buffer.add((x0,)) else: output_buffer = None # import implicit_euler to be used in the first iterations # this is because we don't have any dx when we start, nor any past point value if (max_x is not None and max_x > 0) or max_dx is not None: import implicit_euler printing.print_info_line(("MNA (reduced):", 5), verbose) printing.print_info_line((str(mna), 5), verbose) printing.print_info_line(("D (reduced):", 5), verbose) printing.print_info_line((str(D), 5), verbose) # setup the initial values to start the iteration: x = None time = tstart nv = len(circ.nodes_dict) Gmin_matrix = dc_analysis.build_gmin_matrix(circ, options.gmin, mna.shape[0], verbose) # lo step viene generato automaticamente, ma non superare mai quello fornito. if use_step_control: #tstep = min((tstop-tstart)/9999.0, HMAX, 100.0 * options.hmin) tstep = min((tstop-tstart)/9999.0, HMAX) printing.print_info_line(("Initial step: %g"% (tstep,), 5), verbose) if max_dx is None: max_dx_plus_1 = None else: max_dx_plus_1 = max_dx +1 if pmax_dx is None: pmax_dx_plus_1 = None else: pmax_dx_plus_1 = pmax_dx +1 # setup error vectors aerror = numpy.mat(numpy.zeros((x0.shape[0], 1))) aerror[:nv-1, 0] = options.vea aerror[nv-1:, 0] = options.vea rerror = numpy.mat(numpy.zeros((x0.shape[0], 1))) rerror[:nv-1, 0] = options.ver rerror[nv-1:, 0] = options.ier iter_n = 0 # contatore d'iterazione lte = None sol = results.tran_solution(circ, tstart, tstop, op=x0, method=method, outfile=data_filename) printing.print_info_line(("Solving... ", 3), verbose, print_nl=False) tick = ticker.ticker(increments_for_step=1) tick.display(verbose > 1) while time < tstop: if iter_n < max(max_x, max_dx_plus_1): x_coeff, const, x_lte_coeff, prediction, pred_lte_coeff = \ implicit_euler.get_df((thebuffer.get_df_vector()[0],), tstep, \ predict=(use_step_control and (iter_n >= max(pmax_x, pmax_dx_plus_1)))) else: [x_coeff, const, x_lte_coeff, prediction, pred_lte_coeff] = \ df.get_df(thebuffer.get_df_vector(), tstep, predict=use_step_control) if options.transient_prediction_as_x0 and use_step_control and prediction is not None: x0 = prediction elif x is not None: x0 = x (x1, error, solved, n_iter) = dc_analysis.dc_solve(mna=(mna + numpy.multiply(x_coeff, D)) , Ndc=N, Ntran=D*const, circ=circ, Gmin=Gmin_matrix, x0=x0, time=(time + tstep), locked_nodes=locked_nodes, MAXIT=options.transient_max_nr_iter, verbose=0) if solved: old_step = tstep #we will modify it, if we're using step control otherwise it's the same # step control (yeah) if use_step_control: if x_lte_coeff is not None and pred_lte_coeff is not None and prediction is not None: # this is the Local Truncation Error :) lte = abs((x_lte_coeff / (pred_lte_coeff - x_lte_coeff)) * (prediction - x1)) # it should NEVER happen that new_step > 2*tstep, for stability new_step_coeff = 2 for index in xrange(x.shape[0]): if lte[index, 0] != 0: new_value = ((aerror[index, 0] + rerror[index, 0]*abs(x[index, 0])) / lte[index, 0]) \ ** (1.0 / (df.order+1)) if new_value < new_step_coeff: new_step_coeff = new_value #print new_value new_step = tstep * new_step_coeff if options.transient_use_aposteriori_step_control and new_step < options.transient_aposteriori_step_threshold * tstep: #don't recalculate a x for a small change tstep = check_step(new_step, time, tstop, HMAX) #print "Apost. (reducing) step = "+str(tstep) continue tstep = check_step(new_step, time, tstop, HMAX) # used in the next iteration #print "Apriori tstep = "+str(tstep) else: #print "LTE not calculated." lte = None if print_step_and_lte and lte is not None: #if you wish to look at the step. We print just a lte flte.write(str(time)+"\t"+str(old_step)+"\t"+str(lte.max())+"\n") # if we get here, either aposteriori_step_control is # disabled, or it's enabled and the error is small # enough. Anyway, the result is GOOD, STORE IT. time = time + old_step x = x1 iter_n = iter_n + 1 sol.add_line(time, x) dxdt = numpy.multiply(x_coeff, x) + const thebuffer.add((time, x, dxdt)) if output_buffer is not None: output_buffer.add((x, )) tick.step(verbose > 1) else: # If we get here, Newton failed to converge. We need to reduce the step... if use_step_control: tstep = tstep/5.0 tstep = check_step(tstep, time, tstop, HMAX) printing.print_info_line(("At %g s reducing step: %g s (convergence failed)" % (time, tstep), 5), verbose) else: #we can't reduce the step printing.print_general_error("Can't converge with step "+str(tstep)+".") printing.print_general_error("Try setting --t-max-nr to a higher value or set step to a lower one.") solved = False break if options.transient_max_time_iter and iter_n == options.transient_max_time_iter: printing.print_general_error("MAX_TIME_ITER exceeded ("+str(options.transient_max_time_iter)+"), iteration halted.") solved = False break if print_step_and_lte: flte.close() tick.hide(verbose > 1) if solved: printing.print_info_line(("done.", 3), verbose) printing.print_info_line(("Average time step: %g" % ((tstop - tstart)/iter_n,), 3), verbose) if output_buffer: ret_value = output_buffer.get_as_matrix() else: ret_value = sol else: print "failed." ret_value = None return ret_value
def bfpss(circ, period, step=None, mna=None, Tf=None, D=None, points=None, autonomous=False, x0=None, data_filename='stdout', vector_norm=lambda v: max(abs(v)), verbose=3): """Performs a PSS analysis. Time step is constant, IE will be used as DF Parameters: circ is the circuit description class period is the period of the solution mna, D, Tf are not compulsory they will be computed if they're set to None step is the time step between consecutive points points is the number of points to be used step and points are mutually exclusive options: - if step is specified, the number of points will be automatically determined - if points is set, the step will be automatically determined - if none of them is set, options.shooting_default_points will be used as points autonomous has to be False, autonomous circuits are not supported x0 is the initial guess to be used. Needs work. data_filename is the output filename. Defaults to stdout. verbose is set to zero (print errors only) if datafilename == 'stdout'. Returns: nothing """ if data_filename == "stdout": verbose = 0 printing.print_info_line(("Starting periodic steady state analysis:",3), verbose) printing.print_info_line(("Method: brute-force",3), verbose) if mna is None or Tf is None: (mna, Tf) = dc_analysis.generate_mna_and_N(circ) mna = utilities.remove_row_and_col(mna) Tf = utilities.remove_row(Tf, rrow=0) elif not mna.shape[0] == Tf.shape[0]: printing.print_general_error("mna matrix and N vector have different number of rows.") sys.exit(0) if D is None: D = transient.generate_D(circ, [mna.shape[0], mna.shape[0]]) D = utilities.remove_row_and_col(D) elif not mna.shape == D.shape: printing.print_general_error("mna matrix and D matrix have different sizes.") sys.exit(0) (points, step) = check_step_and_points(step, points, period) n_of_var = mna.shape[0] locked_nodes = circ.get_locked_nodes() tick = ticker.ticker(increments_for_step=1) CMAT = build_CMAT(mna, D, step, points, tick, n_of_var=n_of_var, \ verbose=verbose) x = build_x(mna, step, points, tick, x0=x0, n_of_var=n_of_var, \ verbose=verbose) Tf = build_Tf(Tf, points, tick, n_of_var=n_of_var, verbose=verbose) # time variable component: Tt this is always the same in each iter. So we build it once for all # this holds all time-dependent sources (both V/I). Tt = build_Tt(circ, points, step, tick, n_of_var=n_of_var, verbose=verbose) # Indices to differentiate between currents and voltages in the convergence check nv_indices = [] ni_indices = [] nv_1 = len(circ.nodes_dict) - 1 ni = n_of_var - nv_1 for i in range(points): nv_indices += (i*mna.shape[0]*numpy.ones(nv_1) + numpy.arange(nv_1)).tolist() ni_indices += (i*mna.shape[0]*numpy.ones(ni) + numpy.arange(nv_1, n_of_var)).tolist() converged = False printing.print_info_line(("Solving... ",3), verbose, print_nl=False) tick.reset() tick.display(verbose > 2) J = numpy.mat(numpy.zeros(CMAT.shape)) T = numpy.mat(numpy.zeros((CMAT.shape[0], 1))) # td is a numpy matrix that will hold the damping factors td = numpy.mat(numpy.zeros((points, 1))) iteration = 0 # newton iteration counter while True: if iteration: # the first time are already all zeros J[:, :] = 0 T[:, 0] = 0 td[:, 0] = 0 for index in xrange(1, points): for elem in circ.elements: # build all dT(xn)/dxn (stored in J) and T(x) if elem.is_nonlinear: oports = elem.get_output_ports() for opindex in range(len(oports)): dports = elem.get_drive_ports(opindex) v_ports = [] for dpindex in range(len(dports)): dn1, dn2 = dports[dpindex] v = 0 # build v: remember we trashed the 0 row and 0 col of mna -> -1 if dn1: v = v + x[index*n_of_var + dn1 - 1, 0] if dn2: v = v - x[index*n_of_var + dn2 - 1, 0] v_ports.append(v) # all drive ports are ready. n1, n2 = oports[opindex][0], oports[opindex][1] if n1: T[index*n_of_var + n1 - 1, 0] = T[index*n_of_var + n1 - 1, 0] + elem.i(opindex, v_ports) if n2: T[index*n_of_var + n2 - 1, 0] = T[index*n_of_var + n2 - 1, 0] - elem.i(opindex, v_ports) for dpindex in range(len(dports)): dn1, dn2 = dports[dpindex] if n1: if dn1: J[index*n_of_var + n1-1, index*n_of_var + dn1-1] = \ J[index*n_of_var + n1-1, index*n_of_var + dn1-1] + elem.g(opindex, v_ports, dpindex) if dn2: J[index*n_of_var + n1-1, index*n_of_var + dn2-1] =\ J[index*n_of_var + n1-1, index * n_of_var + dn2-1] - 1.0*elem.g(opindex, v_ports, dpindex) if n2: if dn1: J[index*n_of_var + n2-1, index*n_of_var + dn1-1] = \ J[index*n_of_var + n2-1, index*n_of_var + dn1-1] - 1.0*elem.g(opindex, v_ports, dpindex) if dn2: J[index*n_of_var + n2-1, index*n_of_var + dn2-1] =\ J[index*n_of_var + n2-1, index*n_of_var + dn2-1] + elem.g(opindex, v_ports, dpindex) J = J + CMAT residuo = CMAT*x + T + Tf + Tt dx = -1 * (numpy.linalg.inv(J) * residuo) #td for index in xrange(points): td[index, 0] = dc_analysis.get_td(dx[index*n_of_var:(index+1)*n_of_var, 0], locked_nodes, n=-1) x = x + min(abs(td))[0, 0] * dx # convergence check converged = convergence_check(dx, x, nv_indices, ni_indices, vector_norm) if converged: break tick.step(verbose > 2) if options.shooting_max_nr_iter and iteration == options.shooting_max_nr_iter: printing.print_general_error("Hitted SHOOTING_MAX_NR_ITER (" + str(options.shooting_max_nr_iter) + "), iteration halted.") converged = False break else: iteration = iteration + 1 tick.hide(verbose > 2) if converged: printing.print_info_line(("done.", 3), verbose) t = numpy.mat(numpy.arange(points)*step) t = t.reshape((1, points)) x = x.reshape((points, n_of_var)) sol = results.pss_solution(circ=circ, method="brute-force", period=period, outfile=data_filename, t_array=t, x_array=x.T) else: print "failed." sol = None return sol
def ac_analysis(circ, start, points, stop, sweep_type, x0=None, mna=None, AC=None, Nac=None, J=None, outfile="stdout", verbose=3): """Performs an AC analysis of the circuit described by circ. Parameters: start (float): the start angular frequency for the AC analysis stop (float): stop angular frequency points (float): the number of points to be use the discretize the [start, stop] interval. sweep_type (string): Either 'LOG' or 'LINEAR', defaults to 'LOG'. outfile (string): the filename of the output file where the results will be written. '.ac' is automatically added at the end to prevent different analyses from overwriting each-other's results. If unset or set to None, defaults to stdout. verbose (int): the verbosity level, from 0 (silent) to 6 (debug). Returns: an AC results object """ if outfile == 'stdout': verbose = 0 # check step/start/stop parameters nsteps = points - 1 if start == 0: printing.print_general_error("AC analysis has start frequency = 0") sys.exit(5) if start > stop: printing.print_general_error("AC analysis has start > stop") sys.exit(1) if nsteps < 1: printing.print_general_error("AC analysis has number of steps <= 1") sys.exit(1) if sweep_type == options.ac_log_step: omega_iter = utilities.log_axis_iterator(stop, start, nsteps) elif sweep_type == options.ac_lin_step: omega_iter = utilities.lin_axis_iterator(stop, start, nsteps) else: printing.print_general_error("Unknown sweep type.") sys.exit(1) tmpstr = "Vea =", options.vea, "Ver =", options.ver, "Iea =", options.iea, "Ier =", \ options.ier, "max_ac_nr_iter =", options.ac_max_nr_iter printing.print_info_line((tmpstr, 5), verbose) del tmpstr printing.print_info_line(("Starting AC analysis: ", 1), verbose) tmpstr = "w: start = %g Hz, stop = %g Hz, %d steps" % (start, stop, nsteps) printing.print_info_line((tmpstr, 3), verbose) del tmpstr # It's a good idea to call AC with prebuilt MNA matrix if the circuit is # big if mna is None: (mna, N) = dc_analysis.generate_mna_and_N(circ, verbose=verbose) del N mna = utilities.remove_row_and_col(mna) if Nac is None: Nac = generate_Nac(circ) Nac = utilities.remove_row(Nac, rrow=0) if AC is None: AC = generate_AC(circ, [mna.shape[0], mna.shape[0]]) AC = utilities.remove_row_and_col(AC) if circ.is_nonlinear(): if J is not None: pass # we used the supplied linearization matrix else: if x0 is None: printing.print_info_line( ("Starting OP analysis to get a linearization point...", 3), verbose, print_nl=False) # silent OP x0 = dc_analysis.op_analysis(circ, verbose=0) if x0 is None: # still! Then op_analysis has failed! printing.print_info_line(("failed.", 3), verbose) printing.print_general_error( "OP analysis failed, no linearization point available. Quitting.") sys.exit(3) else: printing.print_info_line(("done.", 3), verbose) printing.print_info_line( ("Linearization point (xop):", 5), verbose) if verbose > 4: x0.print_short() printing.print_info_line( ("Linearizing the circuit...", 5), verbose, print_nl=False) J = generate_J(xop=x0.asmatrix(), circ=circ, mna=mna, Nac=Nac, data_filename=outfile, verbose=verbose) printing.print_info_line((" done.", 5), verbose) # we have J, continue else: # not circ.is_nonlinear() # no J matrix is required. J = 0 printing.print_info_line(("MNA (reduced):", 5), verbose) printing.print_info_line((str(mna), 5), verbose) printing.print_info_line(("AC (reduced):", 5), verbose) printing.print_info_line((str(AC), 5), verbose) printing.print_info_line(("J (reduced):", 5), verbose) printing.print_info_line((str(J), 5), verbose) printing.print_info_line(("Nac (reduced):", 5), verbose) printing.print_info_line((str(Nac), 5), verbose) sol = results.ac_solution(circ, ostart=start, ostop=stop, opoints=nsteps, stype=sweep_type, op=x0, outfile=outfile) # setup the initial values to start the iteration: nv = len(circ.nodes_dict) j = numpy.complex('j') Gmin_matrix = dc_analysis.build_gmin_matrix( circ, options.gmin, mna.shape[0], verbose) iter_n = 0 # contatore d'iterazione printing.print_info_line(("Solving... ", 3), verbose, print_nl=False) tick = ticker.ticker(increments_for_step=1) tick.display(verbose > 1) x = x0 for omega in omega_iter: (x, error, solved, n_iter) = dc_analysis.dc_solve( mna=(mna + numpy.multiply(j * omega, AC) + J), Ndc = Nac, Ntran = 0, circ = circuit.Circuit( title="Dummy circuit for AC", filename=None), Gmin = Gmin_matrix, x0 = x, time = None, locked_nodes = None, MAXIT = options.ac_max_nr_iter, skip_Tt = True, verbose = 0) if solved: tick.step(verbose > 1) iter_n = iter_n + 1 # hooray! sol.add_line(omega, x) else: break tick.hide(verbose > 1) if solved: printing.print_info_line(("done.", 1), verbose) ret_value = sol else: printing.print_info_line(("failed.", 1), verbose) ret_value = None return ret_value
def transient_analysis(circ, tstart, tstep, tstop, method=TRAP, x0=None, mna=None, N=None, \ D=None, data_filename="stdout", use_step_control=True, return_req_dict=None, verbose=3): """Performs a transient analysis of the circuit described by circ. Important parameters: - tstep is the maximum step to be allowed during simulation. - print_step_and_lte is a boolean value. Is set to true, the step and the LTE of the first element of x will be printed out to step_and_lte.graph in the current directory. """ if data_filename == "stdout": verbose = 0 _debug = False if _debug: print_step_and_lte = True else: print_step_and_lte = False HMAX = tstep #check parameters if tstart > tstop: printing.print_general_error("tstart > tstop") sys.exit(1) if tstep < 0: printing.print_general_error("tstep < 0") sys.exit(1) if verbose > 4: tmpstr = "Vea = %g Ver = %g Iea = %g Ier = %g max_time_iter = %g HMIN = %g" % \ (options.vea, options.ver, options.iea, options.ier, options.transient_max_time_iter, options.hmin) printing.print_info_line((tmpstr, 5), verbose) locked_nodes = circ.get_locked_nodes() if print_step_and_lte: flte = open("step_and_lte.graph", "w") flte.write("#T\tStep\tLTE\n") printing.print_info_line(("Starting transient analysis: ", 3), verbose) printing.print_info_line(("Selected method: %s" % (method, ), 3), verbose) #It's a good idea to call transient with prebuilt MNA and N matrix #the analysis will be slightly faster (long netlists). if mna is None or N is None: (mna, N) = dc_analysis.generate_mna_and_N(circ) mna = utilities.remove_row_and_col(mna) N = utilities.remove_row(N, rrow=0) elif not mna.shape[0] == N.shape[0]: printing.print_general_error( "mna matrix and N vector have different number of columns.") sys.exit(0) if D is None: # if you do more than one tran analysis, output streams should be changed... # this needs to be fixed D = generate_D(circ, [mna.shape[0], mna.shape[0]]) D = utilities.remove_row_and_col(D) # setup x0 if x0 is None: printing.print_info_line(("Generating x(t=%g) = 0" % (tstart, ), 5), verbose) x0 = numpy.matrix(numpy.zeros((mna.shape[0], 1))) opsol = results.op_solution(x=x0, error=x0, circ=circ, outfile=None) else: if isinstance(x0, results.op_solution): opsol = x0 x0 = x0.asmatrix() else: opsol = results.op_solution(x=x0, error=numpy.matrix( numpy.zeros((mna.shape[0], 1))), circ=circ, outfile=None) printing.print_info_line( ("Using the supplied op as x(t=%g)." % (tstart, ), 5), verbose) if verbose > 4: print "x0:" opsol.print_short() # setup the df method printing.print_info_line( ("Selecting the appropriate DF (" + method + ")... ", 5), verbose, print_nl=False) if method == IMPLICIT_EULER: import implicit_euler as df elif method == TRAP: import trap as df elif method == GEAR1: import gear as df df.order = 1 elif method == GEAR2: import gear as df df.order = 2 elif method == GEAR3: import gear as df df.order = 3 elif method == GEAR4: import gear as df df.order = 4 elif method == GEAR5: import gear as df df.order = 5 elif method == GEAR6: import gear as df df.order = 6 else: df = import_custom_df_module(method, print_out=(data_filename != "stdout")) # df is none if module is not found if df is None: sys.exit(23) if not df.has_ff() and use_step_control: printing.print_warning( "The chosen DF does not support step control. Turning off the feature." ) use_step_control = False #use_aposteriori_step_control = False printing.print_info_line(("done.", 5), verbose) # setup the data buffer # if you use the step control, the buffer has to be one point longer. # That's because the excess point is used by a FF in the df module to predict the next value. printing.print_info_line(("Setting up the buffer... ", 5), verbose, print_nl=False) ((max_x, max_dx), (pmax_x, pmax_dx)) = df.get_required_values() if max_x is None and max_dx is None: printing.print_general_error("df doesn't need any value?") sys.exit(1) if use_step_control: thebuffer = dfbuffer(length=max(max_x, max_dx, pmax_x, pmax_dx) + 1, width=3) else: thebuffer = dfbuffer(length=max(max_x, max_dx) + 1, width=3) thebuffer.add((tstart, x0, None)) #setup the first values printing.print_info_line(("done.", 5), verbose) #FIXME #setup the output buffer if return_req_dict: output_buffer = dfbuffer(length=return_req_dict["points"], width=1) output_buffer.add((x0, )) else: output_buffer = None # import implicit_euler to be used in the first iterations # this is because we don't have any dx when we start, nor any past point value if (max_x is not None and max_x > 0) or max_dx is not None: import implicit_euler printing.print_info_line(("MNA (reduced):", 5), verbose) printing.print_info_line((str(mna), 5), verbose) printing.print_info_line(("D (reduced):", 5), verbose) printing.print_info_line((str(D), 5), verbose) # setup the initial values to start the iteration: x = None time = tstart nv = len(circ.nodes_dict) Gmin_matrix = dc_analysis.build_gmin_matrix(circ, options.gmin, mna.shape[0], verbose) # lo step viene generato automaticamente, ma non superare mai quello fornito. if use_step_control: #tstep = min((tstop-tstart)/9999.0, HMAX, 100.0 * options.hmin) tstep = min((tstop - tstart) / 9999.0, HMAX) printing.print_info_line(("Initial step: %g" % (tstep, ), 5), verbose) if max_dx is None: max_dx_plus_1 = None else: max_dx_plus_1 = max_dx + 1 if pmax_dx is None: pmax_dx_plus_1 = None else: pmax_dx_plus_1 = pmax_dx + 1 # setup error vectors aerror = numpy.mat(numpy.zeros((x0.shape[0], 1))) aerror[:nv - 1, 0] = options.vea aerror[nv - 1:, 0] = options.vea rerror = numpy.mat(numpy.zeros((x0.shape[0], 1))) rerror[:nv - 1, 0] = options.ver rerror[nv - 1:, 0] = options.ier iter_n = 0 # contatore d'iterazione lte = None sol = results.tran_solution(circ, tstart, tstop, op=x0, method=method, outfile=data_filename) printing.print_info_line(("Solving... ", 3), verbose, print_nl=False) tick = ticker.ticker(increments_for_step=1) tick.display(verbose > 1) while time < tstop: if iter_n < max(max_x, max_dx_plus_1): x_coeff, const, x_lte_coeff, prediction, pred_lte_coeff = \ implicit_euler.get_df((thebuffer.get_df_vector()[0],), tstep, \ predict=(use_step_control and (iter_n >= max(pmax_x, pmax_dx_plus_1)))) else: [x_coeff, const, x_lte_coeff, prediction, pred_lte_coeff] = \ df.get_df(thebuffer.get_df_vector(), tstep, predict=use_step_control) if options.transient_prediction_as_x0 and use_step_control and prediction is not None: x0 = prediction elif x is not None: x0 = x (x1, error, solved, n_iter) = dc_analysis.dc_solve(mna=(mna + numpy.multiply(x_coeff, D)), Ndc=N, Ntran=D * const, circ=circ, Gmin=Gmin_matrix, x0=x0, time=(time + tstep), locked_nodes=locked_nodes, MAXIT=options.transient_max_nr_iter, verbose=0) if solved: old_step = tstep #we will modify it, if we're using step control otherwise it's the same # step control (yeah) if use_step_control: if x_lte_coeff is not None and pred_lte_coeff is not None and prediction is not None: # this is the Local Truncation Error :) lte = abs((x_lte_coeff / (pred_lte_coeff - x_lte_coeff)) * (prediction - x1)) # it should NEVER happen that new_step > 2*tstep, for stability new_step_coeff = 2 for index in xrange(x.shape[0]): if lte[index, 0] != 0: new_value = ((aerror[index, 0] + rerror[index, 0]*abs(x[index, 0])) / lte[index, 0]) \ ** (1.0 / (df.order+1)) if new_value < new_step_coeff: new_step_coeff = new_value #print new_value new_step = tstep * new_step_coeff if options.transient_use_aposteriori_step_control and new_step < options.transient_aposteriori_step_threshold * tstep: #don't recalculate a x for a small change tstep = check_step(new_step, time, tstop, HMAX) #print "Apost. (reducing) step = "+str(tstep) continue tstep = check_step(new_step, time, tstop, HMAX) # used in the next iteration #print "Apriori tstep = "+str(tstep) else: #print "LTE not calculated." lte = None if print_step_and_lte and lte is not None: #if you wish to look at the step. We print just a lte flte.write( str(time) + "\t" + str(old_step) + "\t" + str(lte.max()) + "\n") # if we get here, either aposteriori_step_control is # disabled, or it's enabled and the error is small # enough. Anyway, the result is GOOD, STORE IT. time = time + old_step x = x1 iter_n = iter_n + 1 sol.add_line(time, x) dxdt = numpy.multiply(x_coeff, x) + const thebuffer.add((time, x, dxdt)) if output_buffer is not None: output_buffer.add((x, )) tick.step(verbose > 1) else: # If we get here, Newton failed to converge. We need to reduce the step... if use_step_control: tstep = tstep / 5.0 tstep = check_step(tstep, time, tstop, HMAX) printing.print_info_line( ("At %g s reducing step: %g s (convergence failed)" % (time, tstep), 5), verbose) else: #we can't reduce the step printing.print_general_error("Can't converge with step " + str(tstep) + ".") printing.print_general_error( "Try setting --t-max-nr to a higher value or set step to a lower one." ) solved = False break if options.transient_max_time_iter and iter_n == options.transient_max_time_iter: printing.print_general_error("MAX_TIME_ITER exceeded (" + str(options.transient_max_time_iter) + "), iteration halted.") solved = False break if print_step_and_lte: flte.close() tick.hide(verbose > 1) if solved: printing.print_info_line(("done.", 3), verbose) printing.print_info_line( ("Average time step: %g" % ((tstop - tstart) / iter_n, ), 3), verbose) if output_buffer: ret_value = output_buffer.get_as_matrix() else: ret_value = sol else: print "failed." ret_value = None return ret_value
def shooting(circ, period, step=None, x0=None, points=None, autonomous=False, mna=None, Tf=None, D=None, outfile='stdout', vector_norm=lambda v: max(abs(v)), verbose=3): """Performs a periodic steady state analysis based on the algorithm described in Brambilla, A.; D'Amore, D., "Method for steady-state simulation of strongly nonlinear circuits in the time domain," Circuits and Systems I: Fundamental Theory and Applications, IEEE Transactions on, vol.48, no.7, pp.885-889, Jul 2001 URL: http://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=933329&isnumber=20194 The results have been computed again by me, the formulas are not exactly the same, but the idea behind the shooting algorithm is. This method allows us to have a period with many points without having to invert a huge matrix (and being limited to the maximum matrix size). A tran is performed to initialize the solver. We compute the change in the last point, calculating several matrices in the process. From that, with the same matrices we calculate the changes in all points, starting from 0 (which is the same as the last one), then 1, ... Key points: - Only not autonomous circuits are supported. - The time step is constant - Implicit euler is used as DF Parameters: circ is the circuit description class period is the period of the solution mna, D, Tf are not compulsory they will be computed if they're set to None step is the time step between consecutive points points is the number of points to be used step and points are mutually exclusive options: - if step is specified, the number of points will be automatically determined - if points is set, the step will be automatically determined - if none of them is set, options.shooting_default_points will be used as points autonomous has to be False, autonomous circuits are not supported outfile is the output filename. Defaults to stdout. verbose is set to zero (print errors only) if datafilename == 'stdout'. Returns: nothing """ if outfile == "stdout": verbose = 0 printing.print_info_line( ("Starting periodic steady state analysis:", 3), verbose) printing.print_info_line(("Method: shooting", 3), verbose) if isinstance(x0, results.op_solution): x0 = x0.asmatrix() if mna is None or Tf is None: (mna, Tf) = dc_analysis.generate_mna_and_N(circ, verbose=verbose) mna = utilities.remove_row_and_col(mna) Tf = utilities.remove_row(Tf, rrow=0) elif not mna.shape[0] == Tf.shape[0]: printing.print_general_error( "mna matrix and N vector have different number of rows.") sys.exit(0) if D is None: D = transient.generate_D(circ, [mna.shape[0], mna.shape[0]]) D = utilities.remove_row_and_col(D) elif not mna.shape == D.shape: printing.print_general_error( "mna matrix and D matrix have different sizes.") sys.exit(0) (points, step) = check_step_and_points(step, points, period) print "points", points print "step", step n_of_var = mna.shape[0] locked_nodes = circ.get_locked_nodes() printing.print_info_line( ("Starting transient analysis for algorithm init: tstop=%g, tstep=%g... " % (10 * points * step, step), 3), verbose, print_nl=False) xtran = transient.transient_analysis( circ=circ, tstart=0, tstep=step, tstop=10 * points * step, method="TRAP", x0=None, mna=mna, N=Tf, D=D, use_step_control=False, outfile=outfile + ".tran", return_req_dict={"points": points}, verbose=0) if xtran is None: print "failed." return None printing.print_info_line(("done.", 3), verbose) x = [] for index in range(points): x.append(xtran[index * n_of_var:(index + 1) * n_of_var, 0]) tick = ticker.ticker(increments_for_step=1) MAass_static, MBass = build_static_MAass_and_MBass(mna, D, step) # This contains # the time invariant part, Tf # time variable component: Tt this is always the same, since the time interval is the same # this holds all time-dependent sources (both V/I). Tass_static_vector = build_Tass_static_vector( circ, Tf, points, step, tick, n_of_var, verbose) converged = False printing.print_info_line(("Solving... ", 3), verbose, print_nl=False) tick.reset() tick.display(verbose > 2) iteration = 0 # newton iteration counter conv_counter = 0 while True: dx = [] Tass_variable_vector = [] MAass_variable_vector = [] for index in range(points): if index == 0: xn_minus_1 = x[points - 1] else: xn_minus_1 = x[index - 1] MAass_variable, Tass_variable = get_variable_MAass_and_Tass( circ, x[index], xn_minus_1, mna, D, step, n_of_var) MAass_variable_vector.append(MAass_variable + MAass_static) Tass_variable_vector.append( Tass_variable + Tass_static_vector[index]) dxN = compute_dxN(circ, MAass_variable_vector, MBass, Tass_variable_vector, n_of_var, points, verbose=verbose) td = dc_analysis.get_td(dxN, locked_nodes, n=-1) x[points - 1] = td * dxN + x[points - 1] for index in range(points - 1): if index == 0: dxi_minus_1 = dxN else: dxi_minus_1 = dx[index - 1] dx.append( compute_dx(MAass_variable_vector[index], MBass, Tass_variable_vector[index], dxi_minus_1)) td = dc_analysis.get_td(dx[index], locked_nodes, n=-1) x[index] = td * dx[index] + x[index] dx.append(dxN) if (vector_norm_wrapper(dx, vector_norm) < min(options.ver, options.ier) * vector_norm_wrapper(x, vector_norm) + min(options.vea, options.iea)): # \ # and (dc_analysis.vector_norm(residuo) < # options.er*dc_analysis.vector_norm(x) + options.ea): if conv_counter == 3: converged = True break else: conv_counter = conv_counter + 1 elif vector_norm(dx[points - 1]) is numpy.nan: # needs work fixme raise OverflowError # break else: conv_counter = 0 tick.step(verbose > 2) if options.shooting_max_nr_iter and iteration == options.shooting_max_nr_iter: printing.print_general_error( "Hitted SHOOTING_MAX_NR_ITER (" + str(options.shooting_max_nr_iter) + "), iteration halted.") converged = False break else: iteration = iteration + 1 tick.hide(verbose > 2) if converged: printing.print_info_line(("done.", 3), verbose) t = numpy.mat(numpy.arange(points) * step) t = t.reshape((1, points)) xmat = x[0] for index in xrange(1, points): xmat = numpy.concatenate((xmat, x[index]), axis=1) sol = results.pss_solution( circ=circ, method="shooting", period=period, outfile=outfile, t_array=t, x_array=xmat) # print_results(circ, x, fdata, points, step) else: print "failed." sol = None return sol
def ac_analysis(circ, start, nsteps, stop, step_type, xop=None, mna=None,\ AC=None, Nac=None, J=None, data_filename="stdout", verbose=3): """Performs an AC analysis of the circuit (described by circ). """ if data_filename == 'stdout': verbose = 0 #check step/start/stop parameters if start == 0: printing.print_general_error("AC analysis has start frequency = 0") sys.exit(5) if start > stop: printing.print_general_error("AC analysis has start > stop") sys.exit(1) if nsteps < 1: printing.print_general_error("AC analysis has number of steps <= 1") sys.exit(1) if step_type == options.ac_log_step: omega_iter = utilities.log_axis_iterator(stop, start, nsteps) elif step_type == options.ac_lin_step: omega_iter = utilities.lin_axis_iterator(stop, start, nsteps) else: printing.print_general_error("Unknown sweep type.") sys.exit(1) tmpstr = "Vea =", options.vea, "Ver =", options.ver, "Iea =", options.iea, "Ier =", \ options.ier, "max_ac_nr_iter =", options.ac_max_nr_iter printing.print_info_line((tmpstr, 5), verbose) del tmpstr printing.print_info_line(("Starting AC analysis: ", 1), verbose) tmpstr = "w: start = %g Hz, stop = %g Hz, %d steps" % (start, stop, nsteps) printing.print_info_line((tmpstr, 3), verbose) del tmpstr #It's a good idea to call AC with prebuilt MNA matrix if the circuit is big if mna is None: (mna, N) = dc_analysis.generate_mna_and_N(circ) del N mna = utilities.remove_row_and_col(mna) if Nac is None: Nac = generate_Nac(circ) Nac = utilities.remove_row(Nac, rrow=0) if AC is None: AC = generate_AC(circ, [mna.shape[0], mna.shape[0]]) AC = utilities.remove_row_and_col(AC) if circ.is_nonlinear(): if J is not None: pass # we used the supplied linearization matrix else: if xop is None: printing.print_info_line(("Starting OP analysis to get a linearization point...", 3), verbose, print_nl=False) #silent OP xop = dc_analysis.op_analysis(circ, verbose=0) if xop is None: #still! Then op_analysis has failed! printing.print_info_line(("failed.", 3), verbose) printing.print_general_error("OP analysis failed, no linearization point available. Quitting.") sys.exit(3) else: printing.print_info_line(("done.", 3), verbose) printing.print_info_line(("Linearization point (xop):", 5), verbose) if verbose > 4: xop.print_short() printing.print_info_line(("Linearizing the circuit...", 5), verbose, print_nl=False) J = generate_J(xop=xop.asmatrix(), circ=circ, mna=mna, Nac=Nac, data_filename=data_filename, verbose=verbose) printing.print_info_line((" done.", 5), verbose) # we have J, continue else: #not circ.is_nonlinear() # no J matrix is required. J = 0 printing.print_info_line(("MNA (reduced):", 5), verbose) printing.print_info_line((str(mna), 5), verbose) printing.print_info_line(("AC (reduced):", 5), verbose) printing.print_info_line((str(AC), 5), verbose) printing.print_info_line(("J (reduced):", 5), verbose) printing.print_info_line((str(J), 5), verbose) printing.print_info_line(("Nac (reduced):", 5), verbose) printing.print_info_line((str(Nac), 5), verbose) sol = results.ac_solution(circ, ostart=start, ostop=stop, opoints=nsteps, stype=step_type, op=xop, outfile=data_filename) # setup the initial values to start the iteration: nv = len(circ.nodes_dict) j = numpy.complex('j') Gmin_matrix = dc_analysis.build_gmin_matrix(circ, options.gmin, mna.shape[0], verbose) iter_n = 0 # contatore d'iterazione #printing.print_results_header(circ, fdata, print_int_nodes=options.print_int_nodes, print_omega=True) printing.print_info_line(("Solving... ", 3), verbose, print_nl=False) tick = ticker.ticker(increments_for_step=1) tick.display(verbose > 1) x = xop for omega in omega_iter: (x, error, solved, n_iter) = dc_analysis.dc_solve(mna=(mna + numpy.multiply(j*omega, AC) + J), \ Ndc=Nac, Ntran=0, circ=circuit.circuit(title="Dummy circuit for AC", filename=None), Gmin=Gmin_matrix, x0=x, \ time=None, locked_nodes=None, MAXIT=options.ac_max_nr_iter, skip_Tt=True, verbose=0) if solved: tick.step(verbose > 1) iter_n = iter_n + 1 # hooray! sol.add_line(omega, x) else: break tick.hide(verbose > 1) if solved: printing.print_info_line(("done.", 1), verbose) ret_value = sol else: printing.print_info_line(("failed.", 1), verbose) ret_value = None return ret_value
def shooting(circ, period, step=None, mna=None, Tf=None, D=None, points=None, autonomous=False, data_filename='stdout', vector_norm=lambda v: max(abs(v)), verbose=3): """Performs a periodic steady state analysis based on the algorithm described in Brambilla, A.; D'Amore, D., "Method for steady-state simulation of strongly nonlinear circuits in the time domain," Circuits and Systems I: Fundamental Theory and Applications, IEEE Transactions on, vol.48, no.7, pp.885-889, Jul 2001 URL: http://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=933329&isnumber=20194 The results have been computed again by me, the formulas are not exactly the same, but the idea behind the shooting algorithm is. This method allows us to have a period with many points without having to invert a huge matrix (and being limited to the maximum matrix size). A tran is performed to initialize the solver. We compute the change in the last point, calculating several matrices in the process. From that, with the same matrices we calculate the changes in all points, starting from 0 (which is the same as the last one), then 1, ... Key points: - Only not autonomous circuits are supported. - The time step is constant - Implicit euler is used as DF Parameters: circ is the circuit description class period is the period of the solution mna, D, Tf are not compulsory they will be computed if they're set to None step is the time step between consecutive points points is the number of points to be used step and points are mutually exclusive options: - if step is specified, the number of points will be automatically determined - if points is set, the step will be automatically determined - if none of them is set, options.shooting_default_points will be used as points autonomous has to be False, autonomous circuits are not supported data_filename is the output filename. Defaults to stdout. verbose is set to zero (print errors only) if datafilename == 'stdout'. Returns: nothing """ if data_filename == "stdout": verbose = 0 printing.print_info_line(("Starting periodic steady state analysis:", 3), verbose) printing.print_info_line(("Method: shooting", 3), verbose) if mna is None or Tf is None: (mna, Tf) = dc_analysis.generate_mna_and_N(circ) mna = utilities.remove_row_and_col(mna) Tf = utilities.remove_row(Tf, rrow=0) elif not mna.shape[0] == Tf.shape[0]: printing.print_general_error( "mna matrix and N vector have different number of rows.") sys.exit(0) if D is None: D = transient.generate_D(circ, [mna.shape[0], mna.shape[0]]) D = utilities.remove_row_and_col(D) elif not mna.shape == D.shape: printing.print_general_error( "mna matrix and D matrix have different sizes.") sys.exit(0) (points, step) = check_step_and_points(step, points, period) print "points", points print "step", step n_of_var = mna.shape[0] locked_nodes = circ.get_locked_nodes() printing.print_info_line(( "Starting transient analysis for algorithm init: tstop=%g, tstep=%g... " % (10 * points * step, step), 3), verbose, print_nl=False) xtran = transient.transient_analysis(circ=circ, tstart=0, tstep=step, tstop=10*points*step, method="TRAP", x0=None, mna=mna, N=Tf, \ D=D, use_step_control=False, data_filename=data_filename+".tran", return_req_dict={"points":points}, verbose=0) if xtran is None: print "failed." return None printing.print_info_line(("done.", 3), verbose) x = [] for index in range(points): x.append(xtran[index * n_of_var:(index + 1) * n_of_var, 0]) tick = ticker.ticker(increments_for_step=1) MAass_static, MBass = build_static_MAass_and_MBass(mna, D, step) # This contains # the time invariant part, Tf # time variable component: Tt this is always the same, since the time interval is the same # this holds all time-dependent sources (both V/I). Tass_static_vector = build_Tass_static_vector(circ, Tf, points, step, tick, n_of_var, verbose) converged = False printing.print_info_line(("Solving... ", 3), verbose, print_nl=False) tick.reset() tick.display(verbose > 2) iteration = 0 # newton iteration counter conv_counter = 0 while True: dx = [] Tass_variable_vector = [] MAass_variable_vector = [] for index in range(points): if index == 0: xn_minus_1 = x[points - 1] else: xn_minus_1 = x[index - 1] MAass_variable, Tass_variable = get_variable_MAass_and_Tass( circ, x[index], xn_minus_1, mna, D, step, n_of_var) MAass_variable_vector.append(MAass_variable + MAass_static) Tass_variable_vector.append(Tass_variable + Tass_static_vector[index]) dxN = compute_dxN(circ, MAass_variable_vector, MBass, Tass_variable_vector, n_of_var, points, verbose=verbose) td = dc_analysis.get_td(dxN, locked_nodes, n=-1) x[points - 1] = td * dxN + x[points - 1] for index in range(points - 1): if index == 0: dxi_minus_1 = dxN else: dxi_minus_1 = dx[index - 1] dx.append( compute_dx(MAass_variable_vector[index], MBass, Tass_variable_vector[index], dxi_minus_1)) td = dc_analysis.get_td(dx[index], locked_nodes, n=-1) x[index] = td * dx[index] + x[index] dx.append(dxN) if (vector_norm_wrapper(dx, vector_norm) < min(options.ver, options.ier) * vector_norm_wrapper( x, vector_norm) + min(options.vea, options.iea)): #\ #and (dc_analysis.vector_norm(residuo) < options.er*dc_analysis.vector_norm(x) + options.ea): if conv_counter == 3: converged = True break else: conv_counter = conv_counter + 1 elif vector_norm(dx[points - 1]) is numpy.nan: #needs work fixme raise OverflowError #break else: conv_counter = 0 tick.step(verbose > 2) if options.shooting_max_nr_iter and iteration == options.shooting_max_nr_iter: printing.print_general_error("Hitted SHOOTING_MAX_NR_ITER (" + str(options.shooting_max_nr_iter) + "), iteration halted.") converged = False break else: iteration = iteration + 1 tick.hide(verbose > 2) if converged: printing.print_info_line(("done.", 3), verbose) t = numpy.mat(numpy.arange(points) * step) t = t.reshape((1, points)) xmat = x[0] for index in xrange(1, points): xmat = numpy.concatenate((xmat, x[index]), axis=1) sol = results.pss_solution(circ=circ, method="shooting", period=period, outfile=data_filename, t_array=t, x_array=xmat) #print_results(circ, x, fdata, points, step) else: print "failed." sol = None return sol
def bfpss(circ, period, step=None, points=None, autonomous=False, x0=None, mna=None, Tf=None, D=None, outfile='stdout', vector_norm=lambda v: max(abs(v)), verbose=3): """Performs a PSS analysis. Time step is constant, IE will be used as DF Parameters: circ is the circuit description class period is the period of the solution mna, D, Tf are not compulsory they will be computed if they're set to None step is the time step between consecutive points points is the number of points to be used step and points are mutually exclusive options: - if step is specified, the number of points will be automatically determined - if points is set, the step will be automatically determined - if none of them is set, options.shooting_default_points will be used as points autonomous has to be False, autonomous circuits are not supported x0 is the initial guess to be used. Needs work. outfile is the output filename. Defaults to stdout. verbose is set to zero (print errors only) if datafilename == 'stdout'. Returns: nothing """ if outfile == "stdout": verbose = 0 printing.print_info_line(("Starting periodic steady state analysis:", 3), verbose) printing.print_info_line(("Method: brute-force", 3), verbose) if mna is None or Tf is None: (mna, Tf) = dc_analysis.generate_mna_and_N(circ, verbose=verbose) mna = utilities.remove_row_and_col(mna) Tf = utilities.remove_row(Tf, rrow=0) elif not mna.shape[0] == Tf.shape[0]: printing.print_general_error( "mna matrix and N vector have different number of rows.") sys.exit(0) if D is None: D = transient.generate_D(circ, [mna.shape[0], mna.shape[0]]) D = utilities.remove_row_and_col(D) elif not mna.shape == D.shape: printing.print_general_error( "mna matrix and D matrix have different sizes.") sys.exit(0) (points, step) = check_step_and_points(step, points, period) n_of_var = mna.shape[0] locked_nodes = circ.get_locked_nodes() tick = ticker.ticker(increments_for_step=1) CMAT = build_CMAT(mna, D, step, points, tick, n_of_var=n_of_var, verbose=verbose) x = build_x(mna, step, points, tick, x0=x0, n_of_var=n_of_var, verbose=verbose) Tf = build_Tf(Tf, points, tick, n_of_var=n_of_var, verbose=verbose) # time variable component: Tt this is always the same in each iter. So we build it once for all # this holds all time-dependent sources (both V/I). Tt = build_Tt(circ, points, step, tick, n_of_var=n_of_var, verbose=verbose) # Indices to differentiate between currents and voltages in the # convergence check nv_indices = [] ni_indices = [] nv_1 = len(circ.nodes_dict) - 1 ni = n_of_var - nv_1 for i in range(points): nv_indices += (i * mna.shape[0] * numpy.ones(nv_1) + \ numpy.arange(nv_1)).tolist() ni_indices += (i * mna.shape[0] * numpy.ones(ni) + \ numpy.arange(nv_1, n_of_var)).tolist() converged = False printing.print_info_line(("Solving... ", 3), verbose, print_nl=False) tick.reset() tick.display(verbose > 2) J = numpy.mat(numpy.zeros(CMAT.shape)) T = numpy.mat(numpy.zeros((CMAT.shape[0], 1))) # td is a numpy matrix that will hold the damping factors td = numpy.mat(numpy.zeros((points, 1))) iteration = 0 # newton iteration counter while True: if iteration: # the first time are already all zeros J[:, :] = 0 T[:, 0] = 0 td[:, 0] = 0 for index in xrange(1, points): for elem in circ: # build all dT(xn)/dxn (stored in J) and T(x) if elem.is_nonlinear: oports = elem.get_output_ports() for opindex in range(len(oports)): dports = elem.get_drive_ports(opindex) v_ports = [] for dpindex in range(len(dports)): dn1, dn2 = dports[dpindex] v = 0 # build v: remember we trashed the 0 row and 0 col of mna -> -1 if dn1: v = v + x[index * n_of_var + dn1 - 1, 0] if dn2: v = v - x[index * n_of_var + dn2 - 1, 0] v_ports.append(v) # all drive ports are ready. n1, n2 = oports[opindex][0], oports[opindex][1] if n1: T[index * n_of_var + n1 - 1, 0] = T[index * n_of_var + n1 - 1, 0] + \ elem.i(opindex, v_ports) if n2: T[index * n_of_var + n2 - 1, 0] = T[index * n_of_var + n2 - 1, 0] - \ elem.i(opindex, v_ports) for dpindex in range(len(dports)): dn1, dn2 = dports[dpindex] if n1: if dn1: J[index * n_of_var + n1 - 1, index * n_of_var + dn1 - 1] = \ J[index * n_of_var + n1 - 1, index * n_of_var + dn1 - 1] + \ elem.g(opindex, v_ports, dpindex) if dn2: J[index * n_of_var + n1 - 1, index * n_of_var + dn2 - 1] =\ J[index * n_of_var + n1 - 1, index * n_of_var + dn2 - 1] - 1.0 * \ elem.g(opindex, v_ports, dpindex) if n2: if dn1: J[index * n_of_var + n2 - 1, index * n_of_var + dn1 - 1] = \ J[index * n_of_var + n2 - 1, index * n_of_var + dn1 - 1] - 1.0 * \ elem.g(opindex, v_ports, dpindex) if dn2: J[index * n_of_var + n2 - 1, index * n_of_var + dn2 - 1] =\ J[index * n_of_var + n2 - 1, index * n_of_var + dn2 - 1] + \ elem.g(opindex, v_ports, dpindex) J = J + CMAT residuo = CMAT * x + T + Tf + Tt dx = -1 * (numpy.linalg.inv(J) * residuo) # td for index in xrange(points): td[index, 0] = dc_analysis.get_td(dx[index * n_of_var:(index + 1) * n_of_var, 0], locked_nodes, n=-1) x = x + min(abs(td))[0, 0] * dx # convergence check converged = convergence_check(dx, x, nv_indices, ni_indices, vector_norm) if converged: break tick.step(verbose > 2) if options.shooting_max_nr_iter and iteration == options.shooting_max_nr_iter: printing.print_general_error("Hitted SHOOTING_MAX_NR_ITER (" + str(options.shooting_max_nr_iter) + "), iteration halted.") converged = False break else: iteration = iteration + 1 tick.hide(verbose > 2) if converged: printing.print_info_line(("done.", 3), verbose) t = numpy.mat(numpy.arange(points) * step) t = t.reshape((1, points)) x = x.reshape((points, n_of_var)) sol = results.pss_solution(circ=circ, method="brute-force", period=period, outfile=outfile, t_array=t, x_array=x.T) else: print "failed." sol = None return sol