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