def build_x0_from_user_supplied_ic(circ, voltages_dict, currents_dict): """Builds a numpy.matrix of appropriate size (reduced!) from the values supplied in voltages_dict and currents_dict. What is not found in the dictionary is set to 0. Parameters: circ: the circuit instance voltages_dict: keys are the external nodes, values are the node voltages. currents_dict: keys are the elements names (eg l1, v4), the values are the currents Note: this simulator uses the normal convention. Returns: The x0 matrix """ nv = len(circ.nodes_dict) #number of voltage variables voltage_defined_elements = [ x for x in circ.elements if circuit.is_elem_voltage_defined(x) ] ni = len(voltage_defined_elements) #number of current variables current_labels_list = [ elem.letter_id + elem.descr for elem in voltage_defined_elements ] x0 = numpy.mat(numpy.zeros((nv + ni, 1))) for ext_node, value in voltages_dict.iteritems(): int_node = circ.ext_node_to_int(ext_node) x0[int_node, 0] = value for current_label, value in currents_dict.iteritems(): index = current_labels_list.index(current_label) x0[nv + index, 0] = value return x0[1:, :]
def build_x0_from_user_supplied_ic(circ, voltages_dict, currents_dict): """Builds a numpy.matrix of appropriate size (reduced!) from the values supplied in voltages_dict and currents_dict. What is not found in the dictionary is set to 0. Parameters: circ: the circuit instance voltages_dict: keys are the external nodes, values are the node voltages. currents_dict: keys are the elements names (eg l1, v4), the values are the currents Note: this simulator uses the normal convention. Returns: The x0 matrix """ nv = len(circ.nodes_dict) # number of voltage variables voltage_defined_elements = [x for x in circ.elements if circuit.is_elem_voltage_defined(x)] ni = len(voltage_defined_elements) # number of current variables current_labels_list = [elem.letter_id + elem.descr for elem in voltage_defined_elements] x0 = numpy.mat(numpy.zeros((nv + ni, 1))) for ext_node, value in voltages_dict.iteritems(): int_node = circ.ext_node_to_int(ext_node) x0[int_node, 0] = value for current_label, value in currents_dict.iteritems(): index = current_labels_list.index(current_label) x0[nv + index, 0] = value return x0[1:, :]
def build_Tass_static_vector(circ, Tf, points, step, tick, n_of_var, verbose=3): Tass_vector = [] nv = len(circ.nodes_dict) printing.print_info_line(("Building Tass...", 5), verbose, print_nl=False) tick.reset() tick.display(verbose > 2) for index in xrange(0, points): Tt = numpy.zeros((n_of_var, 1)) v_eq = 0 time = index * step 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.0 * 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 tick.step(verbose > 2) Tass_vector.append(Tf + Tt) tick.hide(verbose > 2) printing.print_info_line(("done.", 5), verbose) return Tass_vector
def __init__(self, circ, start, stop, sweepvar, stype, outfile): """Holds a set of DC results. circ: the circuit instance of the simulated circuit start: the sweep start value stop: the sweep stop value sweepvar: the swept variable stype: type of sweep outfile: the file to write the results to. (Use "stdout" to write to std output) """ solution.__init__(self, circ, outfile) self.start, self.stop = start, stop self.stype = stype nv_1 = len(circ.nodes_dict ) - 1 # numero di soluzioni di tensione (al netto del ref) self.variables = [sweepvar] self.units = case_insensitive_dict() if self.variables[0][0] == 'V': self.units.update({self.variables[0]: 'V'}) if self.variables[0][0] == 'I': self.units.update({self.variables[0]: 'A'}) for index in range(nv_1): varname = "V%s" % (str(circ.nodes_dict[index + 1]), ) self.variables += [varname] self.units.update({varname: "V"}) if circ.is_int_node_internal_only(index + 1): self.skip_nodes_list.append(index) for elem in circ: if circuit.is_elem_voltage_defined(elem): varname = "I(%s)" % (elem.part_id.upper(), ) self.variables += [varname] self.units.update({varname: "A"})
def __init__(self, circ, tstart, tstop, op, method, outfile): """Holds a set of TRANSIENT results. circ: the circuit instance of the simulated circuit tstart: the sweep starting angular frequency tstop: the sweep stopping frequency op: the op used to start the tran analysis outfile: the file to write the results to. (Use "stdout" to write to std output) """ solution.__init__(self, circ, outfile) self.start_op = op self.tstart, self.tstop = tstart, tstop self.method = method self._lock = False nv_1 = len(circ.nodes_dict ) - 1 # numero di soluzioni di tensione (al netto del ref) self.variables = ["T"] self.units.update({"T": "s"}) for index in range(nv_1): varname = ("V%s" % (str(circ.nodes_dict[index + 1]), )).upper() self.variables += [varname] self.units.update({varname: "V"}) if circ.is_int_node_internal_only(index + 1): self.skip_nodes_list.append(index) for elem in circ: if circuit.is_elem_voltage_defined(elem): varname = ("I(%s)" % (elem.part_id.upper(), )).upper() self.variables += [varname] self.units.update({varname: "A"})
def modify_x0_for_ic(circ, x0): """Modifies a supplied x0. """ if isinstance(x0, results.op_solution): x0 = x0.asmatrix() return_obj = True else: return_obj = False nv = len(circ.nodes_dict) # number of voltage variables voltage_defined_elements = [x for x in circ.elements if circuit.is_elem_voltage_defined(x)] # setup voltages this may _not_ work properly for elem in circ.elements: if isinstance(elem, devices.capacitor) and elem.ic or isinstance(elem, devices.diode) and elem.ic: x0[elem.n1 - 1, 0] = x0[elem.n2 - 1, 0] + elem.ic # setup the currents for elem in voltage_defined_elements: if isinstance(elem, devices.inductor) and elem.ic: x0[nv - 1 + voltage_defined_elements.index(elem), 0] = elem.ic if return_obj: xnew = results.op_solution(x=x0, error=numpy.mat(numpy.zeros(x0.shape)), circ=circ, outfile=None) xnew.netlist_file = None xnew.netlist_title = "Self-generated OP to be used as tran IC" else: xnew = x0 return xnew
def build_Tt(circ, points, step, tick, n_of_var, verbose=3): nv = len(circ.nodes_dict) printing.print_info_line(("Building Tt...", 5), verbose, print_nl=False) tick.reset() tick.display(verbose > 2) Tt = numpy.zeros((points * n_of_var, 1)) for index in xrange(1, points): v_eq = 0 time = index * step for elem in circ: if (isinstance(elem, devices.VSource) or isinstance( elem, devices.ISource)) and elem.is_timedependent: if isinstance(elem, devices.VSource): Tt[index * n_of_var + nv - 1 + v_eq, 0] = - \ 1.0 * elem.V(time) elif isinstance(elem, devices.ISource): if elem.n1: Tt[index * n_of_var + elem.n1 - 1, 0] = \ Tt[index * n_of_var + elem.n1 - 1, 0] + \ elem.I(time) if elem.n2: Tt[index * n_of_var + elem.n2 - 1, 0] = \ Tt[index * n_of_var + elem.n2 - 1, 0] - \ elem.I(time) if circuit.is_elem_voltage_defined(elem): v_eq = v_eq + 1 # print Tt[index*n_of_var:(index+1)*n_of_var] tick.step(verbose > 2) tick.hide(verbose > 2) printing.print_info_line(("done.", 5), verbose) return Tt
def modify_x0_for_ic(circ, x0): """Modifies a supplied x0. """ if isinstance(x0, results.op_solution): x0 = x0.asmatrix() return_obj = True else: return_obj = False nv = len(circ.nodes_dict) # number of voltage variables voltage_defined_elements = [ x for x in circ if circuit.is_elem_voltage_defined(x) ] # setup voltages this may _not_ work properly for elem in circ: if isinstance(elem, devices.Capacitor) and elem.ic or \ isinstance(elem, diode.diode) and elem.ic: x0[elem.n1 - 1, 0] = x0[elem.n2 - 1, 0] + elem.ic # setup the currents for elem in voltage_defined_elements: if isinstance(elem, devices.Inductor) and elem.ic: x0[nv - 1 + voltage_defined_elements.index(elem), 0] = elem.ic if return_obj: xnew = results.op_solution(x=x0, \ error=numpy.mat(numpy.zeros(x0.shape)), circ=circ, outfile=None) xnew.netlist_file = None xnew.netlist_title = "Self-generated OP to be used as tran IC" else: xnew = x0 return xnew
def __init__(self, circ, method, period, outfile, t_array=None, x_array=None): """Holds a set of TRANSIENT results. circ: the circuit instance of the simulated circuit tstart: the sweep starting angular frequency tstop: the sweep stopping frequency op: the op used to start the tran analysis outfile: the file to write the results to. (Use "stdout" to write to std output) """ solution.__init__(self, circ, outfile) self.period = period self.method = method # We have mixed current and voltage results nv_1 = len(circ.nodes_dict) - 1 # numero di soluzioni di tensione (al netto del ref) self.variables = ["T"] self.units.update({"T":"s"}) for index in range(nv_1): varname = "V%s" % (str(circ.nodes_dict[index + 1]),) self.variables += [varname] self.units.update({varname:"V"}) if circ.is_int_node_internal_only(index+1): self.skip_nodes_list.append(index) for elem in circ: if circuit.is_elem_voltage_defined(elem): varname = "I(%s)" % (elem.part_id.upper(),) self.variables += [varname] self.units.update({varname:"A"}) if t_array is not None and x_array is not None: self.set_results(t_array, x_array)
def __init__(self, circ, tstart, tstop, op, method, outfile): """Holds a set of TRANSIENT results. circ: the circuit instance of the simulated circuit tstart: the sweep starting angular frequency tstop: the sweep stopping frequency op: the op used to start the tran analysis outfile: the file to write the results to. (Use "stdout" to write to std output) """ solution.__init__(self, circ, outfile) self.start_op = op self.tstart, self.tstop = tstart, tstop self.method = method self._lock = False nv_1 = len(circ.nodes_dict) - 1 # numero di soluzioni di tensione (al netto del ref) self.variables = ["T"] self.units.update({"T":"s"}) for index in range(nv_1): varname = ("V%s" % (str(circ.nodes_dict[index + 1]),)).upper() self.variables += [varname] self.units.update({varname:"V"}) if circ.is_int_node_internal_only(index+1): self.skip_nodes_list.append(index) for elem in circ: if circuit.is_elem_voltage_defined(elem): varname = ("I(%s)" % (elem.part_id.upper(),)).upper() self.variables += [varname] self.units.update({varname:"A"})
def __init__(self, circ, start, stop, sweepvar, stype, outfile): """Holds a set of DC results. circ: the circuit instance of the simulated circuit start: the sweep start value stop: the sweep stop value sweepvar: the swept variable stype: type of sweep outfile: the file to write the results to. (Use "stdout" to write to std output) """ solution.__init__(self, circ, outfile) self.start, self.stop = start, stop self.stype = stype nv_1 = len(circ.nodes_dict) - 1 # numero di soluzioni di tensione (al netto del ref) self.variables = [sweepvar] self.units = case_insensitive_dict() if self.variables[0][0] == 'V': self.units.update({self.variables[0]:'V'}) if self.variables[0][0] == 'I': self.units.update({self.variables[0]:'A'}) for index in range(nv_1): varname = "V%s" % (str(circ.nodes_dict[index + 1]),) self.variables += [varname] self.units.update({varname:"V"}) if circ.is_int_node_internal_only(index+1): self.skip_nodes_list.append(index) for elem in circ: if circuit.is_elem_voltage_defined(elem): varname = "I(%s)" % (elem.part_id.upper(),) self.variables += [varname] self.units.update({varname:"A"})
def __init__(self, circ, ostart, ostop, opoints, stype, op, outfile): """Holds a set of AC results. circ: the circuit instance of the simulated circuit ostart: the sweep starting angular frequency ostop: the sweep stopping frequency stype: the sweep type op: the linearization op used to compute the results """ solution.__init__(self, circ, outfile) self.linearization_op = op self.stype = stype self.ostart, self.ostop, self.opoints = ostart, ostop, opoints nv_1 = len(circ.nodes_dict) - 1 # numero di soluzioni di tensione (al netto del ref) self.variables += ["w"] self.units.update({"w": "rad/s"}) for index in range(nv_1): varname_abs = "|V%s|" % (str(circ.nodes_dict[index + 1]),) varname_arg = "arg(V%s)" % (str(circ.nodes_dict[index + 1]),) self.variables += [varname_abs] self.variables += [varname_arg] self.units.update({varname_abs: "V"}) self.units.update({varname_arg: ""}) if circ.is_int_node_internal_only(index+1): self.skip_nodes_list.append(index) for elem in circ: if circuit.is_elem_voltage_defined(elem): varname_abs = "|I(%s)|" % (elem.part_id.upper(),) varname_arg = "arg(I(%s))" % (elem.part_id.upper(),) self.variables += [varname_abs] self.variables += [varname_arg] self.units.update({varname_abs: "A"}) self.units.update({varname_arg: ""})
def build_Tt(circ, points, step, tick, n_of_var, verbose=3): nv = len(circ.nodes_dict) printing.print_info_line(("Building Tt...", 5), verbose, print_nl=False) tick.reset() tick.display(verbose > 2) Tt = numpy.zeros((points*n_of_var, 1)) for index in xrange(1, points): v_eq = 0 time = index * step 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[index*n_of_var + nv - 1 + v_eq, 0] = -1.0 * elem.V(time) elif isinstance(elem, devices.isource): if elem.n1: Tt[index*n_of_var + elem.n1-1, 0] = \ Tt[index*n_of_var + elem.n1-1, 0] + elem.I(time) if elem.n2: Tt[index*n_of_var + elem.n2-1, 0] = \ Tt[index*n_of_var + elem.n2-1, 0] - elem.I(time) if circuit.is_elem_voltage_defined(elem): v_eq = v_eq +1 #print Tt[index*n_of_var:(index+1)*n_of_var] tick.step(verbose > 2) tick.hide(verbose > 2) printing.print_info_line(("done.", 5), verbose) return Tt
def print_elements_ops(circ, x): tot_power = 0 i_index = 0 nv_1 = len(circ.nodes_dict) - 1 print "OP INFORMATION:" for elem in circ: ports_v_v = [] if hasattr(elem, "print_op_info"): if elem.is_nonlinear: oports = elem.get_output_ports() for index in range(len(oports)): dports = elem.get_drive_ports(index) ports_v = [] for port in dports: tempv = 0 if port[0]: tempv = x[port[0] - 1] if port[1]: tempv = tempv - x[port[1] - 1] ports_v.append(tempv) ports_v_v.append(ports_v) else: port = (elem.n1, elem.n2) tempv = 0 if port[0]: tempv = x[port[0] - 1] if port[1]: tempv = tempv - x[port[1] - 1] ports_v_v = ((tempv, ), ) elem.print_op_info(ports_v_v) print "-------------------" if isinstance(elem, devices.GISource): v = 0 v = v + x[elem.n1 - 1] if elem.n1 != 0 else v v = v - x[elem.n2 - 1] if elem.n2 != 0 else v vs = 0 vs = vs + x[elem.n1 - 1] if elem.n1 != 0 else vs vs = vs - x[elem.n2 - 1] if elem.n2 != 0 else vs tot_power = tot_power - v * vs * elem.alpha elif isinstance(elem, devices.ISource): v = 0 v = v + x[elem.n1 - 1] if elem.n1 != 0 else v v = v - x[elem.n2 - 1] if elem.n2 != 0 else v tot_power = tot_power - v * elem.I() elif isinstance(elem, devices.VSource) or isinstance( elem, devices.EVSource): v = 0 v = v + x[elem.n1 - 1] if elem.n1 != 0 else v v = v - x[elem.n2 - 1] if elem.n2 != 0 else v tot_power = tot_power - v * x[nv_1 + i_index, 0] i_index = i_index + 1 elif circuit.is_elem_voltage_defined(elem): i_index = i_index + 1 print "TOTAL POWER: " + str(float(tot_power)) + " W" return None
def get_elements_op(self, circ, x): tot_power = 0 i_index = 0 nv_1 = len(circ.nodes_dict) - 1 op_info = [] for elem in circ: ports_v_v = [] if hasattr(elem, "get_op_info"): if elem.is_nonlinear: oports = elem.get_output_ports() for index in range(len(oports)): dports = elem.get_drive_ports(index) ports_v = [] for port in dports: tempv = 0 if port[0]: tempv = x[port[0] - 1] if port[1]: tempv = tempv - x[port[1] - 1] ports_v.append(tempv) ports_v_v.append(ports_v) else: port = (elem.n1, elem.n2) tempv = 0 if port[0]: tempv = x[port[0] - 1] if port[1]: tempv = tempv - x[port[1] - 1] ports_v_v = ((tempv, ), ) op_info += [elem.get_op_info(ports_v_v)] if isinstance(elem, devices.GISource): v = 0 v = v + x[elem.n1 - 1] if elem.n1 != 0 else v v = v - x[elem.n2 - 1] if elem.n2 != 0 else v vs = 0 vs = vs + x[elem.n1 - 1] if elem.n1 != 0 else vs vs = vs - x[elem.n2 - 1] if elem.n2 != 0 else vs tot_power = tot_power - v * vs * elem.alpha elif isinstance(elem, devices.ISource): v = 0 v = v + x[elem.n1 - 1] if elem.n1 != 0 else v v = v - x[elem.n2 - 1] if elem.n2 != 0 else v tot_power = tot_power - v * elem.I() elif isinstance(elem, devices.VSource) or \ isinstance(elem, devices.EVSource): v = 0 v = v + x[elem.n1 - 1] if elem.n1 != 0 else v v = v - x[elem.n2 - 1] if elem.n2 != 0 else v tot_power = tot_power - v * x[nv_1 + i_index, 0] i_index = i_index + 1 elif circuit.is_elem_voltage_defined(elem): i_index = i_index + 1 op_info.append("TOTAL POWER: %e W\n" % (tot_power, )) return op_info
def print_elements_ops(circ, x): tot_power = 0 i_index = 0 nv_1 = len(circ.nodes_dict) - 1 print "OP INFORMATION:" for elem in circ.elements: ports_v_v = [] if hasattr(elem, "print_op_info"): if elem.is_nonlinear: oports = elem.get_output_ports() for index in range(len(oports)): dports = elem.get_drive_ports(index) ports_v = [] for port in dports: tempv = 0 if port[0]: tempv = x[port[0] - 1] if port[1]: tempv = tempv - x[port[1] - 1] ports_v.append(tempv) ports_v_v.append(ports_v) else: port = (elem.n1, elem.n2) tempv = 0 if port[0]: tempv = x[port[0] - 1] if port[1]: tempv = tempv - x[port[1] - 1] ports_v_v = ((tempv,),) elem.print_op_info(ports_v_v) print "-------------------" if isinstance(elem, devices.gisource): v = 0 v = v + x[elem.n1 - 1] if elem.n1 != 0 else v v = v - x[elem.n2 - 1] if elem.n2 != 0 else v vs = 0 vs = vs + x[elem.n1 - 1] if elem.n1 != 0 else vs vs = vs - x[elem.n2 - 1] if elem.n2 != 0 else vs tot_power = tot_power - v * vs * elem.alpha elif isinstance(elem, devices.isource): v = 0 v = v + x[elem.n1 - 1] if elem.n1 != 0 else v v = v - x[elem.n2 - 1] if elem.n2 != 0 else v tot_power = tot_power - v * elem.I() elif isinstance(elem, devices.vsource) or isinstance(elem, devices.evsource): v = 0 v = v + x[elem.n1 - 1] if elem.n1 != 0 else v v = v - x[elem.n2 - 1] if elem.n2 != 0 else v tot_power = tot_power - v * x[nv_1 + i_index, 0] i_index = i_index + 1 elif circuit.is_elem_voltage_defined(elem): i_index = i_index + 1 print "TOTAL POWER: " + str(float(tot_power)) + " W" return None
def get_elements_op(self, circ, x): tot_power = 0 i_index = 0 nv_1 = len(circ.nodes_dict) - 1 op_info = [] for elem in circ: ports_v_v = [] if hasattr(elem, "get_op_info"): if elem.is_nonlinear: oports = elem.get_output_ports() for index in range(len(oports)): dports = elem.get_drive_ports(index) ports_v = [] for port in dports: tempv = 0 if port[0]: tempv = x[port[0]-1] if port[1]: tempv = tempv - x[port[1]-1] ports_v.append(tempv) ports_v_v.append(ports_v) else: port = (elem.n1, elem.n2) tempv = 0 if port[0]: tempv = x[port[0]-1] if port[1]: tempv = tempv - x[port[1]-1] ports_v_v = ((tempv,),) op_info += [elem.get_op_info(ports_v_v)] if isinstance(elem, devices.GISource): v = 0 v = v + x[elem.n1-1] if elem.n1 != 0 else v v = v - x[elem.n2-1] if elem.n2 != 0 else v vs = 0 vs = vs + x[elem.n1-1] if elem.n1 != 0 else vs vs = vs - x[elem.n2-1] if elem.n2 != 0 else vs tot_power = tot_power - v*vs*elem.alpha elif isinstance(elem, devices.ISource): v = 0 v = v + x[elem.n1-1] if elem.n1 != 0 else v v = v - x[elem.n2-1] if elem.n2 != 0 else v tot_power = tot_power - v*elem.I() elif isinstance(elem, devices.VSource) or \ isinstance(elem, devices.EVSource): v = 0 v = v + x[elem.n1-1] if elem.n1 != 0 else v v = v - x[elem.n2-1] if elem.n2 != 0 else v tot_power = tot_power - v*x[nv_1 + i_index, 0] i_index = i_index + 1 elif circuit.is_elem_voltage_defined(elem): i_index = i_index + 1 op_info.append("TOTAL POWER: %e W\n" % (tot_power,)) return op_info
def print_results_header(circ, fp, print_int_nodes=False, print_time=False, print_omega=False): """Prints the header of the results. circ, a circuit instance fp, the file pointer to which the header should be written print_int_nodes=False, Print internal nodes print_time=False, Print the time (it's always the first column) Returns: None """ voltage_labels = [] for n in range(1, len(circ.nodes_dict)): if (print_int_nodes or not circ.is_int_node_internal_only(n)): if print_omega == False: iter_voltage_labels = ["V" + circ.nodes_dict[n]] else: iter_voltage_labels = [ "|V" + circ.nodes_dict[n] + "|", "arg(V" + circ.nodes_dict[n] + ")" ] voltage_labels = voltage_labels + iter_voltage_labels current_labels = [] for elem in circ.elements: if circuit.is_elem_voltage_defined(elem): if print_omega == False: iter_current_labels = [ "I(" + elem.letter_id.upper() + elem.descr + ")" ] else: iter_current_labels = [ "|I(" + elem.letter_id.upper() + elem.descr + ")|", "arg(I(" + elem.letter_id.upper() + elem.descr + "))" ] current_labels = current_labels + iter_current_labels labels = voltage_labels + current_labels if print_time: labels.insert(0, "#T") elif print_omega: labels.insert(0, "#w") else: labels[0] = "#" + labels[0] for lab in labels: fp.write(lab + "\t") fp.write("\n") fp.flush() return None
def __init__(self, circ, ostart, ostop, opoints, stype, op, outfile): """Holds a set of AC results. circ: the circuit instance of the simulated circuit ostart: the sweep starting angular frequency ostop: the sweep stopping frequency stype: the sweep type op: the linearization op used to compute the results """ self.timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()) self.netlist_file = circ.filename self.netlist_title = circ.title self.vea = options.vea self.ver = options.ver self.iea = options.iea self.ier = options.ier self.gmin = options.gmin self.temp = constants.T self.linearization_op = op self.stype = stype self.ostart, self.ostop, self.opoints = ostart, ostop, opoints self.filename = outfile self._init_file_done = False #We have mixed current and voltage results # per primi vengono tanti valori di tensioni quanti sono i nodi del circuito meno uno, # quindi tante correnti quanti sono gli elementi definiti in tensione presenti # (per questo, per misurare una corrente, si può fare uso di generatori di tensione da 0V) nv_1 = len(circ.nodes_dict) - 1 # numero di soluzioni di tensione (al netto del ref) self.skip_nodes_list = [] # nodi da saltare, solo interni self.variables = ["w"] self.units = case_insensitive_dict() self.units.update({"w":"rad/s"}) for index in range(nv_1): varname_abs = "|V%s|" % (str(circ.nodes_dict[index + 1]),) varname_arg = "arg(V%s)" % (str(circ.nodes_dict[index + 1]),) self.variables += [varname_abs] self.variables += [varname_arg] self.units.update({varname_abs:"V"}) self.units.update({varname_arg:""}) if circ.is_int_node_internal_only(index+1): skip_nodes_list.append(index) for elem in circ.elements: if circuit.is_elem_voltage_defined(elem): varname_abs = "|I(%s)|" % (elem.letter_id.upper()+elem.descr,) varname_arg = "arg(I(%s))" % (elem.letter_id.upper()+elem.descr,) self.variables += [varname_abs] self.variables += [varname_arg] self.units.update({varname_abs:"A"}) self.units.update({varname_arg:""})
def __init__(self, x, error, circ, outfile, iterations=0): """Holds a set of Operating Point results. x: the result set error: the residual error after solution, circ: the circuit instance of the simulated circuit print_int_nodes: a boolean to be set True if you wish to see voltage values of the internal nodes added automatically by the simulator. """ self.timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()) self.netlist_file = circ.filename self.netlist_title = circ.title self.vea = options.vea self.ver = options.ver self.iea = options.iea self.ier = options.ier self.gmin = options.gmin self.temp = constants.T # in Kelvin self.filename = outfile self.iterations = iterations #We have mixed current and voltage results # per primi vengono tanti valori di tensioni quanti sono i nodi del circuito meno uno, # quindi tante correnti quanti sono gli elementi definiti in tensione presenti # (per questo, per misurare una corrente, si può fare uso di generatori di tensione da 0V) nv_1 = len(circ.nodes_dict) - 1 # numero di soluzioni di tensione (al netto del ref) self.skip_nodes_list = [] # nodi da saltare, solo interni self.variables = [] self.results = case_insensitive_dict() self.errors = case_insensitive_dict() self.units = case_insensitive_dict() self.x = x for index in range(nv_1): varname = ("V" + str(circ.nodes_dict[index + 1])).upper() self.variables += [varname] self.results.update({varname:x[index, 0]}) self.errors.update({varname:error[index, 0]}) self.units.update({varname:"V"}) #if circ.is_int_node_internal_only(index+1): # self.skip_nodes_list.append(index) for elem in circ.elements: if circuit.is_elem_voltage_defined(elem): index = index + 1 varname = ("I("+elem.letter_id.upper()+elem.descr+")").upper() self.variables += [varname] self.results.update({varname:x[index, 0]}) self.errors.update({varname:error[index, 0]}) self.units.update({varname:"A"}) self.op_info = self.get_elements_op(circ, x)
def __init__(self, circ, start, stop, sweepvar, stype, outfile): """Holds a set of DC results. circ: the circuit instance of the simulated circuit start: the sweep start value stop: the sweep stop value sweepvar: the swept variable stype: type of sweep outfile: the file to write the results to. (Use "stdout" to write to std output) """ self.timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()) self.netlist_file = circ.filename self.netlist_title = circ.title self.vea = options.vea self.ver = options.ver self.iea = options.iea self.ier = options.ier self.gmin = options.gmin self.temp = constants.T self.start, self.stop = start, stop self.stype = stype self.filename = outfile self._init_file_done = False #We have mixed current and voltage results # per primi vengono tanti valori di tensioni quanti sono i nodi del circuito meno uno, # quindi tante correnti quanti sono gli elementi definiti in tensione presenti # (per questo, per misurare una corrente, si può fare uso di generatori di tensione da 0V) nv_1 = len(circ.nodes_dict) - 1 # numero di soluzioni di tensione (al netto del ref) self.skip_nodes_list = [] # nodi da saltare, solo interni self.variables = [sweepvar] self.units = case_insensitive_dict() if self.variables[0][0] == 'V': self.units.update({self.variables[0]:'V'}) if self.variables[0][0] == 'I': self.units.update({self.variables[0]:'A'}) for index in range(nv_1): varname = "V%s" % (str(circ.nodes_dict[index + 1]),) self.variables += [varname] self.units.update({varname:"V"}) if circ.is_int_node_internal_only(index+1): skip_nodes_list.append(index) for elem in circ.elements: if circuit.is_elem_voltage_defined(elem): varname = "I(%s)" % (elem.letter_id.upper()+elem.descr,) self.variables += [varname] self.units.update({varname:"A"})
def print_dc_results(x, error, circ, print_int_nodes=False, print_error=True): """Prints out a set of DC results. x: the result set error: the residual error after solution, circ: the circuit instance of the simulated circuit print_int_nodes: a boolean to be set True if you wish to see voltage values of the internal nodes added automatically by the simulator. Returns: None """ #We have mixed current and voltage results # per primi vengono tanti valori di tensioni quanti sono i nodi del circuito meno uno, # quindi tante correnti quanti sono gli elementi definiti in tensione presenti # (per questo, per misurare una corrente, si può fare uso di generatori di tensione da 0V) nv_1 = len(circ.nodes_dict ) - 1 # numero di soluzioni di tensione (al netto del ref) skip_nodes_list = [] # nodi da saltare, solo interni # descrizioni dei componenti non definibili in tensione idescr = [ (elem.letter_id.upper() + elem.descr) \ for elem in circ.elements if circuit.is_elem_voltage_defined(elem) ] #cleaner ?? print_array = [] #print "Solution:" for index in xrange(x.shape[0]): if index < nv_1: if print_int_nodes or not circ.is_int_node_internal_only(index + 1): line_array = [ "V" + str(circ.nodes_dict[index + 1]) + ":", str(x[index, 0]), "V" ] if print_error: line_array.append("(" + str(float(error[index])) + ")") else: skip_nodes_list.append(index) else: line_array = [ "I(" + idescr[index - nv_1] + "):", str(x[index, 0]), "A" ] if print_error: line_array.append("(" + str(float(error[index])) + ")") print_array.append(line_array) table_print(print_array) return None
def __init__(self, circ, tstart, tstop, op, method, outfile): """Holds a set of TRANSIENT results. circ: the circuit instance of the simulated circuit tstart: the sweep starting angular frequency tstop: the sweep stopping frequency op: the op used to start the tran analysis outfile: the file to write the results to. (Use "stdout" to write to std output) """ self.timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()) self.netlist_file = circ.filename self.netlist_title = circ.title self.vea = options.vea self.ver = options.ver self.iea = options.iea self.ier = options.ier self.gmin = options.gmin self.temp = constants.T self.start_op = op self.tstart, self.tstop = tstart, tstop self.filename = outfile self.method = method self._init_file_done = False self._lock = False #We have mixed current and voltage results # per primi vengono tanti valori di tensioni quanti sono i nodi del circuito meno uno, # quindi tante correnti quanti sono gli elementi definiti in tensione presenti # (per questo, per misurare una corrente, si può fare uso di generatori di tensione da 0V) nv_1 = len(circ.nodes_dict) - 1 # numero di soluzioni di tensione (al netto del ref) self.skip_nodes_list = [] # nodi da saltare, solo interni self.variables = ["T"] self.units = case_insensitive_dict() self.units.update({"T":"s"}) for index in range(nv_1): varname = ("V%s" % (str(circ.nodes_dict[index + 1]),)).upper() self.variables += [varname] self.units.update({varname:"V"}) if circ.is_int_node_internal_only(index+1): skip_nodes_list.append(index) for elem in circ.elements: if circuit.is_elem_voltage_defined(elem): varname = ("I(%s)" % (elem.letter_id.upper()+elem.descr,)).upper() self.variables += [varname] self.units.update({varname:"A"})
def __init__(self, circ, nkeys, outfile): """Holds a set of MONTE CARLO results. circ: the circuit instance of the simulated circuit nkeys: number of random number needed for each point outfile: the file to write the results to. (Use "stdout" to write to std output) """ self.timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime()) self.netlist_file = circ.filename self.netlist_title = circ.title self.vea = options.vea self.ver = options.ver self.iea = options.iea self.ier = options.ier self.gmin = options.gmin self.temp = constants.T self.nkeys = nkeys self.filename = outfile #We have mixed current and voltage results # per primi vengono tanti valori di tensioni quanti sono i nodi del circuito meno uno, # quindi tante correnti quanti sono gli elementi definiti in tensione presenti # (per questo, per misurare una corrente, si può fare uso di generatori di tensione da 0V) self.variables = [] nv_1 = len(circ.nodes_dict) - 1 # numero di soluzioni di tensione (al netto del ref) self.skip_nodes_list = [] # nodi da saltare, solo interni self.units = case_insensitive_dict() for index in range(nv_1): varname = "V%s" % (str(circ.nodes_dict[index + 1]),) self.variables += [varname] self.units.update({varname:"V"}) if circ.is_int_node_internal_only(index+1): skip_nodes_list.append(index) for elem in circ.elements: if circuit.is_elem_voltage_defined(elem): varname = "I(%s)" % (elem.letter_id.upper()+elem.descr,) self.variables += [varname] self.units.update({varname:"A"}) # the keys get stored along with the simulation results for i in range(self.nkeys): varname = "MCKEY%d" % (i,) self.variables += [varname] self.units.update({varname:""}) csvlib.write_headers(self.filename, copy.copy(self.variables))
def print_results_header(circ, fp, print_int_nodes=False, print_time=False, print_omega=False): """Prints the header of the results. circ, a circuit instance fp, the file pointer to which the header should be written print_int_nodes=False, Print internal nodes print_time=False, Print the time (it's always the first column) Returns: None """ voltage_labels = [] for n in range(1, len(circ.nodes_dict)): if print_int_nodes or not circ.is_int_node_internal_only(n): if print_omega == False: iter_voltage_labels = ["V" + circ.nodes_dict[n]] else: iter_voltage_labels = ["|V" + circ.nodes_dict[n] + "|", "arg(V" + circ.nodes_dict[n] + ")"] voltage_labels = voltage_labels + iter_voltage_labels current_labels = [] for elem in circ.elements: if circuit.is_elem_voltage_defined(elem): if print_omega == False: iter_current_labels = ["I(" + elem.letter_id.upper() + elem.descr + ")"] else: iter_current_labels = [ "|I(" + elem.letter_id.upper() + elem.descr + ")|", "arg(I(" + elem.letter_id.upper() + elem.descr + "))", ] current_labels = current_labels + iter_current_labels labels = voltage_labels + current_labels if print_time: labels.insert(0, "#T") elif print_omega: labels.insert(0, "#w") else: labels[0] = "#" + labels[0] for lab in labels: fp.write(lab + "\t") fp.write("\n") fp.flush() return None
def build_x0_from_user_supplied_ic(circ, icdict): """Builds a numpy.matrix of appropriate size (reduced!) from the values supplied in voltages_dict and currents_dict. Supplying a custom x0 can be useful: - To aid convergence in tough circuits. - To start a transient simulation from a particular x0 Parameters: circ: the circuit instance icdict: a dictionary assembled as follows: - to specify a nodal voltage: {'V(node)':<voltage value>} Eg {'V(n1)':2.3, 'V(n2)':0.45, ...} All unspecified voltages default to 0. - to specify a branch current: 'I(<element>)':<voltage value>} ie. the elements names are sorrounded by I(...). Eg. {'I(L1)':1.03e-3, I(V4):2.3e-6, ...} All unspecified currents default to 0. Notes: this simulator uses the normal convention. Returns: The x0 matrix assembled according to icdict """ Vregex = re.compile("V\s*\(\s*(\w?)\s*\)", re.IGNORECASE | re.DOTALL) Iregex = re.compile("I\s*\(\s*(\w?)\s*\)", re.IGNORECASE | re.DOTALL) nv = len(circ.nodes_dict) # number of voltage variables voltage_defined_elem_names = \ [elem.part_id for elem in circ if circuit.is_elem_voltage_defined( elem)] voltage_defined_elem_names = map(str.lower, voltage_defined_elem_names) ni = len(voltage_defined_elem_names) # number of current variables x0 = numpy.mat(numpy.zeros((nv + ni, 1))) for label, value in icdict.iteritems(): if Vregex.search(label): ext_node = Vregex.findall(label)[0] int_node = circ.ext_node_to_int(ext_node) x0[int_node, 0] = value elif Iregex.search(label): element_name = Iregex.findall(label)[0] index = voltage_defined_elem_names.index(element_name) x0[nv + index, 0] = value else: raise ValueError, "Unrecognized label " + label return x0[1:, :]
def get_variables(circ): """Returns a sympy matrix with the circuit variables to be solved for. """ nv_1 = len(circ.nodes_dict) - 1 # numero di soluzioni di tensione (al netto del ref) # descrizioni dei componenti non definibili in tensione idescr = [ (elem.letter_id.upper() + elem.descr) \ for elem in circ.elements if circuit.is_elem_voltage_defined(elem) ] mna_size = nv_1 + len(idescr) x = smzeros((mna_size, 1)) for i in range(mna_size): if i < nv_1: x[i, 0] = sympy.Symbol("V" + str(circ.nodes_dict[i + 1])) else: x[i, 0] = sympy.Symbol("I["+idescr[i - nv_1]+"]") return x
def print_dc_results(x, error, circ, print_int_nodes=False, print_error=True): """Prints out a set of DC results. x: the result set error: the residual error after solution, circ: the circuit instance of the simulated circuit print_int_nodes: a boolean to be set True if you wish to see voltage values of the internal nodes added automatically by the simulator. Returns: None """ # We have mixed current and voltage results # per primi vengono tanti valori di tensioni quanti sono i nodi del circuito meno uno, # quindi tante correnti quanti sono gli elementi definiti in tensione presenti # (per questo, per misurare una corrente, si può fare uso di generatori di tensione da 0V) nv_1 = len(circ.nodes_dict) - 1 # numero di soluzioni di tensione (al netto del ref) skip_nodes_list = [] # nodi da saltare, solo interni # descrizioni dei componenti non definibili in tensione idescr = [ (elem.letter_id.upper() + elem.descr) for elem in circ.elements if circuit.is_elem_voltage_defined(elem) ] # cleaner ?? print_array = [] # print "Solution:" for index in xrange(x.shape[0]): if index < nv_1: if print_int_nodes or not circ.is_int_node_internal_only(index + 1): line_array = ["V" + str(circ.nodes_dict[index + 1]) + ":", str(x[index, 0]), "V"] if print_error: line_array.append("(" + str(float(error[index])) + ")") else: skip_nodes_list.append(index) else: line_array = ["I(" + idescr[index - nv_1] + "):", str(x[index, 0]), "A"] if print_error: line_array.append("(" + str(float(error[index])) + ")") print_array.append(line_array) table_print(print_array) return None
def __init__(self, x, error, circ, outfile, iterations=0): """Holds a set of Operating Point results. x: the result set error: the residual error after solution, circ: the circuit instance of the simulated circuit print_int_nodes: a boolean to be set True if you wish to see voltage values of the internal nodes added automatically by the simulator. """ solution.__init__(self, circ, outfile) self.iterations = iterations # We have mixed current and voltage results # per primi vengono tanti valori di tensioni quanti sono i nodi del circuito meno # uno, quindi tante correnti quanti sono gli elementi definiti in tensione presenti # (per questo, per misurare una corrente, si può fare uso di generatori di tensione # da 0V) nv_1 = len(circ.nodes_dict ) - 1 # numero di soluzioni di tensione (al netto del ref) self.results = case_insensitive_dict() self.errors = case_insensitive_dict() self.x = x for index in range(nv_1): varname = ("V" + str(circ.nodes_dict[index + 1])).upper() self.variables += [varname] self.results.update({varname: x[index, 0]}) self.errors.update({varname: error[index, 0]}) self.units.update({varname: "V"}) if circ.is_int_node_internal_only(index + 1): self.skip_nodes_list.append(index) for elem in circ: if circuit.is_elem_voltage_defined(elem): index = index + 1 varname = ("I(" + elem.part_id.upper() + ")").upper() self.variables += [varname] self.results.update({varname: x[index, 0]}) self.errors.update({varname: error[index, 0]}) self.units.update({varname: "A"}) self.op_info = self.get_elements_op(circ, x)
def __init__(self, x, error, circ, outfile, iterations=0): """Holds a set of Operating Point results. x: the result set error: the residual error after solution, circ: the circuit instance of the simulated circuit print_int_nodes: a boolean to be set True if you wish to see voltage values of the internal nodes added automatically by the simulator. """ solution.__init__(self, circ, outfile) self.iterations = iterations # We have mixed current and voltage results # per primi vengono tanti valori di tensioni quanti sono i nodi del circuito meno # uno, quindi tante correnti quanti sono gli elementi definiti in tensione presenti # (per questo, per misurare una corrente, si può fare uso di generatori di tensione # da 0V) nv_1 = len(circ.nodes_dict) - 1 # numero di soluzioni di tensione (al netto del ref) self.results = case_insensitive_dict() self.errors = case_insensitive_dict() self.x = x for index in range(nv_1): varname = ("V" + str(circ.nodes_dict[index + 1])).upper() self.variables += [varname] self.results.update({varname: x[index, 0]}) self.errors.update({varname: error[index, 0]}) self.units.update({varname: "V"}) if circ.is_int_node_internal_only(index+1): self.skip_nodes_list.append(index) for elem in circ: if circuit.is_elem_voltage_defined(elem): index = index + 1 varname = ("I("+elem.part_id.upper()+")").upper() self.variables += [varname] self.results.update({varname: x[index, 0]}) self.errors.update({varname: error[index, 0]}) self.units.update({varname: "A"}) self.op_info = self.get_elements_op(circ, x)
def __init__(self, circ, method, period, outfile, t_array=None, x_array=None): """Holds a set of TRANSIENT results. circ: the circuit instance of the simulated circuit tstart: the sweep starting angular frequency tstop: the sweep stopping frequency op: the op used to start the tran analysis outfile: the file to write the results to. (Use "stdout" to write to std output) """ solution.__init__(self, circ, outfile) self.period = period self.method = method # We have mixed current and voltage results nv_1 = len(circ.nodes_dict ) - 1 # numero di soluzioni di tensione (al netto del ref) self.variables = ["T"] self.units.update({"T": "s"}) for index in range(nv_1): varname = "V%s" % (str(circ.nodes_dict[index + 1]), ) self.variables += [varname] self.units.update({varname: "V"}) if circ.is_int_node_internal_only(index + 1): self.skip_nodes_list.append(index) for elem in circ: if circuit.is_elem_voltage_defined(elem): varname = "I(%s)" % (elem.part_id.upper(), ) self.variables += [varname] self.units.update({varname: "A"}) if t_array is not None and x_array is not None: self.set_results(t_array, x_array)
def generate_Nac(circ): """Generate the vector holding the contribution of AC sources. """ n_of_nodes = len(circ.nodes_dict) Nac = numpy.mat(numpy.zeros((n_of_nodes, 1)), dtype=complex) j = numpy.complex('j') # process isources for elem in circ.elements: if isinstance(elem, devices.isource) and elem.abs_ac is not None: #convenzione normale! N[elem.n1, 0] = N[elem.n1, 0] + elem.abs_ac*numpy.exp(j*elem.arg_ac) N[elem.n2, 0] = N[elem.n2, 0] - elem.abs_ac*numpy.exp(j*elem.arg_ac) # process vsources # for each vsource, introduce a new variable: the current flowing through it. # then we introduce a KVL equation to be able to solve the circuit for elem in circ.elements: if circuit.is_elem_voltage_defined(elem): index = Nac.shape[0] Nac = utilities.expand_matrix(Nac, add_a_row=True, add_a_col=False) if isinstance(elem, devices.vsource) and elem.abs_ac is not None: Nac[index, 0] = -1.0*elem.abs_ac*numpy.exp(j*elem.arg_ac) return Nac
def build_Tass_static_vector(circ, Tf, points, step, tick, n_of_var, verbose=3): Tass_vector = [] nv = len(circ.nodes_dict) printing.print_info_line(("Building Tass...", 5), verbose, print_nl=False) tick.reset() tick.display(verbose > 2) for index in xrange(0, points): Tt = numpy.zeros((n_of_var, 1)) v_eq = 0 time = index * step 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.0 * 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 tick.step(verbose > 2) Tass_vector.append(Tf + Tt) tick.hide(verbose > 2) printing.print_info_line(("done.", 5), verbose) return Tass_vector
def __init__(self, circ, ostart, ostop, opoints, stype, op, outfile): """Holds a set of AC results. circ: the circuit instance of the simulated circuit ostart: the sweep starting angular frequency ostop: the sweep stopping frequency stype: the sweep type op: the linearization op used to compute the results """ solution.__init__(self, circ, outfile) self.linearization_op = op self.stype = stype self.ostart, self.ostop, self.opoints = ostart, ostop, opoints nv_1 = len(circ.nodes_dict ) - 1 # numero di soluzioni di tensione (al netto del ref) self.variables += ["w"] self.units.update({"w": "rad/s"}) for index in range(nv_1): varname_abs = "|V%s|" % (str(circ.nodes_dict[index + 1]), ) varname_arg = "arg(V%s)" % (str(circ.nodes_dict[index + 1]), ) self.variables += [varname_abs] self.variables += [varname_arg] self.units.update({varname_abs: "V"}) self.units.update({varname_arg: ""}) if circ.is_int_node_internal_only(index + 1): self.skip_nodes_list.append(index) for elem in circ: if circuit.is_elem_voltage_defined(elem): varname_abs = "|I(%s)|" % (elem.part_id.upper(), ) varname_arg = "arg(I(%s))" % (elem.part_id.upper(), ) self.variables += [varname_abs] self.variables += [varname_arg] self.units.update({varname_abs: "A"}) self.units.update({varname_arg: ""})
def generate_mna_and_N(circ, opts, ac=False, verbose=3): """Generates a symbolic Modified Nodal Analysis matrix and N vector. """ # print options n_of_nodes = len(circ.nodes_dict) mna = smzeros(n_of_nodes) N = smzeros((n_of_nodes, 1)) s = sympy.Symbol('s', complex=True) subs_g = {} for elem in circ: if isinstance(elem, devices.Resistor): # we use conductances instead of 1/R because there is a significant # overhead handling many 1/R terms in sympy. if elem.is_symbolic: R = sympy.Symbol(elem.part_id.upper(), real=True, positive=True) G = sympy.Symbol('G' + elem.part_id[1:], real=True, positive=True) # but we keep track of which is which and substitute back after # solving. subs_g.update({G: 1 / R}) else: R = elem.value G = 1.0 / R mna[elem.n1, elem.n1] = mna[elem.n1, elem.n1] + G mna[elem.n1, elem.n2] = mna[elem.n1, elem.n2] - G mna[elem.n2, elem.n1] = mna[elem.n2, elem.n1] - G mna[elem.n2, elem.n2] = mna[elem.n2, elem.n2] + G elif isinstance(elem, devices.Capacitor): if ac: if elem.is_symbolic: capa = sympy.Symbol(elem.part_id.upper(), real=True, positive=True) else: capa = elem.value mna[elem.n1, elem.n1] = mna[elem.n1, elem.n1] + s * capa mna[elem.n1, elem.n2] = mna[elem.n1, elem.n2] - s * capa mna[elem.n2, elem.n2] = mna[elem.n2, elem.n2] + s * capa mna[elem.n2, elem.n1] = mna[elem.n2, elem.n1] - s * capa else: pass elif isinstance(elem, devices.Inductor): pass elif isinstance(elem, devices.GISource): if elem.is_symbolic: alpha = sympy.Symbol(elem.part_id.upper(), real=True) else: alpha = elem.value mna[elem.n1, elem.sn1] = mna[elem.n1, elem.sn1] + alpha mna[elem.n1, elem.sn2] = mna[elem.n1, elem.sn2] - alpha mna[elem.n2, elem.sn1] = mna[elem.n2, elem.sn1] - alpha mna[elem.n2, elem.sn2] = mna[elem.n2, elem.sn2] + alpha elif isinstance(elem, devices.ISource): if elem.is_symbolic: IDC = sympy.Symbol(elem.part_id.upper(), real=True) else: IDC = elem.dc_value N[elem.n1, 0] = N[elem.n1, 0] + IDC N[elem.n2, 0] = N[elem.n2, 0] - IDC elif isinstance(elem, mosq.mosq_device) or isinstance( elem, ekv.ekv_device): gm = sympy.Symbol('gm_' + elem.part_id, real=True, positive=True) mna[elem.n1, elem.ng] = mna[elem.n1, elem.ng] + gm mna[elem.n1, elem.n2] = mna[elem.n1, elem.n2] - gm mna[elem.n2, elem.ng] = mna[elem.n2, elem.ng] - gm mna[elem.n2, elem.n2] = mna[elem.n2, elem.n2] + gm if opts['r0s']: r0 = sympy.Symbol('r0_' + elem.part_id, real=True, positive=True) mna[elem.n1, elem.n1] = mna[elem.n1, elem.n1] + 1 / r0 mna[elem.n1, elem.n2] = mna[elem.n1, elem.n2] - 1 / r0 mna[elem.n2, elem.n1] = mna[elem.n2, elem.n1] - 1 / r0 mna[elem.n2, elem.n2] = mna[elem.n2, elem.n2] + 1 / r0 elif isinstance(elem, diode.diode): gd = sympy.Symbol("g" + elem.part_id, positive=True) mna[elem.n1, elem.n1] = mna[elem.n1, elem.n1] + gd mna[elem.n1, elem.n2] = mna[elem.n1, elem.n2] - gd mna[elem.n2, elem.n1] = mna[elem.n2, elem.n1] - gd mna[elem.n2, elem.n2] = mna[elem.n2, elem.n2] + gd elif isinstance(elem, devices.InductorCoupling): pass # this is taken care of within the inductors elif circuit.is_elem_voltage_defined(elem): pass # we'll add its lines afterwards elif verbose: printing.print_warning("Skipped elem %s: not implemented." % (elem.part_id.upper(), )) pre_vde = mna.shape[0] for elem in circ: if circuit.is_elem_voltage_defined(elem): index = mna.shape[0] # get_matrix_size(mna)[0] mna = expand_matrix(mna, add_a_row=True, add_a_col=True) N = expand_matrix(N, add_a_row=True, add_a_col=False) # KCL mna[elem.n1, index] = +1 mna[elem.n2, index] = -1 # KVL mna[index, elem.n1] = +1 mna[index, elem.n2] = -1 if isinstance(elem, devices.VSource): if elem.is_symbolic: VDC = sympy.Symbol(elem.part_id.upper(), real=True) else: VDC = elem.dc_value N[index, 0] = -VDC elif isinstance(elem, devices.EVSource): if elem.is_symbolic: alpha = sympy.Symbol(elem.part_id.upper(), real=True) else: alpha = elem.alpha mna[index, elem.sn1] = -alpha mna[index, elem.sn2] = +alpha elif isinstance(elem, devices.Inductor): if ac: if elem.is_symbolic: L = sympy.Symbol(elem.part_id.upper(), real=True, positive=True) else: L = elem.L mna[index, index] = -s * L else: pass # already so: commented out # N[index,0] = 0 elif isinstance(elem, devices.HVSource): printing.print_warning( "symbolic.py: BUG - hvsources are not implemented yet.") sys.exit(33) for elem in circ: if circuit.is_elem_voltage_defined(elem): if isinstance(elem, devices.Inductor): if ac: # find its index to know which column corresponds to its # current this_index = circ.find_vde_index(elem.part_id, verbose=0) for cd in elem.coupling_devices: if cd.is_symbolic: M = sympy.Symbol(cd.part_id, real=True, positive=True) else: M = cd.K # get `part_id` of the other inductor (eg. "L32") other_id_wdescr = cd.get_other_inductor(elem.part_id) # find its index to know which column corresponds to # its current other_index = circ.find_vde_index(other_id_wdescr, verbose=0) # add the term. mna[pre_vde + this_index, pre_vde + other_index] += -s * M else: pass # all done return (mna, N, subs_g)
def get_dc_guess(circ, verbose=3): """This method tries to build a DC guess, according to what the elements suggest. A element can suggest its guess through the elem.dc_guess field. verbose: verbosity level (from 0 silent to 5 debug) Returns: the dc_guess (matrix) or None """ if verbose: sys.stdout.write("Calculating guess: ") sys.stdout.flush() # A DC guess has meaning only if the circuit has NL elements if not circ.is_nonlinear(): if verbose: print "skipped. (linear circuit)" return None if verbose > 3: print "" nv = len(circ.nodes_dict) M = numpy.mat(numpy.zeros((1, nv))) T = numpy.mat(numpy.zeros((1, 1))) index = 0 v_eq = 0 # number of current equations one_element_with_dc_guess_found = False for elem in circ.elements: # In the meanwhile, check how many current equations are # required to solve the circuit if circuit.is_elem_voltage_defined(elem): v_eq = v_eq + 1 # This is the main focus: build a system of equations (M*x = T) if hasattr(elem, "dc_guess") and elem.dc_guess is not None: if not one_element_with_dc_guess_found: one_element_with_dc_guess_found = True if elem.is_nonlinear: port_index = 0 for (n1, n2) in elem.ports: if n1 == n2: continue if index: M = utilities.expand_matrix(M, add_a_row=True, add_a_col=False) T = utilities.expand_matrix(T, add_a_row=True, add_a_col=False) M[index, n1] = +1 M[index, n2] = -1 T[index] = elem.dc_guess[port_index] port_index = port_index + 1 index = index + 1 else: if elem.n1 == elem.n2: continue if index: M = utilities.expand_matrix(M, add_a_row=True, add_a_col=False) T = utilities.expand_matrix(T, add_a_row=True, add_a_col=False) M[index, elem.n1] = +1 M[index, elem.n2] = -1 T[index] = elem.dc_guess[0] index = index + 1 if verbose == 5: print "DBG: get_dc_guess(): M and T, no reduction" print M print T M = utilities.remove_row_and_col(M, rrow=10*M.shape[0], rcol=0) if not one_element_with_dc_guess_found: if verbose == 5: print "DBG: get_dc_guess(): no element has a dc_guess" elif verbose <= 3: print "skipped." return None # We wish to find the linearly dependent lines of the M matrix. # The matrix is made by +1, -1, 0 elements. # Hence, if two lines are linearly dependent, one of these equations # has to be satisfied: (L1, L2 are two lines) # L1 + L2 = 0 (vector) # L2 - L1 = 0 (vector) # This is tricky, because I wish to remove lines of the matrix while # browsing it. # We browse the matrix by line from bottom up and compare each line # with the upper lines. If a linearly dep. line is found, we remove # the current line. # Then break from the loop, get the next line (bottom up), which is # the same we were considering before; compare with the upper lines.. # Not optimal, but it works. for i in range(M.shape[0]-1, -1, -1): for j in range(i-1, -1, -1): #print i, j, M[i, :], M[j, :] dummy1 = M[i, :] - M[j, :] dummy2 = M[i, :] + M[j, :] if not dummy1.any() or not dummy2.any(): #print "REM:", M[i, :] M = utilities.remove_row(M, rrow=i) T = utilities.remove_row(T, rrow=i) break if verbose == 5: print "DBG: get_dc_guess(): M and T, after removing LD lines" print M print T # Remove empty columns: # If a column is empty, we have no guess regarding the corresponding # node. It makes the matrix singular. -> Remove the col & remember # that we are _not_ calculating a guess for it. removed_index = [] for i in range(M.shape[1]-1, -1, -1): if not M[:, i].any(): M = utilities.remove_row_and_col(M, rrow=M.shape[0], rcol=i) removed_index.append(i) if verbose > 3: print "DBG: get_dc_guess(): M and T, after removing empty columns." print M print "T\n", T # Now, we have a set of equations to be solved. # There are three cases: # 1. The M matrix has a different number of rows and columns. # We use the Moore-Penrose matrix inverse to get # the shortest length least squares solution to the problem # M*x + T = 0 # 2. The matrix is square. # It seems that if the circuit is not pathological, # we are likely to find a solution (the matrix has det != 0). # I'm not sure about this though. if M.shape[0] != M.shape[1]: Rp = numpy.mat(numpy.linalg.pinv(M)) * T else: # case M.shape[0] == M.shape[1], use normal if numpy.linalg.det(M) != 0: try: Rp = numpy.linalg.inv(M) * T except numpy.linalg.linalg.LinAlgError: eig = numpy.linalg.eig(M)[0] cond = abs(eig).max()/abs(eig).min() if verbose: print "cond=" +str(cond)+". No guess." return None else: if verbose: print "Guess matrix is singular. No guess." return None # Now we want to: # 1. Add voltages for the nodes for which we have no clue to guess. # 2. Append to each vector of guesses the values for currents in # voltage defined elem. # Both them are set to 0 for index in removed_index: Rp = numpy.concatenate(( \ numpy.concatenate((Rp[:index, 0], \ numpy.mat(numpy.zeros((1, 1)))), axis=0), \ Rp[index:, 0]), axis=0) # add the 0s for the currents due to the voltage defined # elements (we have no guess for those...) if v_eq > 0: Rp = numpy.concatenate((Rp, numpy.mat(numpy.zeros((v_eq, 1)))), axis=0) if verbose == 5: print circ.nodes_dict if verbose and verbose < 4: print "done." if verbose > 3: print "Guess:" print Rp return Rp
def get_dc_guess(circ, verbose=3): """This method tries to build a DC guess, according to what the elements suggest. A element can suggest its guess through the elem.dc_guess field. verbose: verbosity level (from 0 silent to 5 debug) Returns: the dc_guess (matrix) or None """ if verbose: sys.stdout.write("Calculating guess: ") sys.stdout.flush() # A DC guess has meaning only if the circuit has NL elements if not circ.is_nonlinear(): if verbose: print "skipped. (linear circuit)" return None if verbose > 3: print "" nv = len(circ.nodes_dict) M = numpy.mat(numpy.zeros((1, nv))) T = numpy.mat(numpy.zeros((1, 1))) index = 0 v_eq = 0 # number of current equations one_element_with_dc_guess_found = False for elem in circ.elements: # In the meanwhile, check how many current equations are # required to solve the circuit if circuit.is_elem_voltage_defined(elem): v_eq = v_eq + 1 # This is the main focus: build a system of equations (M*x = T) if hasattr(elem, "dc_guess") and elem.dc_guess is not None: if not one_element_with_dc_guess_found: one_element_with_dc_guess_found = True if elem.is_nonlinear: port_index = 0 for (n1, n2) in elem.ports: if n1 == n2: continue if index: M = utilities.expand_matrix(M, add_a_row=True, add_a_col=False) T = utilities.expand_matrix(T, add_a_row=True, add_a_col=False) M[index, n1] = +1 M[index, n2] = -1 T[index] = elem.dc_guess[port_index] port_index = port_index + 1 index = index + 1 else: if elem.n1 == elem.n2: continue if index: M = utilities.expand_matrix(M, add_a_row=True, add_a_col=False) T = utilities.expand_matrix(T, add_a_row=True, add_a_col=False) M[index, elem.n1] = +1 M[index, elem.n2] = -1 T[index] = elem.dc_guess[0] index = index + 1 if verbose == 5: print "DBG: get_dc_guess(): M and T, no reduction" print M print T M = utilities.remove_row_and_col(M, rrow=10 * M.shape[0], rcol=0) if not one_element_with_dc_guess_found: if verbose == 5: print "DBG: get_dc_guess(): no element has a dc_guess" elif verbose <= 3: print "skipped." return None # We wish to find the linearly dependent lines of the M matrix. # The matrix is made by +1, -1, 0 elements. # Hence, if two lines are linearly dependent, one of these equations # has to be satisfied: (L1, L2 are two lines) # L1 + L2 = 0 (vector) # L2 - L1 = 0 (vector) # This is tricky, because I wish to remove lines of the matrix while # browsing it. # We browse the matrix by line from bottom up and compare each line # with the upper lines. If a linearly dep. line is found, we remove # the current line. # Then break from the loop, get the next line (bottom up), which is # the same we were considering before; compare with the upper lines.. # Not optimal, but it works. for i in range(M.shape[0] - 1, -1, -1): for j in range(i - 1, -1, -1): #print i, j, M[i, :], M[j, :] dummy1 = M[i, :] - M[j, :] dummy2 = M[i, :] + M[j, :] if not dummy1.any() or not dummy2.any(): #print "REM:", M[i, :] M = utilities.remove_row(M, rrow=i) T = utilities.remove_row(T, rrow=i) break if verbose == 5: print "DBG: get_dc_guess(): M and T, after removing LD lines" print M print T # Remove empty columns: # If a column is empty, we have no guess regarding the corresponding # node. It makes the matrix singular. -> Remove the col & remember # that we are _not_ calculating a guess for it. removed_index = [] for i in range(M.shape[1] - 1, -1, -1): if not M[:, i].any(): M = utilities.remove_row_and_col(M, rrow=M.shape[0], rcol=i) removed_index.append(i) if verbose > 3: print "DBG: get_dc_guess(): M and T, after removing empty columns." print M print "T\n", T # Now, we have a set of equations to be solved. # There are three cases: # 1. The M matrix has a different number of rows and columns. # We use the Moore-Penrose matrix inverse to get # the shortest length least squares solution to the problem # M*x + T = 0 # 2. The matrix is square. # It seems that if the circuit is not pathological, # we are likely to find a solution (the matrix has det != 0). # I'm not sure about this though. if M.shape[0] != M.shape[1]: Rp = numpy.mat(numpy.linalg.pinv(M)) * T else: # case M.shape[0] == M.shape[1], use normal if numpy.linalg.det(M) != 0: try: Rp = numpy.linalg.inv(M) * T except numpy.linalg.linalg.LinAlgError: eig = numpy.linalg.eig(M)[0] cond = abs(eig).max() / abs(eig).min() if verbose: print "cond=" + str(cond) + ". No guess." return None else: if verbose: print "Guess matrix is singular. No guess." return None # Now we want to: # 1. Add voltages for the nodes for which we have no clue to guess. # 2. Append to each vector of guesses the values for currents in # voltage defined elem. # Both them are set to 0 for index in removed_index: Rp = numpy.concatenate(( \ numpy.concatenate((Rp[:index, 0], \ numpy.mat(numpy.zeros((1, 1)))), axis=0), \ Rp[index:, 0]), axis=0) # add the 0s for the currents due to the voltage defined # elements (we have no guess for those...) if v_eq > 0: Rp = numpy.concatenate((Rp, numpy.mat(numpy.zeros((v_eq, 1)))), axis=0) if verbose == 5: print circ.nodes_dict if verbose and verbose < 4: print "done." if verbose > 3: print "Guess:" print Rp return Rp
def generate_mna_and_N(circ, verbose=3): """La vecchia versione usava il sistema visto a lezione, quella nuova mira ad essere magari meno elegante, ma funzionale, flessibile e comprensibile. MNA e N vengono creati direttamente della dimensione det. dal numero dei nodi, poi se ci sono voltage sources vengono allargate. Il vettore incognita � fatto cos�: x vettore colonna di lunghezza (N_nodi - 1) + N_vsources, i primi N_nodi valori di x, corrispondono alle tensioni ai nodi, gli altri alle correnti nei generatori di tensione. Le tensioni nodali sono ordinate tramite i numeri interni dei nodi, in ordine CRESCENTE, saltando il nodo 0, preso a riferimento. L'ordine delle correnti nei gen di tensione � det. dall'ordine in cui essi vengono incontrati scorrendo `circ`. Viene sempre usata la convenzione normale. Il sistema � cos� fatto: MNA*x + N = 0 Richiede in ingresso la descrizione del circuito, circ. Restituisce: (MNA, N) """ n_of_nodes = len(circ.nodes_dict) mna = numpy.mat(numpy.zeros((n_of_nodes, n_of_nodes))) N = numpy.mat(numpy.zeros((n_of_nodes, 1))) for elem in circ: if elem.is_nonlinear: continue elif isinstance(elem, devices.Resistor): mna[elem.n1, elem.n1] = mna[elem.n1, elem.n1] + 1.0 / elem.value mna[elem.n1, elem.n2] = mna[elem.n1, elem.n2] - 1.0 / elem.value mna[elem.n2, elem.n1] = mna[elem.n2, elem.n1] - 1.0 / elem.value mna[elem.n2, elem.n2] = mna[elem.n2, elem.n2] + 1.0 / elem.value elif isinstance(elem, devices.Capacitor): pass # In a capacitor I(V) = 0 elif isinstance(elem, devices.GISource): mna[elem.n1, elem.sn1] = mna[elem.n1, elem.sn1] + elem.alpha mna[elem.n1, elem.sn2] = mna[elem.n1, elem.sn2] - elem.alpha mna[elem.n2, elem.sn1] = mna[elem.n2, elem.sn1] - elem.alpha mna[elem.n2, elem.sn2] = mna[elem.n2, elem.sn2] + elem.alpha elif isinstance(elem, devices.ISource): if not elem.is_timedependent: # convenzione normale! N[elem.n1, 0] = N[elem.n1, 0] + elem.I() N[elem.n2, 0] = N[elem.n2, 0] - elem.I() else: pass # vengono aggiunti volta per volta elif isinstance(elem, devices.InductorCoupling): pass # this is taken care of within the inductors elif circuit.is_elem_voltage_defined(elem): pass # we'll add its lines afterwards else: print "dc_analysis.py: BUG - Unknown linear element. Ref. #28934" # process vsources # i generatori di tensione non sono pilotabili in tensione: g � infinita # for each vsource, introduce a new variable: the current flowing through it. # then we introduce a KVL equation to be able to solve the circuit for elem in circ: if circuit.is_elem_voltage_defined(elem): index = mna.shape[0] # get_matrix_size(mna)[0] mna = utilities.expand_matrix(mna, add_a_row=True, add_a_col=True) N = utilities.expand_matrix(N, add_a_row=True, add_a_col=False) # KCL mna[elem.n1, index] = 1.0 mna[elem.n2, index] = -1.0 # KVL mna[index, elem.n1] = +1.0 mna[index, elem.n2] = -1.0 if isinstance(elem, devices.VSource) and not elem.is_timedependent: # corretto, se � def una parte tempo-variabile ci pensa # mdn_solver a scegliere quella giusta da usare. N[index, 0] = -1.0 * elem.V() elif isinstance(elem, devices.VSource) and elem.is_timedependent: pass # taken care step by step elif isinstance(elem, devices.EVSource): mna[index, elem.sn1] = -1.0 * elem.alpha mna[index, elem.sn2] = +1.0 * elem.alpha elif isinstance(elem, devices.Inductor): # N[index,0] = 0 pass, it's already zero pass elif isinstance(elem, devices.HVSource): print "dc_analysis.py: BUG - hvsources are not implemented yet." sys.exit(33) else: print "dc_analysis.py: BUG - found an unknown voltage_def elem." print elem sys.exit(33) # Seems a good place to run some sanity check # for the time being we do not halt the execution check_ground_paths(mna, circ, reduced_mna=False, verbose=verbose) # all done return (mna, N)
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 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 generate_mna_and_N(circ): """La vecchia versione usava il sistema visto a lezione, quella nuova mira ad essere magari meno elegante, ma funzionale, flessibile e comprensibile. MNA e N vengono creati direttamente della dimensione det. dal numero dei nodi, poi se ci sono voltage sources vengono allargate. Il vettore incognita � fatto cos�: x vettore colonna di lunghezza (N_nodi - 1) + N_vsources, i primi N_nodi valori di x, corrispondono alle tensioni ai nodi, gli altri alle correnti nei generatori di tensione. Le tensioni nodali sono ordinate tramite i numeri interni dei nodi, in ordine CRESCENTE, saltando il nodo 0, preso a riferimento. L'ordine delle correnti nei gen di tensione � det. dall'ordine in cui essi vengono incontrati scorrendo circ.elements. Viene sempre usata la convenzione normale. Il sistema � cos� fatto: MNA*x + N = 0 Richiede in ingresso la descrizione del circuito, circ. Restituisce: (MNA, N) """ n_of_nodes = len(circ.nodes_dict) mna = numpy.mat(numpy.zeros((n_of_nodes, n_of_nodes))) N = numpy.mat(numpy.zeros((n_of_nodes, 1))) for elem in circ.elements: if elem.is_nonlinear: continue elif isinstance(elem, devices.resistor): mna[elem.n1, elem.n1] = mna[elem.n1, elem.n1] + 1.0 / elem.R mna[elem.n1, elem.n2] = mna[elem.n1, elem.n2] - 1.0 / elem.R mna[elem.n2, elem.n1] = mna[elem.n2, elem.n1] - 1.0 / elem.R mna[elem.n2, elem.n2] = mna[elem.n2, elem.n2] + 1.0 / elem.R elif isinstance(elem, devices.capacitor): pass # In a capacitor I(V) = 0 elif isinstance(elem, devices.gisource): mna[elem.n1, elem.sn1] = mna[elem.n1, elem.sn1] + elem.alpha mna[elem.n1, elem.sn2] = mna[elem.n1, elem.sn2] - elem.alpha mna[elem.n2, elem.sn1] = mna[elem.n2, elem.sn1] - elem.alpha mna[elem.n2, elem.sn2] = mna[elem.n2, elem.sn2] + elem.alpha elif isinstance(elem, devices.isource): if not elem.is_timedependent: # convenzione normale! N[elem.n1, 0] = N[elem.n1, 0] + elem.I() N[elem.n2, 0] = N[elem.n2, 0] - elem.I() else: pass # vengono aggiunti volta per volta elif isinstance(elem, devices.inductor_coupling): pass # this is taken care of within the inductors elif circuit.is_elem_voltage_defined(elem): pass # we'll add its lines afterwards else: print "dc_analysis.py: BUG - Unknown linear element. Ref. #28934" # process vsources # i generatori di tensione non sono pilotabili in tensione: g � infinita # for each vsource, introduce a new variable: the current flowing through it. # then we introduce a KVL equation to be able to solve the circuit for elem in circ.elements: if circuit.is_elem_voltage_defined(elem): index = mna.shape[0] # get_matrix_size(mna)[0] mna = utilities.expand_matrix(mna, add_a_row=True, add_a_col=True) N = utilities.expand_matrix(N, add_a_row=True, add_a_col=False) # KCL mna[elem.n1, index] = 1.0 mna[elem.n2, index] = -1.0 # KVL mna[index, elem.n1] = +1.0 mna[index, elem.n2] = -1.0 if isinstance(elem, devices.vsource) and not elem.is_timedependent: # corretto, se � def una parte tempo-variabile ci pensa # mdn_solver a scegliere quella giusta da usare. N[index, 0] = -1.0 * elem.V() elif isinstance(elem, devices.vsource) and elem.is_timedependent: pass # taken care step by step elif isinstance(elem, devices.evsource): mna[index, elem.sn1] = -1.0 * elem.alpha mna[index, elem.sn2] = +1.0 * elem.alpha elif isinstance(elem, devices.inductor): # N[index,0] = 0 pass, it's already zero pass elif isinstance(elem, devices.hvsource): print "dc_analysis.py: BUG - hvsources are not implemented yet." sys.exit(33) else: print "dc_analysis.py: BUG - found an unknown voltage_def elem." print elem sys.exit(33) # Seems a good place to run some sanity check # for the time being we do not halt the execution check_ground_paths(mna, circ, reduced_mna=False) # all done return (mna, N)
def generate_mna_and_N(circ, opts, ac=False): """Generates a symbolic Modified Nodal Analysis matrix and N vector. """ #print options n_of_nodes = len(circ.nodes_dict) mna = smzeros(n_of_nodes) N = smzeros((n_of_nodes, 1)) s = sympy.Symbol("s", complex=True) subs_g = {} #process_elements() for elem in circ.elements: #if elem.is_nonlinear and not (isinstance(elem, mosq.mosq_device) or isinstance(elem, ekv.ekv_device)): # print "Skipped elem "+elem.letter_id.upper()+elem.descr + ": not implemented." # continue if isinstance(elem, devices.resistor): # we use conductances instead of 1/R because there is a significant # overhead handling many 1/R terms in sympy. if elem.is_symbolic: R = sympy.Symbol(elem.letter_id.upper()+elem.descr, real=True) G = sympy.Symbol("G"+elem.descr, real=True) # but we keep track of which is which and substitute back after solving. subs_g.update({G:1/R}) else: R = elem.R G = 1.0/R #mna[elem.n1, elem.n1] = mna[elem.n1, elem.n1] + 1/R #mna[elem.n1, elem.n2] = mna[elem.n1, elem.n2] - 1/R #mna[elem.n2, elem.n1] = mna[elem.n2, elem.n1] - 1/R #mna[elem.n2, elem.n2] = mna[elem.n2, elem.n2] + 1/R mna[elem.n1, elem.n1] = mna[elem.n1, elem.n1] + G mna[elem.n1, elem.n2] = mna[elem.n1, elem.n2] - G mna[elem.n2, elem.n1] = mna[elem.n2, elem.n1] - G mna[elem.n2, elem.n2] = mna[elem.n2, elem.n2] + G elif isinstance(elem, devices.capacitor): if ac: if elem.is_symbolic: capa = sympy.Symbol(elem.letter_id.upper()+elem.descr, real=True) else: capa = elem.C mna[elem.n1, elem.n1] = mna[elem.n1, elem.n1] + s*capa mna[elem.n1, elem.n2] = mna[elem.n1, elem.n2] - s*capa mna[elem.n2, elem.n2] = mna[elem.n2, elem.n2] + s*capa mna[elem.n2, elem.n1] = mna[elem.n2, elem.n1] - s*capa else: pass elif isinstance(elem, devices.inductor): pass elif isinstance(elem, devices.gisource): if elem.is_symbolic: alpha = sympy.Symbol(elem.letter_id+elem.descr, real=True) else: alpha = elem.alpha mna[elem.n1, elem.sn1] = mna[elem.n1, elem.sn1] + alpha mna[elem.n1, elem.sn2] = mna[elem.n1, elem.sn2] - alpha mna[elem.n2, elem.sn1] = mna[elem.n2, elem.sn1] - alpha mna[elem.n2, elem.sn2] = mna[elem.n2, elem.sn2] + alpha elif isinstance(elem, devices.isource): if elem.is_symbolic: IDC = sympy.Symbol(elem.letter_id.upper()+elem.descr, real=True) else: IDC = elem.idc N[elem.n1, 0] = N[elem.n1, 0] + IDC N[elem.n2, 0] = N[elem.n2, 0] - IDC elif isinstance(elem, mosq.mosq_device) or isinstance(elem, ekv.ekv_device): gm = sympy.Symbol('gm_'+elem.letter_id+elem.descr, real=True) mna[elem.n1, elem.ng] = mna[elem.n1, elem.ng] + gm mna[elem.n1, elem.n2] = mna[elem.n1, elem.n2] - gm mna[elem.n2, elem.ng] = mna[elem.n2, elem.ng] - gm mna[elem.n2, elem.n2] = mna[elem.n2, elem.n2] + gm if opts['r0s']: r0 = sympy.Symbol('r0_'+elem.letter_id+elem.descr, real=True) mna[elem.n1, elem.n1] = mna[elem.n1, elem.n1] + 1/r0 mna[elem.n1, elem.n2] = mna[elem.n1, elem.n2] - 1/r0 mna[elem.n2, elem.n1] = mna[elem.n2, elem.n1] - 1/r0 mna[elem.n2, elem.n2] = mna[elem.n2, elem.n2] + 1/r0 elif isinstance(elem, diode.diode): gd = sympy.Symbol("g"+elem.letter_id+elem.descr) mna[elem.n1, elem.n1] = mna[elem.n1, elem.n1] + gd mna[elem.n1, elem.n2] = mna[elem.n1, elem.n2] - gd mna[elem.n2, elem.n1] = mna[elem.n2, elem.n1] - gd mna[elem.n2, elem.n2] = mna[elem.n2, elem.n2] + gd elif isinstance(elem, devices.inductor_coupling): pass # this is taken care of within the inductors elif circuit.is_elem_voltage_defined(elem): pass #we'll add its lines afterwards else: printing.print_warning("Skipped elem %s: not implemented." % (elem.letter_id.upper()+elem.descr,)) pre_vde = mna.shape[0] for elem in circ.elements: if circuit.is_elem_voltage_defined(elem): index = mna.shape[0] #get_matrix_size(mna)[0] mna = expand_matrix(mna, add_a_row=True, add_a_col=True) N = expand_matrix(N, add_a_row=True, add_a_col=False) # KCL mna[elem.n1, index] = +1 mna[elem.n2, index] = -1 # KVL mna[index, elem.n1] = +1 mna[index, elem.n2] = -1 if isinstance(elem, devices.vsource): if elem.is_symbolic: VDC = sympy.Symbol(elem.letter_id.upper() + elem.descr, real=True) else: VDC = elem.vdc N[index, 0] = -VDC elif isinstance(elem, devices.evsource): if elem.is_symbolic: alpha = sympy.Symbol(elem.letter_id.upper() + elem.descr, real=True) else: alpha = elem.alpha mna[index, elem.sn1] = -alpha mna[index, elem.sn2] = +alpha elif isinstance(elem, devices.inductor): if ac: if elem.is_symbolic: L = sympy.Symbol(elem.letter_id.upper() + elem.descr, real=True) else: L = elem.L mna[index, index] = -s*L else: pass # already so: commented out # N[index,0] = 0 elif isinstance(elem, devices.hvsource): printing.print_warning("symbolic.py: BUG - hvsources are not implemented yet.") sys.exit(33) for elem in circ.elements: if circuit.is_elem_voltage_defined(elem): if isinstance(elem, devices.inductor): if ac: # find its index to know which column corresponds to its current this_index = circ.find_vde_index("L"+elem.descr, verbose=0) for cd in elem.coupling_devices: if cd.is_symbolic: M = sympy.Symbol("M" + cd.descr, real=True) else: M = cd.K # get id+descr of the other inductor (eg. "L32") other_id_wdescr = cd.get_other_inductor("L"+elem.descr) # find its index to know which column corresponds to its current other_index = circ.find_vde_index(other_id_wdescr, verbose=0) # add the term. #print "other_index: "+str(other_index) #print "this_index: "+str(this_index) mna[pre_vde+this_index,pre_vde+other_index] += -s*M #print mna else: pass # already so: commented out # N[index,0] = 0 #all done return (mna, N, subs_g)