def add_inductor_coupling(self, name, L1, L2, Kvalue): """ Write DOC XXX """ L1elem, L2elem = None, None for e in self: if isinstance(e, devices.Inductor) and (L1 == e.part_id): L1elem = e elif isinstance(e, devices.Inductor) and (L2 == e.part_id): L2elem = e if L1elem is None or L2elem is None: error_msg = "One or more coupled inductors for %s were not found: %s (found: %s), %s (found: %s)." % \ (name, L1, L1elem is not None, L2, L2elem is not None) printing.print_general_error(error_msg) printing.print_general_error("Quitting.") sys.exit(30) M = math.sqrt(L1elem.L * L2elem.L) * Kvalue elem = devices.InductorCoupling(L1=L1, L2=L2, K=Kvalue, M=M) elem.part_id = name L1elem.coupling_devices.append(elem) L2elem.coupling_devices.append(elem) self.append(elem)
def main(filename, outfile="stdout", tran_method=transient.TRAP.lower(), no_step_control=False, dc_guess='guess', print_circuit=False, remote=True, verbose=3): """This method allows calling ahkab from a Python script. """ printing.print_info_line( ("This is ahkab %s running with:" % (__version__), 6), verbose) printing.print_info_line( (" Python %s" % (sys.version.split('\n')[0], ), 6), verbose) printing.print_info_line((" Numpy %s" % (numpy.__version__), 6), verbose) printing.print_info_line((" Sympy %s" % (sympy.__version__), 6), verbose) printing.print_info_line((" Matplotlib %s" % (matplotlib.__version__), 6), verbose) utilities._set_execution_lock() read_netlist_from_stdin = (filename is None or filename == "-") (circ, directives, postproc_direct) = netlist_parser.parse_circuit(filename, read_netlist_from_stdin) check, reason = dc_analysis.check_circuit(circ) if not check: printing.print_general_error(reason) printing.print_circuit(circ) sys.exit(3) if verbose > 3 or print_circuit: print "Parsed circuit:" printing.print_circuit(circ) elif verbose > 1: print circ.title.upper() an_list = netlist_parser.parse_analysis(circ, directives) postproc_list = netlist_parser.parse_postproc(circ, an_list, postproc_direct) if len(an_list) > 0: printing.print_info_line(("Requested an.:", 4), verbose) if verbose >= 4: map(printing.print_analysis, an_list) else: if verbose: printing.print_warning("No analysis requested.") if len(an_list) > 0: results = process_analysis(an_list, circ, outfile, verbose, guess=dc_guess.lower()=="guess", \ cli_tran_method=tran_method, disable_step_control=no_step_control) else: printing.print_warning("Nothing to do. Quitting.") if len(an_list) > 0 and len(postproc_list) > 0 and len(results): process_postproc(postproc_list, circ.title, results, outfile, remote) utilities._unset_execution_lock() return results
def value(self, time): if not self.ready(): printing.print_general_error("Error: sin function not well defined. This is a bug.") if time < self.td: return self.vo elif self.theta: return self.vo + self.va * math.exp(-1*(time-self.td)/self.theta) * math.sin(2*math.pi*self.freq*(time-self.td)) else: return self.vo + self.va * math.sin(2*math.pi*self.freq*(time-self.td))
def value(self, time): if not self.ready(): printing.print_general_error("Error: exp function not well defined. This is a bug.") if time < self.td1: return self.v1 elif time < self.td2: return self.v1+(self.v2 - self.v1) * (1-math.exp(-1 * (time - self.td1)/self.tau1)) else: return self.v1 + (self.v2 - self.v1) * (1 - math.exp(-1 * (time - self.td1 ) / self.tau1))+(self.v1 - self.v2 ) * ( 1 - math.exp(-1 * (time - self.td2) / self.tau2))
def read_data_header(filename): fp = open(filename, "r") line = fp.readline() if line[0] == '#': labels = line[1:].upper().split() else: printing.print_general_error("Unrecognized file: " + filename) labels = None fp.close() return labels
def add_line(self, time, x): """This method adds a solution and its corresponding time value to the results set. """ if not self._lock: time = numpy.mat(numpy.array([time])) data = numpy.concatenate((time, x), axis=0) csvlib.write_csv(self.filename, data, copy.copy(self.variables), append=self._init_file_done) self._init_file_done = True else: printing.print_general_error("Attempting to add values to a complete result set. BUG")
def add_line(self, time, x): """This method adds a solution and its corresponding time value to the results set. """ if not self._lock: time = numpy.mat(numpy.array([time])) data = numpy.concatenate((time, x), axis=0) solution._add_data(self, data) else: printing.print_general_error( "Attempting to add values to a complete result set. BUG")
def parse_postproc(circ, an_list, postproc_direc): postproc_list = [] for line, line_n in postproc_direc: if line[0] == ".": try: line_elements = line.split() #plot if line_elements[0] == ".plot": plot_postproc = {} plot_postproc["type"] = "plot" plot_postproc["analysis"] = line_elements[1] existing_an = False for an in an_list: if an["type"] == plot_postproc["analysis"]: existing_an = True if plot_postproc["analysis"] == "tran" or plot_postproc["analysis"] == "shooting": plot_postproc["x"] = "T" elif plot_postproc["analysis"] == "ac": plot_postproc["x"] = "w" #QUIRKY FOR THE TIME BEING elif plot_postproc["analysis"] == "dc": plot_postproc["x"] = an["source_name"] else: printing.print_general_error("Plotting is unsupported for analysis type "+plot_postproc["analysis"]) break if not existing_an: raise NetlistParseError("Analysis "+plot_postproc["analysis"]+" not found.") graph_labels = "" for glabel in line_elements[2:]: graph_labels = graph_labels + " " + glabel l2l1 = plotting.split_netlist_label(graph_labels) if plot_postproc["analysis"] == "ac": l2l1ac = [] for l2, l1 in l2l1: if l1 is not None: l1 = "|%s|" % (l1, ) else: l1 = None if l2 is not None: l2 = "|%s|" % (l2, ) else: l2 = None l2l1ac.append((l2,l1)) l2l1 = l2l1ac plot_postproc["l2l1"] = l2l1 postproc_list.append(plot_postproc) else: raise NetlistParseError("Unknown postproc directive.") except NetlistParseError, (msg,): if len(msg): printing.print_general_error(msg) printing.print_parse_error(line_n, line) sys.exit(0)
def parse_elem_inductor_coupling(line, circ, line_elements=None, elements=[]): """Parses a inductor coupling from the line supplied, returns a list holding the element. Parameters: line: the line, if you have already .split()-ed it, set this to None and supply the elements through line_elements. circ: the circuit instance. line_elements: will be generated by the function from line.split() if set to None. Returns: [inductor_coupling_elem] """ if line_elements is None: line_elements = line.split() if (len(line_elements) < 4) or (len(line_elements) > 4 and not line_elements[5][0]=="*"): raise NetlistParseError, "" name = line_elements[0] L1 = line_elements[1] L2 = line_elements[2] try: Kvalue = convert_units(line_elements[3]) except ValueError: (label, value) = parse_param_value_from_string(line_elements[3]) if not label == "k": raise NetlistParseError, "unknown parameter " + label Kvalue = convert_units(value) L1descr = L1[1:] L2descr = L2[1:] L1elem, L2elem = None, None for e in elements: if isinstance(e, devices.inductor) and L1descr == e.descr: L1elem = e elif isinstance(e, devices.inductor) and L2descr == e.descr: L2elem = e if L1elem is None or L2elem is None: error_msg = "One or more coupled inductors for %s were not found: %s (found: %s), %s (found: %s)." % \ (name, L1, L1elem is not None, L2, L2elem is not None) printing.print_general_error(error_msg) raise NetlistParseError, "" M = math.sqrt(L1elem.L * L2elem.L) * Kvalue elem = devices.inductor_coupling(L1=L1, L2=L2, K=Kvalue, M=M) elem.descr = name[1:] L1elem.coupling_devices.append(elem) L2elem.coupling_devices.append(elem) return [elem]
def add_line(self, time, x): """This method adds a solution and its corresponding time value to the results set. """ if not self._lock: time = numpy.mat(numpy.array([time])) data = numpy.concatenate((time, x), axis=0) solution._add_data(self, data) else: printing.print_general_error( "Attempting to add values to a complete result set. BUG" )
def _handle_netlist_ics(circ, an_list, ic_list): for ic in ic_list: ic_label = ic.keys()[0] icdict = ic[ic_label] _x0s.update({ic_label: new_x0(circ, icdict)}) for an in an_list: if 'x0' in an and isinstance(an['x0'], str): if an['x0'] in _x0s.keys(): an['x0'] = _x0s[an['x0']] elif an_list.index(an) == 0: printing.print_general_error("Unknown x0 %s" % an["x0"]) sys.exit(54)
def main_netlist_parser(circ, netlist_lines, subckts_dict, models): elements = [] try: for (line, line_n) in netlist_lines: # elements: detect the element type and call the # appropriate parsing function # we always use normal convention V opposite to I # n1 is +, n2 is -, current flows from + to - line_elements = line.split() if line[0] == "r": elements = elements + \ parse_elem_resistor(line, circ, line_elements) elif line[0] == "c": elements = elements + \ parse_elem_capacitor(line, circ, line_elements) elif line[0] == "l": elements = elements + \ parse_elem_inductor(line, circ, line_elements) elif line[0] == "k": elements = elements + \ parse_elem_inductor_coupling(line, circ, \ line_elements, elements) elif line[0] == "v": elements = elements + \ parse_elem_vsource(line, circ, line_elements) elif line[0] == "i": elements = elements + \ parse_elem_isource(line, circ, line_elements) elif line[0] == "d": elements = elements + \ parse_elem_diode(line, circ, line_elements, models) elif line[0] == 'm': #mosfet elements = elements + \ parse_elem_mos(line, circ, line_elements, models) elif line[0] == "e": #vcvs elements = elements + \ parse_elem_vcvs(line, circ, line_elements) elif line[0] == "g": #vccs elements = elements + \ parse_elem_vccs(line, circ, line_elements) elif line[0] == "y": #User defined module -> MODIFY elements = elements + \ parse_elem_user_defined(line, circ, line_elements) elif line[0] == "x": #User defined module -> MODIFY elements = elements + \ parse_sub_instance(line, circ, subckts_dict, line_elements, models) else: raise NetlistParseError, "unknown element." except NetlistParseError, (msg,): if len(msg): printing.print_general_error(msg) printing.print_parse_error(line_n, line) sys.exit(45)
def value(self, time): if not self.ready(): printing.print_general_error( "Error: exp function not well defined. This is a bug.") if time < self.td1: return self.v1 elif time < self.td2: return self.v1 + (self.v2 - self.v1) * ( 1 - math.exp(-1 * (time - self.td1) / self.tau1)) else: return self.v1 + (self.v2 - self.v1) * (1 - math.exp( -1 * (time - self.td1) / self.tau1)) + (self.v1 - self.v2) * ( 1 - math.exp(-1 * (time - self.td2) / self.tau2))
def value(self, time): if not self.ready(): printing.print_general_error( "Error: sin function not well defined. This is a bug.") if time < self.td: return self.vo elif self.theta: return self.vo + self.va * math.exp( -1 * (time - self.td) / self.theta) * math.sin( 2 * math.pi * self.freq * (time - self.td)) else: return self.vo + self.va * math.sin(2 * math.pi * self.freq * (time - self.td))
def parse_analysis(circ, directives): """Parses the analyses. Parameters: circ: a circuit class instance that descirbes the circuit. directives: a list of tuples: (line, line_number). Those lines are taken from the netlistand are the ones that hold the information about the simulations to be performed. Both of them are returned by parse_circuit() Returns: a list of the analysis, see the code. """ analysis = [] for line, line_n in directives: line = line.strip().lower() #if len(line) == 0: # continue if line[0] == ".": try: line_elements = line.split() # operating point if line_elements[0] == ".op": analysis.append(parse_an_op(line, line_elements)) # DC (direct current) sweep elif line_elements[0] == ".dc": analysis.append(parse_an_dc(line, circ, line_elements)) # AC (alternating current) sweep elif line_elements[0] == ".ac": analysis.append(parse_an_ac(line, circ, line_elements)) # transient analysis elif line_elements[0] == ".tran": analysis.append(parse_an_tran(line, line_elements)) # shooting elif line_elements[0] == ".shooting": analysis.append(parse_an_shooting(line, line_elements)) elif line_elements[0] == ".temp": analysis.append(parse_temp_directive(line, line_elements)) elif line_elements[0] == ".ic": analysis.append(parse_ic_directive(line, line_elements)) elif line_elements[0] == ".symbolic": analysis.append(parse_an_symbolic(line, circ, line_elements)) else: raise NetlistParseError("Unknown directive.") except NetlistParseError, (msg,): if len(msg): printing.print_general_error(msg) printing.print_parse_error(line_n, line) sys.exit(0)
def main(filename, outfile="stdout", verbose=3): """This method allows calling ahkab from a Python script. """ printing.print_info_line( ("This is ahkab %s running with:" % (__version__), 6), verbose) printing.print_info_line( (" Python %s" % (sys.version.split('\n')[0], ), 6), verbose) printing.print_info_line((" Numpy %s" % (numpy.__version__), 6), verbose) printing.print_info_line((" Sympy %s" % (sympy.__version__), 6), verbose) printing.print_info_line((" Matplotlib %s" % (matplotlib.__version__), 6), verbose) read_netlist_from_stdin = (filename is None or filename == "-") (circ, directives, postproc_direct) = netlist_parser.parse_circuit(filename, read_netlist_from_stdin) check, reason = dc_analysis.check_circuit(circ) if not check: printing.print_general_error(reason) printing.print_circuit(circ) sys.exit(3) if verbose > 3 or _print: print "Parsed circuit:" printing.print_circuit(circ) ic_list = netlist_parser.parse_ics(directives) _handle_netlist_ics(circ, an_list=[], ic_list=ic_list) results = {} for an in netlist_parser.parse_analysis(circ, directives): if 'outfile' not in an.keys() or not an['outfile']: an.update({ 'outfile': outfile + ("." + an['type']) * (outfile != 'stdout') }) if 'verbose' in an.keys() and (an['verbose'] is None or an['verbose'] < verbose) \ or not 'verbose' in an.keys(): an.update({'verbose': verbose}) _handle_netlist_ics(circ, [an], ic_list=[]) if verbose >= 4: printing.print_info_line(("Requested an.:", 4), verbose) printing.print_analysis(an) results.update(run(circ, [an])) postproc_list = netlist_parser.parse_postproc(circ, postproc_direct) if len(postproc_list) > 0 and len(results): process_postproc(postproc_list, circ.title, results, outfile) return results
def check_file(filename): """Checks whether the supplied path refers to a valid file. Returns: True if it's found (and is a file) False, otherwise. """ filename = os.path.abspath(filename) if not os.path.exists(filename): printing.print_general_error(filename + " not found.") ret = False elif not os.path.isfile(filename): printing.print_general_error(filename + " is not a file.") ret = False else: ret = True return ret
def check_step(tstep, time, tstop, HMAX): """Checks the step for the following problems: - the step must be shorter than HMAX (that usually is the tstep provided by the user) - the step must be shorter than the simulation time left (ie tstop - time) - the step must be longer than options.hmin, if not halt the simulation. Returns: the step provided if it's ok, a shortened step otherwise. """ if tstep > HMAX: tstep = HMAX if tstop - time < tstep: tstep = tstop - time elif tstep < options.hmin: printing.print_general_error("Step size too small: "+str(tstep)) raise Exception, "Step size too small" return tstep
def check_step(tstep, time, tstop, HMAX): """Checks the step for the following problems: - the step must be shorter than HMAX (that usually is the tstep provided by the user) - the step must be shorter than the simulation time left (ie tstop - time) - the step must be longer than options.hmin, if not halt the simulation. Returns: the step provided if it's ok, a shortened step otherwise. """ if tstep > HMAX: tstep = HMAX if tstop - time < tstep: tstep = tstop - time elif tstep < options.hmin: printing.print_general_error("Step size too small: " + str(tstep)) raise Exception, "Step size too small" return tstep
def main(filename, outfile="stdout", verbose=3): """This method allows calling ahkab from a Python script. """ printing.print_info_line( ("This is ahkab %s running with:" % (__version__), 6), verbose) printing.print_info_line( (" Python %s" % (sys.version.split('\n')[0],), 6), verbose) printing.print_info_line((" Numpy %s" % (numpy.__version__), 6), verbose) printing.print_info_line((" Sympy %s" % (sympy.__version__), 6), verbose) printing.print_info_line( (" Matplotlib %s" % (matplotlib.__version__), 6), verbose) read_netlist_from_stdin = (filename is None or filename == "-") (circ, directives, postproc_direct) = netlist_parser.parse_circuit( filename, read_netlist_from_stdin) check, reason = dc_analysis.check_circuit(circ) if not check: printing.print_general_error(reason) printing.print_circuit(circ) sys.exit(3) if verbose > 3 or _print: print "Parsed circuit:" printing.print_circuit(circ) ic_list = netlist_parser.parse_ics(directives) _handle_netlist_ics(circ, an_list=[], ic_list=ic_list) results = {} for an in netlist_parser.parse_analysis(circ, directives): if 'outfile' not in an.keys() or not an['outfile']: an.update( {'outfile': outfile + ("." + an['type']) * (outfile != 'stdout')}) if 'verbose' in an.keys() and (an['verbose'] is None or an['verbose'] < verbose) \ or not 'verbose' in an.keys(): an.update({'verbose': verbose}) _handle_netlist_ics(circ, [an], ic_list=[]) if verbose >= 4: printing.print_info_line(("Requested an.:", 4), verbose) printing.print_analysis(an) results.update(run(circ, [an])) postproc_list = netlist_parser.parse_postproc(circ, postproc_direct) if len(postproc_list) > 0 and len(results): process_postproc(postproc_list, circ.title, results, outfile) return results
def main(filename, outfile="stdout", tran_method=transient.TRAP.lower(), no_step_control=False, dc_guess='guess', print_circuit=False, remote=True, verbose=3): """This method allows calling ahkab from a Python script. """ printing.print_info_line(("This is ahkab %s running with:" %(__version__),6), verbose) printing.print_info_line((" Python %s" % (sys.version.split('\n')[0],),6), verbose) printing.print_info_line((" Numpy %s" % (numpy.__version__),6), verbose) printing.print_info_line((" Sympy %s" % (sympy.__version__),6), verbose) printing.print_info_line((" Matplotlib %s" % (matplotlib.__version__),6), verbose) utilities._set_execution_lock() read_netlist_from_stdin = (filename is None or filename == "-") (circ, directives, postproc_direct) = netlist_parser.parse_circuit(filename, read_netlist_from_stdin) check, reason = dc_analysis.check_circuit(circ) if not check: printing.print_general_error(reason) printing.print_circuit(circ) sys.exit(3) if verbose > 3 or print_circuit: print "Parsed circuit:" printing.print_circuit(circ) elif verbose > 1: print circ.title.upper() an_list = netlist_parser.parse_analysis(circ, directives) postproc_list = netlist_parser.parse_postproc(circ, an_list, postproc_direct) if len(an_list) > 0: printing.print_info_line(("Requested an.:", 4), verbose) if verbose >= 4: map(printing.print_analysis, an_list) else: if verbose: printing.print_warning("No analysis requested.") if len(an_list) > 0: results = process_analysis(an_list, circ, outfile, verbose, guess=dc_guess.lower()=="guess", \ cli_tran_method=tran_method, disable_step_control=no_step_control) else: printing.print_warning("Nothing to do. Quitting.") if len(an_list) > 0 and len(postproc_list) > 0 and len(results): process_postproc(postproc_list, circ.title, results, outfile, remote) utilities._unset_execution_lock() return results
def import_custom_df_module(method, print_out): """Imports a module that implements differentiation formula through imp.load_module Parameters: method: a string, the name of the df method module print_out: print to stdout some verbose messages Returns: The df module or None if the module is not found. """ try: df = imp.load_module(imp.find_module(method.lower())) if print_out: print "Custom df module "+method.lower()+" loaded." except: printing.print_general_error("Unrecognized method: "+method.lower()+".") df = None return df
def read_data(filename, label, labels=None): label = label.upper() if labels == None: labels = read_data_header(filename) else: labels = map(str.upper, labels) try: index = labels.index(label) fp = open(filename, "r") data = [] for line in fp: if line.strip()[0] != '#': data.append(float(line.split()[index])) fp.close() data = numpy.array(data) except ValueError: printing.print_general_error("Unrecognized data set: " + label) data = None return data
def import_custom_df_module(method, print_out): """Imports a module that implements differentiation formula through imp.load_module Parameters: method: a string, the name of the df method module print_out: print to stdout some verbose messages Returns: The df module or None if the module is not found. """ try: df = imp.load_module(imp.find_module(method.lower())) if print_out: print "Custom df module " + method.lower() + " loaded." except: printing.print_general_error("Unrecognized method: " + method.lower() + ".") df = None return df
def dc_analysis(circ, start, stop, step, source, sweep_type='LINEAR', guess=True, x0=None, outfile="stdout", verbose=3): """Performs a sweep of the value of V or I of a independent source from start value to stop value using the provided step. For every circuit generated, computes the op and prints it out. This function relays on dc_analysis.op_analysis to actually solve each circuit. circ: the circuit instance to be simulated start: start value of the sweep source stop: stop value of the sweep source step: the step size in the sweep source: string, the name of the source to be swept sweep_type: either options.dc_lin_step (default) or options.dc_log_step guess: op_analysis will guess to start the first NR iteration for the first point, the previsious dc is used from then on outfile: string, filename of the output file. If set to 'stdout', prints to screen. verbose: verbosity level Returns: * A results.dc_solution instance, if a solution was found for at least one sweep value. * None, if an error occurred (eg invalid start/stop/step values) or there was no solution for any sweep value. """ if outfile == 'stdout': verbose = 0 printing.print_info_line(("Starting DC analysis:", 2), verbose) elem_type, elem_descr = source[0].lower(), source.lower() # eg. 'v', 'v34' sweep_label = elem_type[0].upper() + elem_descr[1:] if sweep_type == options.dc_log_step and stop - start < 0: printing.print_general_error( "DC analysis has log sweeping and negative stepping.") sys.exit(1) if (stop - start) * step < 0: raise ValueError, "Unbonded stepping in DC analysis." points = (stop - start) / step + 1 sweep_type = sweep_type.upper()[:3] if sweep_type == options.dc_log_step: dc_iter = utilities.log_axis_iterator(stop, start, nsteps=points) elif sweep_type == options.dc_lin_step: dc_iter = utilities.lin_axis_iterator(stop, start, nsteps=points) else: printing.print_general_error("Unknown sweep type: %s" % (sweep_type, )) sys.exit(1) if elem_type != 'v' and elem_type != 'i': printing.print_general_error( "Sweeping is possible only with voltage and current sources. (" + str(elem_type) + ")") sys.exit(1) source_elem = None for index in xrange(len(circ)): if circ[index].part_id.lower() == elem_descr: if elem_type == 'v': if isinstance(circ[index], devices.VSource): source_elem = circ[index] break if elem_type == 'i': if isinstance(circ[index], devices.ISource): source_elem = circ[index] break if not source_elem: printing.print_general_error("%s was not found." % source[0].part_id) sys.exit(1) if isinstance(source_elem, devices.VSource): initial_value = source_elem.dc_value else: initial_value = source_elem.dc_value # If the initial value is set to None, op_analysis will attempt a smart guess (if guess), # Then for each iteration, the last result is used as x0, since op_analysis will not # attempt to guess the op if x0 is not None. x = x0 sol = results.dc_solution(circ, start, stop, sweepvar=sweep_label, stype=sweep_type, outfile=outfile) printing.print_info_line(("Solving... ", 3), verbose, print_nl=False) tick = ticker.ticker(1) tick.display(verbose > 2) # sweep setup # tarocca il generatore di tensione, avvia DC silenziosa, ritarocca etc index = 0 for sweep_value in dc_iter: index = index + 1 if isinstance(source_elem, devices.VSource): source_elem.dc_value = sweep_value else: source_elem.dc_value = sweep_value # silently calculate the op x = op_analysis(circ, x0=x, guess=guess, verbose=0) if x is None: tick.hide(verbose > 2) if not options.dc_sweep_skip_allowed: print "Could't solve the circuit for sweep value:", start + index * step solved = False break else: print "Skipping sweep value:", start + index * step continue solved = True sol.add_op(sweep_value, x) tick.step(verbose > 2) tick.hide(verbose > 2) if solved: printing.print_info_line(("done", 3), verbose) # clean up if isinstance(source_elem, devices.VSource): source_elem.dc_value = initial_value else: source_elem.dc_value = initial_value return sol if solved else None
def op_analysis(circ, x0=None, guess=True, outfile=None, verbose=3): """Runs an Operating Point (OP) analysis circ: the circuit instance on which the simulation is run x0: is the initial guess to be used to start the NR mdn_solver guess: if set to True and x0 is None, it will generate a 'smart' guess verbose: verbosity level from 0 (silent) to 6 (debug). Returns a Operation Point result, if successful, None otherwise. """ if outfile == 'stdout': verbose = 0 # silent mode, print out results only. if not options.dc_use_guess: guess = False (mna, N) = generate_mna_and_N(circ, verbose=verbose) printing.print_info_line(("MNA matrix and constant term (complete):", 4), verbose) printing.print_info_line((str(mna), 4), verbose) printing.print_info_line((str(N), 4), verbose) # lets trash the unneeded col & row printing.print_info_line(("Removing unneeded row and column...", 4), verbose) mna = utilities.remove_row_and_col(mna) N = utilities.remove_row(N, rrow=0) printing.print_info_line(("Starting op analysis:", 2), verbose) if x0 is None and guess: x0 = dc_guess.get_dc_guess(circ, verbose=verbose) # if x0 is not None, use that printing.print_info_line(("Solving with Gmin:", 4), verbose) Gmin_matrix = build_gmin_matrix(circ, options.gmin, mna.shape[0], verbose - 2) (x1, error1, solved1, n_iter1) = dc_solve(mna, N, circ, Gmin=Gmin_matrix, x0=x0, verbose=verbose) # We'll check the results now. Recalculate them without Gmin (using previsious solution as initial guess) # and check that differences on nodes and current do not exceed the # tolerances. if solved1: op1 = results.op_solution(x1, error1, circ, outfile=outfile, iterations=n_iter1) printing.print_info_line(("Solving without Gmin:", 4), verbose) (x2, error2, solved2, n_iter2) = dc_solve(mna, N, circ, Gmin=None, x0=x1, verbose=verbose) else: solved2 = False if solved1 and not solved2: printing.print_general_error("Can't solve without Gmin.") if verbose: print "Displaying latest valid results." op1.write_to_file(filename='stdout') opsolution = op1 elif solved1 and solved2: op2 = results.op_solution(x2, error2, circ, outfile=outfile, iterations=n_iter1 + n_iter2) op2.gmin = 0 badvars = results.op_solution.gmin_check(op2, op1) printing.print_result_check(badvars, verbose=verbose) check_ok = not (len(badvars) > 0) if not check_ok and verbose: print "Solution with Gmin:" op1.write_to_file(filename='stdout') print "Solution without Gmin:" op2.write_to_file(filename='stdout') opsolution = op2 else: # not solved1 printing.print_general_error("Couldn't solve the circuit. Giving up.") opsolution = None if opsolution and outfile != 'stdout' and outfile is not None: opsolution.write_to_file() if opsolution and verbose > 2 and options.cli: opsolution.write_to_file(filename='stdout') return opsolution
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 dc_solve(mna, Ndc, circ, Ntran=None, Gmin=None, x0=None, time=None, MAXIT=None, locked_nodes=None, skip_Tt=False, verbose=3): """Tries to perform a DC analysis of the circuit. The system we want to solve is: (mna+Gmin)*x + N + T(x) = 0 mna is the reduced mna matrix with the required KVL rows N is Ndc + Ntran T(x) will be built. circ is the circuit instance from which mna, N were built. x0 is the (optional) initial guess. If not specified, the all-zeros vector will be used This method is used ever by transient analysis. For transient analysis, time has to be set. In "real" DC analysis, time may be left to None. See circuit.py for more info about the default behaviour of time variant sources that also have a dc value. MAXIT is the maximum number of NR iteration to be supplied to mdn_solver. locked_nodes: array of tuples of nodes controlling non linear elements of the circuit. This is generated by circ.get_locked_nodes() and will be generated that way if left to none. However, if you are doing lots of simulations of the same circuit (a transient analysis), it's a good idea to generate it only once. Returns: (x, error, converged, tot_iterations) """ if MAXIT == None: MAXIT = options.dc_max_nr_iter if locked_nodes is None: locked_nodes = circ.get_locked_nodes() mna_size = mna.shape[0] nv = len(circ.nodes_dict) tot_iterations = 0 if Gmin is None: Gmin = 0 if Ntran is None: Ntran = 0 # time variable component: Tt this is always the same in each iter. So we # build it once for all. Tt = numpy.mat(numpy.zeros((mna_size, 1))) v_eq = 0 if not skip_Tt: for elem in circ: if (isinstance(elem, devices.VSource) or isinstance( elem, devices.ISource)) and elem.is_timedependent: if isinstance(elem, devices.VSource): Tt[nv - 1 + v_eq, 0] = -1 * elem.V(time) elif isinstance(elem, devices.ISource): if elem.n1: Tt[elem.n1 - 1, 0] = Tt[elem.n1 - 1, 0] + elem.I(time) if elem.n2: Tt[elem.n2 - 1, 0] = Tt[elem.n2 - 1, 0] - elem.I(time) if circuit.is_elem_voltage_defined(elem): v_eq = v_eq + 1 # update N to include the time variable sources Ndc = Ndc + Tt # initial guess, if specified, otherwise it's zero if x0 is not None: if isinstance(x0, results.op_solution): x = x0.asmatrix() else: x = x0 else: x = numpy.mat(numpy.zeros((mna_size, 1))) # has n-1 rows because of discard of ^^^ converged = False standard_solving, gmin_stepping, source_stepping = get_solve_methods() standard_solving, gmin_stepping, source_stepping = set_next_solve_method( standard_solving, gmin_stepping, source_stepping, verbose) printing.print_info_line(("Solving... ", 3), verbose, print_nl=False) while (not converged): if standard_solving["enabled"]: mna_to_pass = mna + Gmin N_to_pass = Ndc + Ntran * (Ntran is not None) elif gmin_stepping["enabled"]: # print "gmin index:", str(gmin_stepping["index"])+", gmin:", str( # 10**(gmin_stepping["factors"][gmin_stepping["index"]])) printing.print_info_line( ("Setting Gmin to: " + str(10**gmin_stepping["factors"][gmin_stepping["index"]]), 6), verbose) mna_to_pass = build_gmin_matrix( circ, 10**(gmin_stepping["factors"][gmin_stepping["index"]]), mna_size, verbose) + mna N_to_pass = Ndc + Ntran * (Ntran is not None) elif source_stepping["enabled"]: printing.print_info_line(("Setting sources to " + str( source_stepping["factors"][source_stepping["index"]] * 100) + "% of their actual value", 6), verbose) mna_to_pass = mna + Gmin N_to_pass = source_stepping["factors"][ source_stepping["index"]] * Ndc + Ntran * (Ntran is not None) try: (x, error, converged, n_iter, convergence_by_node) = mdn_solver(x, mna_to_pass, circ, T=N_to_pass, nv=nv, print_steps=(verbose > 0), locked_nodes=locked_nodes, time=time, MAXIT=MAXIT, debug=(verbose == 6)) tot_iterations += n_iter except numpy.linalg.linalg.LinAlgError: n_iter = 0 converged = False print "failed." printing.print_general_error("J Matrix is singular") except OverflowError: n_iter = 0 converged = False print "failed." printing.print_general_error("Overflow") if not converged: if verbose == 6: for ivalue in range(len(convergence_by_node)): if not convergence_by_node[ivalue] and ivalue < nv - 1: print "Convergence problem node %s" % ( circ.int_node_to_ext(ivalue), ) elif not convergence_by_node[ivalue] and ivalue >= nv - 1: e = circ.find_vde(ivalue) print "Convergence problem current in %s" % e.part_id if n_iter == MAXIT - 1: printing.print_general_error("Error: MAXIT exceeded (" + str(MAXIT) + ")") if more_solve_methods_available(standard_solving, gmin_stepping, source_stepping): standard_solving, gmin_stepping, source_stepping = set_next_solve_method( standard_solving, gmin_stepping, source_stepping, verbose) else: # print "Giving up." x = None error = None break else: printing.print_info_line(("[%d iterations]" % (n_iter, ), 6), verbose) if (source_stepping["enabled"] and source_stepping["index"] != 9): converged = False source_stepping["index"] = source_stepping["index"] + 1 elif (gmin_stepping["enabled"] and gmin_stepping["index"] != 9): gmin_stepping["index"] = gmin_stepping["index"] + 1 converged = False else: printing.print_info_line((" done.", 3), verbose) return (x, error, converged, tot_iterations)
def op_analysis(circ, x0=None, guess=True, data_filename=None, verbose=3): """Runs an Operating Point (OP) analysis circ: the circuit instance on which the simulation is run x0: is the initial guess to be used to start the NR mdn_solver guess: if set to True and x0 is None, it will generate a 'smart' guess verbose: verbosity level from 0 (silent) to 6 (debug). Returns a Operation Point result, if successful, None otherwise. """ # use_gmin = True # solved=False # x0 = numpy.mat(numpy.zeros((1,2))) (mna, N) = generate_mna_and_N(circ) printing.print_info_line(("MNA matrix and constant term (complete):", 4), verbose) printing.print_info_line((str(mna), 4), verbose) printing.print_info_line((str(N), 4), verbose) # lets trash the unneeded col & row printing.print_info_line(("Removing unneeded row and column...", 4), verbose) mna = utilities.remove_row_and_col(mna) N = utilities.remove_row(N, rrow=0) printing.print_info_line(("Starting op analysis:", 2), verbose) if x0 is None and guess: x0 = dc_guess.get_dc_guess(circ, verbose=verbose) # if x0 is not None, use that printing.print_info_line(("Solving with Gmin:", 4), verbose) Gmin_matrix = build_gmin_matrix(circ, options.gmin, mna.shape[0], verbose - 2) (x1, error1, solved1, n_iter1) = dc_solve(mna, N, circ, Gmin=Gmin_matrix, x0=x0, verbose=verbose) # We'll check the results now. Recalculate them without Gmin (using previsious solution as initial guess) # and check that differences on nodes and current do not exceed the tolerances. if solved1: op1 = results.op_solution(x1, error1, circ, outfile=data_filename, iterations=n_iter1) printing.print_info_line(("Solving without Gmin:", 4), verbose) (x2, error2, solved2, n_iter2) = dc_solve(mna, N, circ, Gmin=None, x0=x1, verbose=verbose) if not solved2: printing.print_general_error("Can't solve without Gmin.") if verbose: print "Displaying latest valid results." op1.write_to_file(filename="stdout") opsolution = op1 else: op2 = results.op_solution(x2, error2, circ, outfile=data_filename, iterations=n_iter1 + n_iter2) op2.gmin = 0 badvars = results.op_solution.gmin_check(op2, op1) printing.print_result_check(badvars, verbose=verbose) check_ok = not (len(badvars) > 0) if not check_ok and verbose: print "Solution with Gmin:" op1.write_to_file(filename="stdout") print "Solution without Gmin:" if verbose: op2.write_to_file(filename="stdout") opsolution = op2 if data_filename != "stdout" and data_filename is not None: opsolution.write_to_file() else: printing.print_general_error("Couldn't solve the circuit. Giving up.") opsolution = None return opsolution
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 process_analysis(an_list, circ, outfile, verbose, cli_tran_method=None, guess=True, disable_step_control=False): """ Processes an analysis vector: an_list: the list of analysis to be performed, as returned by netlist_parser circ: the circuit instance, returned by netlist_parser outfile: a filename. Results will be written to it. If set to stdout, prints to stdout verbose: verbosity level cli_tran_method: force the specified method in each tran analysis (see transient.py) guess: use the builtin method get_dc_guess to guess x0 Returns: None """ x0_op = None x0_ic_dict = {} results = {} for directive in [x for x in an_list if x["type"] == "ic"]: x0_ic_dict.update({ directive["name"]:\ dc_analysis.build_x0_from_user_supplied_ic(circ, voltages_dict=directive["vdict"], currents_dict=directive["cdict"]) }) for an in an_list: if outfile != 'stdout': data_filename = outfile + "." + an["type"] else: data_filename = outfile if an["type"] == "ic": continue if an["type"] == "op": if not an.has_key('guess_label') or an["guess_label"] is None: x0_op = dc_analysis.op_analysis(circ, guess=guess, data_filename=data_filename, verbose=verbose) else: if not an["guess_label"] in x0_ic_dict: printing.print_warning( "op: guess is set but no matching .ic directive was found." ) printing.print_warning( "op: using built-in guess method: " + str(guess)) x0_op = dc_analysis.op_analysis(circ, guess=guess, verbose=verbose) else: x0_op = dc_analysis.op_analysis( circ, guess=False, x0=x0_ic_dict[an["guess_label"]], verbose=verbose) sol = x0_op elif an["type"] == "dc": if an["source_name"][0].lower() == "v": elem_type = "vsource" elif an["source_name"][0].lower() == "i": elem_type = "isource" else: printing.print_general_error( "Type of sweep source is unknown: " + an[1][0]) sys.exit(1) sol = dc_analysis.dc_analysis( circ, start=an["start"], stop=an["stop"], step=an["step"], \ type_descr=(elem_type, an["source_name"][1:]), xguess=x0_op, data_filename=data_filename, guess=guess, stype=an['stype'], verbose=verbose) #{"type":"tran", "tstart":tstart, "tstop":tstop, "tstep":tstep, "uic":uic, "method":method, "ic_label":ic_label} elif an["type"] == "tran": if cli_tran_method is not None: tran_method = cli_tran_method.upper() elif an["method"] is not None: tran_method = an["method"].upper() else: tran_method = options.default_tran_method # setup the initial condition (t=0) according to uic # uic = 0 -> all node voltages and currents are zero # uic = 1 -> node voltages and currents are those computed in the last OP analysis # uic = 2 -> node voltages and currents are those computed in the last OP analysis # combined with the ic=XX directive found in capacitors and inductors # uic = 3 -> use a .ic directive defined by the user uic = an["uic"] if uic == 0: x0 = None elif uic == 1: if x0_op is None: printing.print_general_error( "uic is set to 1, but no op has been calculated yet.") sys.exit(51) x0 = x0_op elif uic == 2: if x0_op is None: printing.print_general_error( "uic is set to 2, but no op has been calculated yet.") sys.exit(51) x0 = dc_analysis.modify_x0_for_ic(circ, x0_op) elif uic == 3: if an["ic_label"] is None: printing.print_general_error( "uic is set to 3, but param ic=<ic_label> was not defined." ) sys.exit(53) elif not an["ic_label"] in x0_ic_dict: printing.print_general_error("uic is set to 3, but no .ic directive named %s was found." \ %(str(an["ic_label"]),)) sys.exit(54) x0 = x0_ic_dict[an["ic_label"]] sol = transient.transient_analysis(circ, \ tstart=an["tstart"], tstep=an["tstep"], tstop=an["tstop"], \ x0=x0, mna=None, N=None, verbose=verbose, data_filename=data_filename, \ use_step_control=(not disable_step_control), method=tran_method) elif an["type"] == "shooting": if an["method"] == "brute-force": sol = bfpss.bfpss(circ, period=an["period"], step=an["step"], mna=None, Tf=None, \ D=None, points=an["points"], autonomous=an["autonomous"], x0=x0_op, \ data_filename=data_filename, verbose=verbose) elif an["method"] == "shooting": sol = shooting.shooting(circ, period=an["period"], step=an["step"], mna=None, \ Tf=None, D=None, points=an["points"], autonomous=an["autonomous"], \ data_filename=data_filename, verbose=verbose) elif an["type"] == "symbolic": if not 'subs' in an.keys(): an.update({'subs': None}) sol = symbolic.solve(circ, an['source'], opts={'ac': an['ac']}, subs=an['subs'], verbose=verbose) elif an["type"] == "ac": sol = ac.ac_analysis(circ=circ, start=an['start'], nsteps=an['nsteps'], \ stop=an['stop'], step_type='LOG', xop=x0_op, mna=None,\ data_filename=data_filename, verbose=verbose) elif an["type"] == "temp": constants.T = utilities.Celsius2Kelvin(an['temp']) results.update({an["type"]: sol}) return results
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 get_df(pv_array, suggested_step, predict=False): """The array must be built in this way: It has to be an array of arrays. Each of them has the following structure: [time, numpy_matrix, numpy_matrix] Hence the pv_array[k] element is made of: _ time is the time in which the solution is valid: t(n-k) _ The first numpy_matrix is x(n-k) _ The second is d(x(n-k))/dt Values that are not needed may be set to None and they will be disregarded. if predict == True, it needs one more point to give a prediction of x at the suggested step. Returns: None if the incorrect values were given, or quits. Otherwise returns an array: _ the [0] element is the numpy matrix of coeffiecients (Nx1) of x(n+1) _ the [1] element is the numpy matrix of constant terms (Nx1) of x(n+1) The derivative may be written as: d(x(n+1))/dt = ret[0]*x(n+1) + ret[1]""" if order is None: printing.print_general_error("You must set Gear's order before using it! e.g. gear.order = 5") sys.exit(1) s = [] s.append(0) for index in range(1, order + 2): s.append(suggested_step + pv_array[0][0] - pv_array[index - 1][0]) # build e[k, i] e = numpy.mat(numpy.zeros((order + 2, order + 2))) for k_index in range(1, order + 2): for i_index in range(1, order + 2): if i_index == k_index: e[k_index, i_index] = 1 else: e[k_index, i_index] = s[i_index] / (s[i_index] -s[k_index]) alpha = numpy.mat(numpy.zeros((1, order + 2))) for k_index in range(1, order + 2): alpha[0, k_index] = 1.0 for j_index in range(order + 1): alpha[0, k_index] = alpha[0, k_index] * e[k_index, j_index+1] #build gamma gamma = numpy.mat(numpy.zeros((1, order +1))) for k_index in range(1, order + 1): gamma[0, k_index] = alpha[0, k_index] * ((1.0/s[order+1]) - (1.0/s[k_index])) gamma[0, 0] = 0 for index in range(1, order + 1): gamma[0, 0] = gamma[0, 0] - gamma[0, index] # values to be returned C1 = gamma[0, 0] C0 = numpy.mat(numpy.zeros(pv_array[0][1].shape)) for index in range(order): C0 = C0 + gamma[0, index + 1] * pv_array[index][1] x_lte_coeff = 0 for k_index in range(1, order+1): x_lte_coeff = x_lte_coeff + (s[k_index] ** (order + 1)) * (-1.0 * gamma[0, k_index] / gamma[0, 0]) x_lte_coeff = ((-1.0)**(order + 1)) *(1.0/utilities.fact(order + 1)) * x_lte_coeff if predict: predict_x = numpy.mat(numpy.zeros(pv_array[0][1].shape)) for index in range(1, order + 2): #order predict_x = predict_x + alpha[0, index] * pv_array[index - 1][1] predict_lte_coeff = -1.0/(utilities.fact(order + 1)) for index in range(1, order + 2): predict_lte_coeff = predict_lte_coeff * s[index] #print predict_lte_coeff #print x_lte_coeff else: predict_x = None predict_lte_coeff = None return [C1, C0, x_lte_coeff, predict_x, predict_lte_coeff]
def process_analysis(an_list, circ, outfile, verbose, cli_tran_method=None, guess=True, disable_step_control=False): """ Processes an analysis vector: an_list: the list of analysis to be performed, as returned by netlist_parser circ: the circuit instance, returned by netlist_parser outfile: a filename. Results will be written to it. If set to stdout, prints to stdout verbose: verbosity level cli_tran_method: force the specified method in each tran analysis (see transient.py) guess: use the builtin method get_dc_guess to guess x0 Returns: None """ x0_op = None x0_ic_dict = {} results = {} for directive in [ x for x in an_list if x["type"] == "ic" ]: x0_ic_dict.update({ directive["name"]:\ dc_analysis.build_x0_from_user_supplied_ic(circ, voltages_dict=directive["vdict"], currents_dict=directive["cdict"]) }) for an in an_list: if outfile != 'stdout': data_filename = outfile + "." + an["type"] else: data_filename = outfile if an["type"] == "ic": continue if an["type"] == "op": if not an.has_key('guess_label') or an["guess_label"] is None: x0_op = dc_analysis.op_analysis(circ, guess=guess, data_filename=data_filename, verbose=verbose) else: if not an["guess_label"] in x0_ic_dict: printing.print_warning("op: guess is set but no matching .ic directive was found.") printing.print_warning("op: using built-in guess method: "+str(guess)) x0_op = dc_analysis.op_analysis(circ, guess=guess, verbose=verbose) else: x0_op = dc_analysis.op_analysis(circ, guess=False, x0=x0_ic_dict[an["guess_label"]], verbose=verbose) sol = x0_op elif an["type"] == "dc": if an["source_name"][0].lower() == "v": elem_type = "vsource" elif an["source_name"][0].lower() == "i": elem_type = "isource" else: printing.print_general_error("Type of sweep source is unknown: " + an[1][0]) sys.exit(1) sol = dc_analysis.dc_analysis( circ, start=an["start"], stop=an["stop"], step=an["step"], \ type_descr=(elem_type, an["source_name"][1:]), xguess=x0_op, data_filename=data_filename, guess=guess, stype=an['stype'], verbose=verbose) #{"type":"tran", "tstart":tstart, "tstop":tstop, "tstep":tstep, "uic":uic, "method":method, "ic_label":ic_label} elif an["type"] == "tran": if cli_tran_method is not None: tran_method = cli_tran_method.upper() elif an["method"] is not None: tran_method = an["method"].upper() else: tran_method = options.default_tran_method # setup the initial condition (t=0) according to uic # uic = 0 -> all node voltages and currents are zero # uic = 1 -> node voltages and currents are those computed in the last OP analysis # uic = 2 -> node voltages and currents are those computed in the last OP analysis # combined with the ic=XX directive found in capacitors and inductors # uic = 3 -> use a .ic directive defined by the user uic = an["uic"] if uic == 0: x0 = None elif uic == 1: if x0_op is None: printing.print_general_error("uic is set to 1, but no op has been calculated yet.") sys.exit(51) x0 = x0_op elif uic == 2: if x0_op is None: printing.print_general_error("uic is set to 2, but no op has been calculated yet.") sys.exit(51) x0 = dc_analysis.modify_x0_for_ic(circ, x0_op) elif uic == 3: if an["ic_label"] is None: printing.print_general_error("uic is set to 3, but param ic=<ic_label> was not defined.") sys.exit(53) elif not an["ic_label"] in x0_ic_dict: printing.print_general_error("uic is set to 3, but no .ic directive named %s was found." \ %(str(an["ic_label"]),)) sys.exit(54) x0 = x0_ic_dict[an["ic_label"]] sol = transient.transient_analysis(circ, \ tstart=an["tstart"], tstep=an["tstep"], tstop=an["tstop"], \ x0=x0, mna=None, N=None, verbose=verbose, data_filename=data_filename, \ use_step_control=(not disable_step_control), method=tran_method) elif an["type"] == "shooting": if an["method"]=="brute-force": sol = bfpss.bfpss(circ, period=an["period"], step=an["step"], mna=None, Tf=None, \ D=None, points=an["points"], autonomous=an["autonomous"], x0=x0_op, \ data_filename=data_filename, verbose=verbose) elif an["method"]=="shooting": sol = shooting.shooting(circ, period=an["period"], step=an["step"], mna=None, \ Tf=None, D=None, points=an["points"], autonomous=an["autonomous"], \ data_filename=data_filename, verbose=verbose) elif an["type"] == "symbolic": if not 'subs' in an.keys(): an.update({'subs':None}) sol = symbolic.solve(circ, an['source'], opts={'ac':an['ac']}, subs=an['subs'], verbose=verbose) elif an["type"] == "ac": sol = ac.ac_analysis(circ=circ, start=an['start'], nsteps=an['nsteps'], \ stop=an['stop'], step_type='LOG', xop=x0_op, mna=None,\ data_filename=data_filename, verbose=verbose) elif an["type"] == "temp": constants.T = utilities.Celsius2Kelvin(an['temp']) results.update({an["type"]:sol}) return results
def get_df(pv_array, suggested_step, predict=False): """The array must be built in this way: It has to be an array of arrays. Each of them has the following structure: [time, numpy_matrix, numpy_matrix] Hence the pv_array[k] element is made of: _ time is the time in which the solution is valid: t(n-k) _ The first numpy_matrix is x(n-k) _ The second is d(x(n-k))/dt Values that are not needed may be set to None and they will be disregarded. if predict == True, it needs one more point to give a prediction of x at the suggested step. Returns: None if the incorrect values were given, or quits. Otherwise returns an array: _ the [0] element is the numpy matrix of coeffiecients (Nx1) of x(n+1) _ the [1] element is the numpy matrix of constant terms (Nx1) of x(n+1) The derivative may be written as: d(x(n+1))/dt = ret[0]*x(n+1) + ret[1]""" if order is None: printing.print_general_error( "You must set Gear's order before using it! e.g. gear.order = 5") sys.exit(1) s = [] s.append(0) for index in range(1, order + 2): s.append(suggested_step + pv_array[0][0] - pv_array[index - 1][0]) # build e[k, i] e = numpy.mat(numpy.zeros((order + 2, order + 2))) for k_index in range(1, order + 2): for i_index in range(1, order + 2): if i_index == k_index: e[k_index, i_index] = 1 else: e[k_index, i_index] = s[i_index] / (s[i_index] - s[k_index]) alpha = numpy.mat(numpy.zeros((1, order + 2))) for k_index in range(1, order + 2): alpha[0, k_index] = 1.0 for j_index in range(order + 1): alpha[0, k_index] = alpha[0, k_index] * e[k_index, j_index + 1] # build gamma gamma = numpy.mat(numpy.zeros((1, order + 1))) for k_index in range(1, order + 1): gamma[0, k_index] = alpha[0, k_index] * \ ((1.0 / s[order + 1]) - (1.0 / s[k_index])) gamma[0, 0] = 0 for index in range(1, order + 1): gamma[0, 0] = gamma[0, 0] - gamma[0, index] # values to be returned C1 = gamma[0, 0] C0 = numpy.mat(numpy.zeros(pv_array[0][1].shape)) for index in range(order): C0 = C0 + gamma[0, index + 1] * pv_array[index][1] x_lte_coeff = 0 for k_index in range(1, order + 1): x_lte_coeff = x_lte_coeff + \ (s[k_index] ** (order + 1)) * \ (-1.0 * gamma[0, k_index] / gamma[0, 0]) x_lte_coeff = ((-1.0) ** (order + 1)) * \ (1.0 / utilities.fact(order + 1)) * x_lte_coeff if predict: predict_x = numpy.mat(numpy.zeros(pv_array[0][1].shape)) for index in range(1, order + 2): # order predict_x = predict_x + alpha[0, index] * pv_array[index - 1][1] predict_lte_coeff = -1.0 / (utilities.fact(order + 1)) for index in range(1, order + 2): predict_lte_coeff = predict_lte_coeff * s[index] # print predict_lte_coeff # print x_lte_coeff else: predict_x = None predict_lte_coeff = None return [C1, C0, x_lte_coeff, predict_x, predict_lte_coeff]
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 dc_analysis( circ, start, stop, step, type_descr, xguess=None, data_filename="stdout", print_int_nodes=True, guess=True, stype="LINEAR", verbose=2, ): """Performs a sweep of the value of V or I of a independent source from start value to stop value using the provided step. For every circuit generated, computes the op and prints it out. This function relays on dc_analysis.op_analysis to actually solve each circuit. circ: the circuit instance to be simulated start: start value of the sweep source stop: stop value of the sweep source step: step value of the sweep source elem_type: string, may be 'vsource' or 'isource' elem_descr: the description of the element, used to recognize it in circ (i.e v<desc>) data_filename: string, filename of the output file. If set to stdout, prints to screen print_int_nodes: do it guess: op_analysis will guess to start the first NR iteration for the first point, the previsious dc is used from then on verbose: verbosity level Returns: A results.dc_solution instance, if a solution was found for at least one sweep value. None, if an error occurred (eg invalid start/stop/step values) or there was no solution for any sweep value. """ if data_filename == "stdout": verbose = 0 printing.print_info_line(("Starting DC analysis:", 2), verbose) (elem_type, elem_descr) = type_descr sweep_label = elem_type[0].upper() + elem_descr # check step/start/stop parameters if step == 0: printing.print_general_error("Can't sweep with step=0 !") sys.exit(1) if start > stop: printing.print_general_error("DC analysis has start > stop") sys.exit(1) if (stop - start) / step < 1: printing.print_general_error("DC analysis has number of steps < 1") sys.exit(1) if stype == options.dc_log_step: dc_iter = utilities.log_axis_iterator(stop, start, nsteps=int(stop - start) / step) elif stype == options.dc_lin_step: dc_iter = utilities.lin_axis_iterator(stop, start, nsteps=int(stop - start) / step) else: printing.print_general_error("Unknown sweep type: %s" % (stype,)) sys.exit(1) if elem_type != "vsource" and elem_type != "isource": printing.print_general_error( "Sweeping is possible only with voltage and current sources. (" + str(elem_type) + ")" ) sys.exit(1) source_elem = None for index in xrange(len(circ.elements)): if circ.elements[index].descr == elem_descr: if elem_type == "vsource": if isinstance(circ.elements[index], devices.vsource): source_elem = circ.elements[index] break if elem_type == "isource": if isinstance(circ.elements[index], devices.isource): source_elem = circ.elements[index] break if not source_elem: printing.print_general_error(elem_type + " element with descr. " + elem_descr + " was not found.") sys.exit(1) if isinstance(source_elem, devices.vsource): initial_value = source_elem.vdc else: initial_value = source_elem.idc # The initial value is set to None and this IS CORRECT. # op_analysis will attempt to do a smart guess, if called with x0 = None and guess=True # For each iteration over the source voltage (current) value, the last result is used as x0. # op_analysis will not attempt to guess the op if x0 is not None x = None sol = results.dc_solution(circ, start, stop, sweepvar=sweep_label, stype=stype, outfile=data_filename) printing.print_info_line(("Solving... ", 3), verbose, print_nl=False) tick = ticker.ticker(1) tick.display(verbose > 2) # sweep setup # tarocca il generatore di tensione, avvia DC silenziosa, ritarocca etc index = 0 for sweep_value in dc_iter: index = index + 1 if isinstance(source_elem, devices.vsource): source_elem.vdc = sweep_value else: source_elem.idc = sweep_value # silently calculate the op op = op_analysis(circ, x0=x, guess=guess, verbose=0) if op is None: tick.hide(verbose > 2) if not options.dc_sweep_skip_allowed: print "Could't solve the circuit for sweep value:", start + index * step solved = False break else: print "Skipping sweep value:", start + index * step continue solved = True sol.add_op(sweep_value, op) if guess: guess = False tick.step(verbose > 2) tick.hide(verbose > 2) if solved: printing.print_info_line(("done", 3), verbose) # clean up if isinstance(source_elem, devices.vsource): source_elem.vdc = initial_value else: source_elem.idc = initial_value return sol if solved else None
def parse_circuit(filename, read_netlist_from_stdin=False): """Parse a SPICE-like netlist and return a circuit instance that includes all components, all nodes known with that you can recreate mna and N at any time. Note that solving the circuit requires accessing to the elements in the circuit instance to evaluate non linear elements' currents. Directives are collected in a list and returned too, except for subcircuits, those are added to circuit.subckts_dict. Returns: (circuit_instance, directives) """ # Lots of differences with spice's syntax: # Support for alphanumeric node names, but the ref has to be 0. always # Do not break lines with + # .end is not required, but if is used anything following it is ignored # many others, see doc. circ = circuit.circuit(title="", filename=filename) if not read_netlist_from_stdin: ffile = open(filename, "r") else: ffile = sys.stdin file_list = [(ffile, "unknown", not read_netlist_from_stdin)] file_index = 0 directives = [] model_directives = [] postproc = [] subckts_list_temp = [] netlist_lines = [] current_subckt_temp = [] within_subckt = False line_n = 0 try: while ffile is not None: for line in ffile: line_n = line_n + 1 line = line.strip().lower() if line_n == 1: # the first line is always the title circ.title = line continue elif len(line) == 0: continue #empty line elif line[0] == "*": # comments start with * continue # directives are grouped together and evaluated after # we have the whole circuit. # subcircuits are grouped too, but processed first if line[0] == ".": line_elements = line.split() if line_elements[0] == '.subckt': if within_subckt: raise NetlistParseError, "nested subcircuit declaration detected" current_subckt_temp = current_subckt_temp + [(line, line_n)] within_subckt = True elif line_elements[0] == '.ends': if not within_subckt: raise NetlistParseError, "corresponding .subckt not found" within_subckt = False subckts_list_temp.append(current_subckt_temp) current_subckt_temp = [] elif line_elements[0] == '.include': file_list.append(parse_include_directive(line, line_elements=None)) elif line_elements[0] == ".end": break elif line_elements[0] == ".plot": postproc.append((line, line_n)) elif line_elements[0] == ".model": model_directives.append((line, line_n)) else: directives.append((line, line_n)) continue if within_subckt: current_subckt_temp = current_subckt_temp + [(line, line_n)] else: netlist_lines = netlist_lines + [(line, line_n)] if within_subckt: raise NetlistParseError, ".ends not found" file_index = file_index + 1 ffile = get_next_file_and_close_current(file_list, file_index) #print file_list except NetlistParseError, (msg,): if len(msg): printing.print_general_error(msg) printing.print_parse_error(line_n, line) #if not read_netlist_from_stdin: #ffile.close() sys.exit(45)
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
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 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 dc_solve( mna, Ndc, circ, Ntran=None, Gmin=None, x0=None, time=None, MAXIT=None, locked_nodes=None, skip_Tt=False, verbose=3 ): """Tries to perform a DC analysis of the circuit. The system we want to solve is: (mna+Gmin)*x + N + T(x) = 0 mna is the reduced mna matrix with the required KVL rows N is Ndc + Ntran T(x) will be built. circ is the circuit instance from which mna, N were built. x0 is the (optional) initial guess. If not specified, the all-zeros vector will be used This method is used ever by transient analysis. For transient analysis, time has to be set. In "real" DC analysis, time may be left to None. See circuit.py for more info about the default behaviour of time variant sources that also have a dc value. MAXIT is the maximum number of NR iteration to be supplied to mdn_solver. locked_nodes: array of tuples of nodes controlling non linear elements of the circuit. This is generated by circ.get_locked_nodes() and will be generated that way if left to none. However, if you are doing lots of simulations of the same circuit (a transient analysis), it's a good idea to generate it only once. Returns: (x, error, converged, tot_iterations) """ if MAXIT == None: MAXIT = options.dc_max_nr_iter if locked_nodes is None: locked_nodes = circ.get_locked_nodes() mna_size = mna.shape[0] nv = len(circ.nodes_dict) tot_iterations = 0 if Gmin is None: Gmin = 0 if Ntran is None: Ntran = 0 # time variable component: Tt this is always the same in each iter. So we build it once for all. Tt = numpy.mat(numpy.zeros((mna_size, 1))) v_eq = 0 if not skip_Tt: for elem in circ.elements: if (isinstance(elem, devices.vsource) or isinstance(elem, devices.isource)) and elem.is_timedependent: if isinstance(elem, devices.vsource): Tt[nv - 1 + v_eq, 0] = -1 * elem.V(time) elif isinstance(elem, devices.isource): if elem.n1: Tt[elem.n1 - 1, 0] = Tt[elem.n1 - 1, 0] + elem.I(time) if elem.n2: Tt[elem.n2 - 1, 0] = Tt[elem.n2 - 1, 0] - elem.I(time) if circuit.is_elem_voltage_defined(elem): v_eq = v_eq + 1 # update N to include the time variable sources Ndc = Ndc + Tt # initial guess, if specified, otherwise it's zero if x0 is not None: if isinstance(x0, results.op_solution): x = x0.asmatrix() else: x = x0 else: x = numpy.mat(numpy.zeros((mna_size, 1))) # has n-1 rows because of discard of ^^^ converged = False standard_solving, gmin_stepping, source_stepping = get_solve_methods() standard_solving, gmin_stepping, source_stepping = set_next_solve_method( standard_solving, gmin_stepping, source_stepping, verbose ) printing.print_info_line(("Solving... ", 3), verbose, print_nl=False) while not converged: if standard_solving["enabled"]: mna_to_pass = mna + Gmin N_to_pass = Ndc + Ntran * (Ntran is not None) elif gmin_stepping["enabled"]: # print "gmin index:", str(gmin_stepping["index"])+", gmin:", str( 10**(gmin_stepping["factors"][gmin_stepping["index"]])) printing.print_info_line( ("Setting Gmin to: " + str(10 ** gmin_stepping["factors"][gmin_stepping["index"]]), 6), verbose ) mna_to_pass = ( build_gmin_matrix(circ, 10 ** (gmin_stepping["factors"][gmin_stepping["index"]]), mna_size, verbose) + mna ) N_to_pass = Ndc + Ntran * (Ntran is not None) elif source_stepping["enabled"]: printing.print_info_line( ( "Setting sources to " + str(source_stepping["factors"][source_stepping["index"]] * 100) + "% of their actual value", 6, ), verbose, ) mna_to_pass = mna + Gmin N_to_pass = source_stepping["factors"][source_stepping["index"]] * Ndc + Ntran * (Ntran is not None) try: (x, error, converged, n_iter, convergence_by_node) = mdn_solver( x, mna_to_pass, circ, T=N_to_pass, nv=nv, print_steps=(verbose > 0), locked_nodes=locked_nodes, time=time, MAXIT=MAXIT, debug=(verbose == 6), ) tot_iterations += n_iter except numpy.linalg.linalg.LinAlgError: n_iter = 0 converged = False print "failed." printing.print_general_error("J Matrix is singular") except OverflowError: n_iter = 0 converged = False print "failed." printing.print_general_error("Overflow") if not converged: if verbose == 6: for ivalue in range(len(convergence_by_node)): if not convergence_by_node[ivalue] and ivalue < nv - 1: print "Convergence problem node %s" % (circ.int_node_to_ext(ivalue),) elif not convergence_by_node[ivalue] and ivalue >= nv - 1: e = circ.find_vde(ivalue) print "Convergence problem current in %s%s" % (e.letter_id, e.descr) if n_iter == MAXIT - 1: printing.print_general_error("Error: MAXIT exceeded (" + str(MAXIT) + ")") if more_solve_methods_available(standard_solving, gmin_stepping, source_stepping): standard_solving, gmin_stepping, source_stepping = set_next_solve_method( standard_solving, gmin_stepping, source_stepping, verbose ) else: # print "Giving up." x = None error = None break else: printing.print_info_line(("[%d iterations]" % (n_iter,), 6), verbose) if source_stepping["enabled"] and source_stepping["index"] != 9: converged = False source_stepping["index"] = source_stepping["index"] + 1 elif gmin_stepping["enabled"] and gmin_stepping["index"] != 9: gmin_stepping["index"] = gmin_stepping["index"] + 1 converged = False else: printing.print_info_line((" done.", 3), verbose) return (x, error, converged, tot_iterations)
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 dc_analysis(circ, start, stop, step, type_descr, xguess=None, data_filename="stdout", print_int_nodes=True, guess=True, stype="LINEAR", verbose=2): """Performs a sweep of the value of V or I of a independent source from start value to stop value using the provided step. For every circuit generated, computes the op and prints it out. This function relays on dc_analysis.op_analysis to actually solve each circuit. circ: the circuit instance to be simulated start: start value of the sweep source stop: stop value of the sweep source step: step value of the sweep source elem_type: string, may be 'vsource' or 'isource' elem_descr: the description of the element, used to recognize it in circ (i.e v<desc>) data_filename: string, filename of the output file. If set to stdout, prints to screen print_int_nodes: do it guess: op_analysis will guess to start the first NR iteration for the first point, the previsious dc is used from then on verbose: verbosity level Returns: A results.dc_solution instance, if a solution was found for at least one sweep value. None, if an error occurred (eg invalid start/stop/step values) or there was no solution for any sweep value. """ if data_filename == 'stdout': verbose = 0 printing.print_info_line(("Starting DC analysis:", 2), verbose) (elem_type, elem_descr) = type_descr sweep_label = elem_type[0].upper()+elem_descr #check step/start/stop parameters if step == 0: printing.print_general_error("Can't sweep with step=0 !") sys.exit(1) if start > stop: printing.print_general_error("DC analysis has start > stop") sys.exit(1) if (stop-start)/step < 1: printing.print_general_error("DC analysis has number of steps < 1") sys.exit(1) if stype == options.dc_log_step: dc_iter = utilities.log_axis_iterator(stop, start, nsteps=int(stop-start)/step) elif stype == options.dc_lin_step: dc_iter = utilities.lin_axis_iterator(stop, start, nsteps=int(stop-start)/step) else: printing.print_general_error("Unknown sweep type: %s" % (stype,)) sys.exit(1) if elem_type != 'vsource' and elem_type != 'isource': printing.print_general_error("Sweeping is possible only with voltage and current sources. (" +str(elem_type)+ ")") sys.exit(1) source_elem = None for index in xrange(len(circ.elements)): if circ.elements[index].descr == elem_descr: if elem_type == 'vsource': if isinstance(circ.elements[index], devices.vsource): source_elem = circ.elements[index] break if elem_type == 'isource': if isinstance(circ.elements[index], devices.isource): source_elem = circ.elements[index] break if not source_elem: printing.print_general_error(elem_type + " element with descr. "+ elem_descr +" was not found.") sys.exit(1) if isinstance(source_elem, devices.vsource): initial_value = source_elem.vdc else: initial_value = source_elem.idc # The initial value is set to None and this IS CORRECT. # op_analysis will attempt to do a smart guess, if called with x0 = None and guess=True # For each iteration over the source voltage (current) value, the last result is used as x0. # op_analysis will not attempt to guess the op if x0 is not None x = None sol = results.dc_solution(circ, start, stop, sweepvar=sweep_label, stype=stype, outfile=data_filename) printing.print_info_line(("Solving... ", 3), verbose, print_nl=False) tick = ticker.ticker(1) tick.display(verbose>2) #sweep setup #tarocca il generatore di tensione, avvia DC silenziosa, ritarocca etc index = 0 for sweep_value in dc_iter: index = index + 1 if isinstance(source_elem, devices.vsource): source_elem.vdc = sweep_value else: source_elem.idc = sweep_value #silently calculate the op op = op_analysis(circ, x0=x, guess=guess, verbose=0) if op is None: tick.hide(verbose>2) if not options.dc_sweep_skip_allowed: print "Could't solve the circuit for sweep value:", start + index*step solved = False break else: print "Skipping sweep value:", start + index*step continue solved = True sol.add_op(sweep_value, op) if guess: guess = False tick.step(verbose>2) tick.hide(verbose>2) if solved: printing.print_info_line(("done", 3), verbose) # clean up if isinstance(source_elem, devices.vsource): source_elem.vdc = initial_value else: source_elem.idc = initial_value return sol if solved else None