MaximeVH / EquivalentCircuits.jl

A julia package to either fit the parameters of a specified equivalent electrical circuit to electrochemical impedance data, or to suggest a plausible circuit configuration for a given set of measurements (either through a comparison of circuits from the literature, or through an evolutionary algorithm approach).
MIT License
22 stars 6 forks source link

EquivalentCircuits() fails to optimize given equivalent circuit #11

Closed StefanPofahl closed 1 year ago

StefanPofahl commented 1 year ago

I observed, that EquivalentCircuits() fails to optimize the parameters of a given equivalent circuit model (string-representation). Therefore, I combine the Python-package impedance_py with the Julia package: EquivalentCircuits.jl. I have uploaded my example on my fork of the: EquivalentCircuits-GitHubRepository: combine_impedance_py_and_EquivalentCircuits.jl

StefanPofahl commented 1 year ago

Today I have tested the new version of the master-repository and compared the fit result with the one optimized with the Gamry SW: The script can be found on my fork of this project: https://github.com/StefanPofahl/EquivalentCircuits.jl/blob/0f77c59ab71c04d2600db160766fbd4cbe237e2e/examples/comparison_EC_Gamry_Fit_against_GenricAlgorithm_Fit.jl

MaximeVH commented 1 year ago

I have written a script that evaluates a number of optimisation methods for this specific complex equivalent circuit setting.

The script can be found at: https://github.com/MaximeVH/EquivalentCircuits.jl/blob/master/examples/EC_optimisation_experiment.jl

MaximeVH commented 1 year ago

selected_methods_nyquists_ selected_methods_error_plot

StefanPofahl commented 1 year ago

Hi Maxime! - Thanks for your nice script! To enable functionality in my Julia-environment I have reworked your example, see here: EC_optimisation_experiment_Maxime.jl I have added two optional parameters to the function trace_quality() to enable a weighting of the impedance points. Do you think, that this makes sense? Is it possible to pass a weighing vector also to the optimization function: optimize()?

And I have added the feature to select only a part of the measured impedance points for optimization.

Another more general question: Is there also an optimization solver available that uses genetic or evolutionary algorithms for a local stochastic optimization strategy?

MaximeVH commented 1 year ago

Hi Stefan,

I think weighing the impedance points makes sense in cases where some areas within the impedance spectrum's frequency range are of greater importance than others. If the low frequencies are less important for the circuit you are fitting, it makes sense to allocate a lower weight to them in the fitting quality evaluation and during the optimization of the parameters.

If you want to add weights at certain frequencies during optimisation as well, you can do this by adjusting the objective function in the ObjectiveFunction.jl source file. The current objective function is implemented as

function objectivefunction(circuitfunc,measurements,frequencies) 
    function objective(x)
        model_output = [circuitfunc(x,fr) for fr in frequencies] 
        return  mean((abs.(measurements - model_output).^2)./(abs.(measurements).^2 .+ abs.(model_output).^2))
    end
    return objective
end

Weights can be included in this function as follows:

function objectivefunction_weighted(circuitfunc,measurements,frequencies,weights) 
    function objective(x)
        model_output = [circuitfunc(x,fr) for fr in frequencies] 
        return  mean(weights .* (abs.(measurements - model_output).^2)./(abs.(measurements).^2 .+ abs.(model_output).^2))
    end
    return objective
end

Here the weights input is a vector of the same dimension as the frequencies input, where each value is a measure of how much importance is attached to the impedance point at the corresponding frequency. Note that you will also have to adjust the parameter optimisation function such that the modified objective function is used and the weights are included as input.

As for your last question, I have not used such modules before, maybe the Evolutionary.jl package will have what you're looking for.

Regards, Maxime

StefanPofahl commented 1 year ago

Maxime, thanks for the quick replay! I would not change the name of the function, but would specify the weighing factor as optional, what do you think?

MaximeVH commented 1 year ago

That's fine by me, I can include the relevant optional arguments to the objectivefunction and parameteroptimisation functions, along with the other updates.

StefanPofahl commented 1 year ago

That sounds promising :-) P.S.: I definitely will have a look on the Evolutionary.jl-package!

StefanPofahl commented 1 year ago

The weighting works perfect, please have a look: https://github.com/StefanPofahl/EquivalentCircuits.jl/blob/9091feb7aa118289137bcaaa88fdf40a59716607/examples/EC_optimisation_experiment_Maxime.jl

StefanPofahl commented 1 year ago

Info: