This module provides the functions needed to perform OP and DC simulations.

The principal are:

Notice that internally, dc_analysis() calls op_analysis(), since a DC sweep is nothing but a series of OP analyses..

The actual circuit solution is done by mdn_solver(), that uses a modified version of the Newton Rhapson method.

Module reference

build_gmin_matrix(circ, gmin, mna_size, verbose)[source]

Build a Gmin matrix


circ : circuit instance
The circuit for which the matrix is built.
gmin : scalar float
The value of the minimum conductance to ground to be used.
mna_size : int
The size of the MNA matrix associated with the GMIN matrix being built.
verbose : int
The verbosity level, from 0 (silent) to 6 (debug).


Gmin : ndarray of size (mna_size, mna_size)
The Gmin matrix itself.
build_x0_from_user_supplied_ic(circ, icdict)[source]

Builds a vector of appropriate (reduced!) size from the values supplied in icdict.

Supplying a custom x0 can be useful: - To aid convergence in tough circuits, - To start a transient simulation from a particular x0.


circ: circuit instance
The circuit the \(x_0\) is being assembled for
icdict: dict
icdict is a 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>)':<current 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 standard convention.


x0 : ndarray
The x0 matrix assembled according to icdict.
Raises:ValueError – whenever a malformed icdict is supplied.
dc_analysis(circ, start, stop, step, source, sweep_type=u'LINEAR', guess=True, x0=None, outfile=u'stdout', verbose=3)[source]

Performs a sweep of the value of V or I of a independent source from start value to stop value using the provided step.

For every circuit generated, computes the OP. This function relays on dc_analysis.op_analysis() to actually solve each circuit.


circ : Circuit instance
The circuit instance to be simulated.
start : float
Start value of the sweep source
stop : float
Stop value of the sweep source
step : float
The step size in the sweep
source : string
The part ID of the source to be swept, eg. 'V1'.
sweep_type : string, optional
Either options.dc_lin_step (default) or options.dc_log_step
guess : boolean, optional
op_analysis will guess to start the first NR iteration for the first point, the previsious OP is used from then on. Defaults to True.
outfile : string, optional
Filename of the output file. If set to 'stdout' (default), prints to screen.
verbose : int
The verbosity level, from 0 (silent) to 6 (debug).


rstdc : results.dc_solution instance or None
A results.dc_solution instance is returned, if a solution was found for at least one sweep value. or None, if an error occurred (eg invalid start/stop/step values) or there was no solution for any sweep value.
dc_solve(mna, Ndc, circ, Ntran=None, Gmin=None, x0=None, time=None, MAXIT=None, locked_nodes=None, skip_Tt=False, verbose=3)[source]

Low-level method to perform a DC solution of the circuit


Typically the user calls dc_analysis.op_analysis() or dc_analysis.dc_analysis(), which in turn will setup all matrices and call this method on their behalf.

The system we want to solve is:

\[(mna + G_{min}) \cdot x + N(t) + T(x, t) = 0\]


  • \(mna\) is the reduced MNA matrix with the required KVL/KCL rows
  • \(N\) is composed by a DC part, \(N_{dc}\), and a dynamic time-dependent part \(N_{tran}(t)\) and a time-dependent part \(T_t(t)\).
  • \(T(x, t)\) is both time-dependent and non-linear with respect to the circuit solution \(x\), and it will be built at each iteration over \(t\) and \(x\).


mna : ndarray
The MNA matrix described above. It can be built calling generate_mna_and_N(). This matrix will contain the dynamic component due to a Differetiation Formula (DF) when this method is called from a transient analysis.
Ndc : ndarray
The DC part of \(N\). Also this vector may be built calling generate_mna_and_N().
circ : Circuit instance
The circuit instance from which mna and N were built.
Ntran : ndarray, optional
The linear time-dependent and dynamic part of \(N\), if available. Notice this is typically set when a DF being applied and the method is being called from a transient analysis.
Gmin : ndarray, optional
A matrix of the same size of mna, containing the minimum transconductances to ground. It can be built with build_gmin_matrix(). If not set, no Gmin matrix is used.
x0 : ndarray or results.op_solution instance, optional
The initial guess for the Newthon-Rhapson algorithm. If not specified, the all-zeros vector will be used.
time : float scalar, optional
The time at which any matrix evaluation done by this method will be performed. Do not set for DC or OP analysis, must be set for a transisent analysis. Notice that \(t=0\) is not the same as DC!
MAXIT : int, optional
The maximum number of Newton Rhapson iterations to be performed before giving up. If unset, options.dc_max_nr_iter is used.
locked_nodes : list of tuples, optional
The nodes that need to have a well behaved, slowly varying voltage applied. Typically they control non-linear elements. This is generated by ahkab.circuit.Circuit.get_locked_nodes() and it will be generated for you if left unset. However, if you are doing many simulations of the same circuit (as it happens in a transient analysis), it’s a good idea to generate it only once.
skip_Tt : boolean, optional
Do not build the \(T_t(t)\) vector. Defaults to False.
verbose : int, optional
The verbosity level. From 0 (silent) to 6 (debug). Defaults to 3.


x : ndarray
The solution, if found.
error : ndarray
The error associated with each solution item, if it was found.
converged : boolean
A flag set to True when convergence was detected.
tot_iterations : int
Total number of NR iterations run.
generate_mna_and_N(circ, verbose=3)[source]

Generate the full unreduced MNA and N matrices required for an MNA analysis

We wish to solve the linear stationary MNA problem:

\[MNA \cdot x + N = 0\]

If nv is the number of nodes in the circuit, MNA is a square matrix composed by:

  • MNA[:nv, :], KCLs ordered by node, from node 0 up to node nv.

In the above submatrix, we have a voltage part: MNA[:nv, :nv], where each term MNA[i, j] is due to the (trans-)conductances in between the nodes and a current part, MNA[:nv, nv:], where each term is due to a current variable introduced by elements whose current flow is not univocally defined by the voltage applied to their port(s).

  • MNA[nv:, :] are the KVL equations introduced by the above terms.

N is similarly partitioned, but it is a vector of size (nv,).


circ : circuit instance
The circuit for which the matrices are to be computed.
verbose : int, optional
The verbosity, from 0 (silent) to 6 (debug).


MNA, N : ndarrays
The MNA matrix and constant term vector computed as per above.

Get all the available solving methods

We have the standard solving method and two homotopies available. The homotopies are \(G_{min}\) stepping and source stepping.

Solving methods may be enabled and disabled through the options values:

  • options.use_standard_solve_method,
  • options.use_gmin_stepping,
  • options.use_source_stepping.


standard_solving, gmin_stepping, source_stepping : dict
The dictionaries contain the options and the status of the methods.
get_td(dx, locked_nodes, n=-1)[source]

Calculates the damping coefficient for the Newthon method.

The damping coefficient is choosen as the lowest between:

  • the damping required for the first NR iterations, a parameter which is set through the integer options.nr_damp_first_iters.
  • If options.nl_voltages_lock evaluates to True, the biggest damping factor that keeps the change in voltage across the locked nodes pairs less than the maximum variation allowed, set by: (options.nl_voltages_lock_factor * Vth)
  • Unity.


dx : ndarray
The undamped increment returned by the NR solver.
locked_nodes : list
A vector of tuples of (internal) nodes that are a port of a non-linear component.
n : int, optional
The NR iteration counter


If n is set to -1 (or any negative value), td is independent from the iteration number and options.nr_damp_first_iters is ignored.


td : float
The damping coefficient.
mdn_solver(x, mna, circ, T, MAXIT, nv, locked_nodes, time=None, print_steps=False, vector_norm=<function <lambda>>, debug=True)[source]

Solves a problem like F(x) = 0 using the Newton Algorithm with a variable damping.


\[F(x) = mna*x + T + T(x)\]
  • \(mna\) is the Modified Network Analysis matrix of the circuit
  • \(T(x)\) is the contribute of nonlinear elements to KCL
  • \(T\) contains the contributions of the independent sources, time
  • invariant and linear

If \(x(0)\) is the initial guess, every \(x(n+1)\) is given by:

\[x(n+1) = x(n) + td \cdot dx\]

Where \(td\) is a damping coefficient to avoid overflow in non-linear components and excessive oscillation in the very first iteration. Afterwards \(td=1\) To calculate \(td\), an array of locked nodes is needed.

