Search code examples
pythonoptimizationsimulationlmfitcircuit-diagram

LMFIT - Creating models for working with electrical circuit models


I have an analytical model of an electrical circuit, the output of which is a transfer function in the s-domain, I am using LMFIT to fit the values of the components of the circuit to get the resulting TF to best fit a measured data-set.

I want to make my code more modular, and I thought a good way to do it would be something like the following;

import lmfit as lm

z1 = lm.models.ExpressionModel('r1 + l1 * x')

z2 = lm.models.ExpressionModel('1 / (c1 * x)')

rlc = z1 * z2 / (z1 + z2)

where x in this case is s, the complex independent variable of the form; s = 2 * pi * j * f

I know that in order to fit complex data you need to wrap the function in one which produces a real output, but in the end when fitting the resulting CompositeModel I will be taking the magnitude (abs()) of the TF, I just want to know;

Question 1:

Can lmfit.models.ExpressionModel handle complex expressions? (as in containing a complex variable)

..and;

Question 2 (optional):

If not, can I do this with lmfit.Model instead? ie. just defining an objective function which returns a complex value and then feeding it to lmfit.Model like so;

def _z1(s, r1, l1):
    return r1 + l1 * s

z1 = lm.Model(_z1)

It says in the documentation for lmfit.Model that it automatically generates the residual function, but does this ever get used if I only ever fit the resulting composite model using my own residual function?

and also;

Question 3:

Would this be ill-advised? ie. would it result in significant overhead?


Solution

  • I believe that ExpressionModel() would work with complex values. But I would still recommend your approach in Q2: define a function that does the work, as it will be easier to trouble-shoot and expand when needed.

    And, yes, the independent data and calculation done can definitely include complex values. But, while your calculation may be complex, the returned residual needs to be purely real. The simplest way to do this is to return real/imag pairs using the view method of the numpy array:

    return complex_array_for_residual.view(np.float)
    

    You could consider returning mag/phase pairs, though it is slightly more complicated, as you have to deal with phase jumps in phase.

    Just to be clear, I regularly fit using Fourier transforms that give complex results, and just turn that into "real/imag" pairs with ndarray.view.