Features:
- Linear oscillator, Duffing oscillator, oscillator in ballistic B-phase
- Fitting of X and Y data components vs frequency
- Fitting of coordinate or velocity response
- Optional fit parameters for constant and linear backgrounds
- A single fit of multiple frequency sweeps and multiple drives is possible
- Internal data scaling (to have all parameters of the order of 1 during fitting)
- Non-linear resonance proccessing according with
Comments and suggestions are very welcome!
V.Zavjalov (vl.zavjalov at gmail dot com), 04.05.2024
Basic usage:
Do the fit and return result object:
fit = fit_res.fit_lin(FF,XX,YY, <parameters>)
Calculate model function:
vv = fit.func(ff)
Get resonance frequency, width, and amplitude:
f0 = fit.get_f0(ff)
df = fit.get_df(ff)
amp = fit.get_amp(ff)
FF
-- Frequency array, numpy arrayXX,YY
-- X and Y data components, numpy arraysDD=1
-- Drive array or number. Use to fit multiple drives simultaneouslycoord=1
-- switch between coordinate/velocity response (1i*f
factor)cbg0=1
-- use background idependent on both drive and freqeuncy (2 extra parameters)cbg=1
-- use background proportional to drive and independent on frequency (2 extra parameters)lbg=1
-- use background proportional to drive and linear in frequency (2 extra parameters)do_fit=1
-- do actual fitting or return initial conditions (useful for tests)weight_pwr=0
-- Use 1/hypot(XX,YY)^weight_pwr as a fitting weight. Default is 0, use 1 or 2 if accurate fitting of background is requiredfit_displ=None, fit_maxiter=10000
-- parameters passed toscipy.optimize.minimize
No not use cbg0
and cbg
together unless you have multiple drives in your data
- Coordinate response:
XX+iYY = amp*DD * df*f0 / (f0^2 - FF^2 + i*FF*df) [+cbg0] [+cbg*DD] [+lbg*DD*(FF-f0)]
- Velocity response:
XX+iYY = amp*DD * i*df*FF / (f0^2 - FF^2 + i*FF*df) [+cbg0] [+cbg*DD] [+lbg*DD*(FF-f0)]
where
amp
-- complex amplitude per unit drive (same units asXX/DD
andYY/DD
)f0, df
-- resonance frequency and width (same units asFF
)cbg0
-- complex constant drive-independent background per unit drive (only ifcbg0=1
)cbg
-- complex constant background per unit drive (only ifcbg=1
)lbg
-- complex linear background per unit drive (only iflbg=1
)
For each parameter two functions are defined:
-
get_<name>(p=None)
-- extract value of parameter<name>
from parameter arrayp
(if not None) or from fit result. -
get_<name>_e(e=None)
-- extract uncertainty of parameter<name>
from uncertainty arraye
(if not None) or from the fit result.
-
fit.func(FF,DD=1,p=None)
-- calculate the model function for parameter arrayp
(if not None) or for the fit result. -
fit.func_bg(FF,DD=1,p=None)
-- calculate background part of the model function.
fit = fit_res.fit_duff(FF,XX,YY, <parameters>)
All parameters and usage is same as for the linear oscillator. The only difference is
the extra fitting parameter a
which is a factor in the Duffing force F = -a*x^3
.
Additional functions: get_a
, get_a_e
.
Some notes:
-
If frequency units are switched from rad/s to Hz then units of
A
changes as well. Also note amplitude and drive units for understanding quantitative value ofA
. -
The response with hysteresis should be fitted properly (see examples below), order of the frequency array is important for calculations.
-
In real life you can rarely observe pure Duffing systems, in many cases this fit will be useful only for small non-linearities.
fit = fit_res.fit_bphase(FF,XX,YY, <parameters>)
All parameters and usage is same as for the linear oscillator. The only difference is
the extra fitting parameter v0
which is a characteristic velocity (in XX and YY units).
Additional functions: get_v0
, get_v0_e
.
For the non-linear damping function the following approximation is used:
df_n(|v|) = df / (1 + 0.447*(|v|/v0)**1.16)
fit = fit_res.fit_nonlin(FF,XX,YY, ffunc=None, dfunc=None, <parameters>)
Use arbitrary non-linear functions f0n(|x|)
and dfn(|v|)
.
The functions can be calculated as integral transforms of the non-linear force acting on the oscillator using following interface:
transform(func, x, npts=100, dfunc0=None)
itransform(func, dfunc, x, npts=100)
See the PDF note for the theory
fit = fit_res.fit_nonlin1(FF,XX,YY, )
Use 1st-order non-linear functions:
f0n(|x|) = f0 (1 + a|x|)
dfn(|v|) = df0 (1 + b|x|)
with two additional fitting parameters, a
and b
.
See examples
folder.
As experimental data I use measurements of vibrating wires done during my work in Lancaster University. I have chosen data which can illustrate linear/duffing/B-phase behaviour, but one can not expect exact match with theoretical models.
Example 1: Linear oscillator, multiple frequency sweeps at a single drive
Example 2: Linear oscillator, multiple frequency sweeps at multiple drives
Example 3: Duffing oscillator, multiple frequency sweeps at multiple drives. Note that experimental system is not exactly a Duffing oscillator, perfect match is not expected.
Example 4: Oscillator in ballistic B-phase, multiple frequency sweeps at multiple drives.
Example 5: Same data as in the example 4.
Examples 6 and 7: more realistic examples for the note (see below)
Example 8: Oscillator with arbitrary 1st-order non-linear functions
https://github.com/slazav/fit_res
-- my c++ command line filter for fitting linear resonances.- fit_res modules in
https://github.com/slazav/exp_py
-- older version of the same code. https://raw.githubusercontent.com/slazav/py_fit_res/master/fit_res_note.pdf
-- the note with some theory and examples :