The convergence check is done this way:


x : ndarray
The initial guess. If set to None, it will be initialized to all zeros. Specifying a initial guess may improve the convergence time of the algorithm and determine which solution (if any) is found if there are more than one.
mna : ndarray
The Modified Network Analysis matrix of the circuit, reduced, see above.
circ : circuit instance
The circuit instance.
T : ndarray,
The \(T\) vector described above.
MAXIT : int
The maximum iterations that the method may perform.
nv : int
Number of nodes in the circuit (counting the ref, 0)
locked_nodes : list of tuples
A list of ports driving non-linear elements, generated by circ.get_locked_nodes()
time : float or None, optional
The value of time to be passed to non_linear _and_ time variant elements.
print_steps : boolean, optional
Show a progress indicator, very verbose. Defaults to False.
vector_norm : function, optional
An R^N -> R^1 function returning the norm of a vector, for convergence checking. Defaults to the maximum norm, ie \(f(x) = max(|x|)\),
debug : int, optional
Debug flag that will result in an array being returned containing node-by-node convergence information.


sol : ndarray
The solution.
err : ndarray
The remaining error.
converged : boolean
A boolean that is set to True whenever the method exits because of a successful convergence check. False whenever convergence problems where found.
N : int
The number of NR iterations performed.
convergence_by_node : list
If debug was set to True, this list has the same size of the MNA matrix and contains the information regarding which nodes fail to converge in the circuit. Ie. if convergence_by_node[j] == False, node j has a convergence problem. This may significantly help debugging non-convergent circuits.
modify_x0_for_ic(circ, x0)[source]

Modifies a supplied x0.

Several circut elements allow the user to set their own Initial Conditions (IC) for either voltage or current, depending on what is appropriate for the element considered.

This method, receives a preliminary x0 value, typically computed by an OP analysis and goes through the circuit, looking for ICs and setting them in x0.

Notice it is possible to require ICs that are incompatible with each other – for example supplying different ICs to two parallel caps. In that case we try to accommodate the user’s requirements in a non-strict best-effort kind of way: for this reason, whenever multiple ICs are specified, it is best to visually inspect x0 to check that what you would have expected is indeed what you got.


circ : circuit instance
The circuit in which the ICs are specified.
x0 : ndarray or results.op_solution
The initial value to be modified


x0p : ndarray or results.op_solution
The modified x0. Notice that we return the same kind of object as it was supplied. Additionally, the results.op_solution is a new instance, while the ndarray is simply the original array modified.
more_solve_methods_available(standard_solving, gmin_stepping, source_stepping)[source]

Are there more solving methods available?


standard_solving, gmin_stepping, source_stepping : dict
The dictionaries contain the options and the status of the methods.


rsp : boolean
The answer.
op_analysis(circ, x0=None, guess=True, outfile=None, verbose=3)[source]

Runs an Operating Point (OP) analysis


circ : Circuit instance
The circuit instance on which the simulation is run
x0 : op_solution instance or ndarray, optional
The initial guess to be used to start the NR mdn_solver().
guess : boolean, optional
If set to True (default) and x0 is None, it will generate a ‘smart’ guess to use as x0.
verbose : int
The verbosity level from 0 (silent) to 6 (debug).


A result.op_solution instance, if successful, None otherwise.

set_next_solve_method(standard_solving, gmin_stepping, source_stepping, verbose=3)[source]

Select the next solving method.

We have the standard solving method and two homotopies available. The homotopies are \(G_{min}\) stepping and source stepping.

They will be selected and enabled when failures occur according to the options values:

  • options.use_standard_solve_method,
  • options.use_gmin_stepping,
  • options.use_source_stepping.

The methods will be used in the order above.

The inputs to this method are three dictionaries that keep track of which method is currently enabled and which ones has failed in the past.


standard_solving, gmin_stepping, source_stepping : dict
The dictionaries contain the options and the status of the methods, they should be the values provided by get_solve_methods().
verbose : int, optional
The verbosity level, from 0 (silent) to 6 (debug).


standard_solving, gmin_stepping, source_stepping : dict
The updated dictionaries.