How to fix a parameter when fitting with optimize.curve_fit? - scipy

I would like to fix a parameter using optimize.curve_fit.
This is my code:
def sinefunction2(x, a, b, c, phi, omega):
return a+ b * np.sin(omega*x + phi) + c*(np.sin(omega*x + phi))**2
phi = how to fix?
x= scan_no
y= fwhm_r
p0=[0.05, 0.1, 0.01, phi, 0.12]
params2, params_covariance2 = optimize.curve_fit(sinefunction2, scan_no, \
fwhm_r, p0, sigma=error_r, absolute_sigma=True)
How can I fix phi param?
Thank you.

Just include it as a variable inside your function and set it to a constant value, so that every time the function is called, it will have that value. Also, remove phi as an argument from the function.
def sinefunction2(x, a, b, c, omega):
phi = 1 # for e.g.
return a + b * np.sin(omega*x + phi) + c*(np.sin(omega*x + phi))**2
x= scan_no
y= fwhm_r
p0=[0.05, 0.1, 0.01, 0.12]
params2, params_covariance2 = optimize.curve_fit(sinefunction2, scan_no, fwhm_r, p0,
sigma=error_r, absolute_sigma=True)

Related

Solving a 4 ODE system in MATLAB using ode45

I am not very used to MATLAB and I'm trying to solve the following problem using MATLAB ode45, however, it's not working.
I was working on a problem in reaction engineering, using a Semi-Batch Reactor.
The reaction is given by
A + B ---> C + D
A is placed in the reactor and B is being continuously added into the reactor with a flowrate of v0 = 0.05 L/s. Initial volume is V0 = 5 L. The reaction is elementary. The reaction constant is k = 2.2 L/mol.s.
Initial Concentrations: for A: 0.05 M, for B: 0.025 M.
Performing a mole balance of each species in the reactor, I got the following 4 ODEs, and the expression of V (volume of the reactor is constantly increasing)
Solving this system and plotting the solution against time, I should get this
Note that plots of C(C) and C(D) are the same.
And let's set tau = v0/V.
Now for the MATLAB code part.
I have searched extensively online, and from what I've learned, I came up with the following code.
First, I wrote the code for the ODE system
function f = ODEsystem(t, y, tau, ra, y0)
f = zeros(4, 1);
f(1) = ra - tau*y(1);
f(2) = ra + tau*(y0(2) - y(2));
f(3) = -ra - tau*y(3);
f(4) = -ra - tau*y(4);
end
Then, in the command window,
t = [0:0.01:5];
v0 = 0.05;
V0 = 5;
k = 2.2;
V = V0 + v0*t;
tau = v0./V;
syms y(t);
ra = -k*y(1)*y(2);
y0 = [0.05 0.025 0 0];
[t, y] = ode45(#ODEsystem(t, y, tau, ra, y0), t, y0);
plot(t, y);
However, I get this...
Please if anyone could help me fix my code. This is really annoying :)
ra should not be passed as parameter but be computed inside the ODE system. V is likewise not a constant. Symbolic expressions should be used for formula transformations, not for numerical methods. One would also have to explicitly evaluate the symbolic expression at the wanted numerical values.
function f = ODEsystem(t, y, k, v0, V0, cB0)
f = zeros(4, 1);
ra = -k*y(1)*y(2);
tau = v0/(V0+t*v0);
f(1) = ra - tau*y(1);
f(2) = ra + tau*(cB0 - y(2));
f(3) = -ra - tau*y(3);
f(4) = -ra - tau*y(4);
end
Then use the time span of the graphic, start with all concentrations zero except for A, use the concentration B only for the inflow.
t = [0:1:500];
v0 = 0.05;
V0 = 5;
k = 2.2;
cB0 = 0.025;
y0 = [0.05 0 0 0];
[t, y] = ode45(#(t,y) ODEsystem(t, y, k, v0, V0, cB0), t, y0);
plot(t, y);
and get a good reproduction of the reference image

Error while evaluating the function convolution

This is my first attempt to write anything in matlab, so please, be patient.
I am trying to evaluate the solution of the following ODE: w'' + N(w, w') = f(t) with the Cauchy conditions w(0) = w'(0) = 0. Here N is a given nonlinear function, f is a given source. I also need the function
where G is the solution of the following ODE:
where G(0) = G'(0) =0, s is a constant, and
My try is as follows: I define N, f, w and G:
k = 1000;
N = #(g1,g2) g1^2 + sin(g2);
f = #(t) 0.5 * (1 + tanh(k * t));
t = linspace(0, 10, 100);
w = nonlinearnonhom(N, f);
G = nonlinearGreen(N);
This part is ok. I can plot both w and G: both seems to be correct. Now, I want to evaluate wG. For that purpose, I use the direct and inverse Laplace transforms as follows:
wG = ilaplace(laplace(G, t, s) * laplace(f, t, s), s, t);
but is says
Undefined function 'laplace' for input arguments of type 'double'.
Error in main (line 13)
wG = ilaplace(laplace(G, t, s) * laplace(f, t, s), s, t);
Now, I am not sure if this definition of wG is correct at all and if there are not any other definitions.
Appendix: nonlinearGreen(N) is defined as follows:
function G = nonlinearGreen(N)
eps = .0001;
del = #(t)[1/(eps * pi) * exp( -t^2/eps^2)];
eqGreen = #(t, g)[g(2); - N(g(1),g(2)) + del(t)];
tspan = [0, 100];
Cc = [0, 0];
solGreen = ode45(eqGreen, tspan, Cc);
t = linspace(0, 10, 1000);
G = deval(solGreen, t, 1);
end
and nonlinearnonhom is defined as follows:
function w = nonlinearnonhom(N, f)
eqnonhom = #(t, g)[g(2); - N(g(1),g(2)) + f(t)];
tspan = [0, 100];
Cc = [0, 0];
solnonhom = ode45(eqnonhom, tspan, Cc);
t = linspace(0, 10, 100);
w = deval(solnonhom, t, 1);
end
You keep mixing different kind of types and it's not a good idea. I suggest you keep with symbolic all the way if you want to use the laplace function. When you define N and f with #(arobase) as function handles and not symbolic expressions as you might want to do. I suggest you have a look at symbolic documentation and rewrite your functions as symbolic.
Then, the error message is pretty clear.
Undefined function 'laplace' for input arguments of type 'double'.
Error in main (line 13)
wG = ilaplace(laplace(G, t, s) * laplace(f, t, s), s, t);
It means that the function laplace can't have arguments of type double.
The problem is that your t is a vector of double. Another mistake is that s is not defined in your code.
According to Matlab documentation of laplace, all arguments are of type symbolic.
You can try to manually specify symbolic s and t.
% t = linspace(0, 10, 100); % This is wrong
syms s t
wG = ilaplace(laplace(G, t, s) * laplace(f, t, s), s, t);
I have no error after that.

Solving for a variable in matlab

I have a system of two equations and I need Matlab to solve for a certain variable. The problem is the variable I need is inside an expression, and trig functions. I wrote the following code:
function [ V1, V2 ] = find_voltages( w1, l1, d, w2, G1, G2, m, v, e, h, a, x)
k1 = sqrt((2*V1*e)/(G1^2*m*v^2));
k2 = sqrt((2*V2*e)/(G2^2*m*v^2));
A = h + l1*a;
b = -A*k1*sin(k1*w1) + a*cos(k1*w1);
B = A*cos(k1*w1) + (a/k1)*sin(k1*w1);
C = B + a*b;
c = C*k2*sinh(k2*w2) + b*cosh(k2*w2);
D = C*cosh(k2*w2) + (b/k2)*sinh(k2*w2);
bd = A*k1*sinh(k1*w1) + a*cosh(k1*w1);
Bd = A*cosh(k1*w1) + (a/k1)*sinh(k1*w1);
Cd = Bd + a*bd;
cd = -Cd*k2*sin(k2*w2) + bd*cos(k2*w2);
Dd = Cd*cos(k2*w2) + (bd/k2)*sin(k2*w2);
fsolve([c*(x-(l1+w1+d+w2)) + D == 0, cd*(x-(l1+w1+d+w2)) + Dd == 0], [V1,V2])
end
and got an error because V1 and V2 are not defined. They are part of an expression, and need to be solved for. Is there a way to do this? Also, is it a problem that the functions I put as arguments to solve are conglomerates of the smaller equations above them?
Valid values:
Drift space 1 (l1): 0.11
Quad 1 length (w1): 0.11
Quad 2 length (w2): 0.048
Separation (d): 0.014
Radius of Separation 1 (G1): 0.016
Radius of Separation 2 (G2): 0.01
Voltage 1 (V1): -588.5
Voltage 2 (V2): 418
Kinetic Energy in eV: 15000
Mass (m) 9.109E-31
Kinetic Energy in Joules (K): 2.4E-15
Velocity (v): 72591415.94
Charge on an Electron (e): 1.602E-19
k1^2=(2*V1*e)/(G1^2*m*v^2): 153.4467773
k2^2=(2*V2*e)/(G2^2*m*v^2): 279.015
First start by re-writing your function as an expression which returns the extent to which your function(s) fail to hold for some valid guess for [V1,V2]. E.g.,
function gap = voltage_eqn(V, w1, l1, d, w2, G1, G2, m, v, e, h, a, x)
V1 = V(1) ;
V2 = V(2) ;
k1 = sqrt((2*V1*e)/(G1^2*m*v^2));
k2 = sqrt((2*V2*e)/(G2^2*m*v^2));
A = h + l1*a;
b = -A*k1*sin(k1*w1) + a*cos(k1*w1);
B = A*cos(k1*w1) + (a/k1)*sin(k1*w1);
C = B + a*b;
c = C*k2*sinh(k2*w2) + b*cosh(k2*w2);
D = C*cosh(k2*w2) + (b/k2)*sinh(k2*w2);
bd = A*k1*sinh(k1*w1) + a*cosh(k1*w1);
Bd = A*cosh(k1*w1) + (a/k1)*sinh(k1*w1);
Cd = Bd + a*bd;
cd = -Cd*k2*sin(k2*w2) + bd*cos(k2*w2);
Dd = Cd*cos(k2*w2) + (bd/k2)*sin(k2*w2);
gap(2) = c*(x-(l1+w1+d+w2)) + D ;
gap(1) = cd*(x-(l1+w1+d+w2)) + Dd ;
end
Then call fsolve from some initial V0:
Vf = fsolve(#(V) voltage_eqn(V, w1, l1, d, w2, G1, G2, q, m, v, e, h, a, x), V0) ;

Symbolic int results in deletion of main variable (MATLAB)

I am trying to do something like this:
syms x h4 t4 c13;
t = 0.6*sin(pi*x);
h1x = 0.5*(1 - t);
h0 = h1x;
h14x = -h4 -t4*(x - 0.5);
h24x = h4 + t4*(x - 0.5);
symvar(h14x)
which returns
ans =
[ h4, t4, x]
Then
u13x = (-4*int(h14x, x, 0, x) + c13)/h0
symvar(u13x)
returns
u13x =
-(c13 + 4*x*(h4 - t4/2) + 2*t4*x^2)/((3*sin(pi*x))/10 - 1/2)
ans =
[ c13, h4, t4, x]
and
p12x = -3*int(u13x, x, 0, x)
symvar(p12x)
which is
p12x =
-3*int(-(c13 + 4*x*(h4 - t4/2) + 2*t4*x^2)/((3*sin(pi*x))/10 - 1/2), x, 0, x)
ans =
[ c13, h4, t4 ]
As you can see from u13x where the variables were [h4, t4, c13, x], while integrating to p12x it got reduced to [h4, t4, c13] even though the integral limits are variable (in terms of x). Is it a bug? I can't seem to get by this weird behaviour. Is there a workaround?
Here are three possible workarounds (tested in R2015a).
1. Use a symbolic function
One option is to make the input that will be passed to sym/symvar a symfun in terms of x and then use the optional second argument to specify a finite number of variable to look for:
syms x h4 t4 c13;
t = 0.6*sin(pi*x);
h1x = 0.5*(1 - t);
h0 = h1x;
h14x = -h4 -t4*(x - 0.5);
u13x = (-4*int(h14x, x, 0, x) + c13)/h0
p12x(x) = -3*int(u13x, x, 0, x) % Make symfun, function of x
n = realmax; % 4 or greater to get all variables in this case
symvar(p12x, n) % Second argument must be finite integer
which returns the expected [ x, t4, h4, c13]. Just setting the second argument to a very large integer value seems to work.
2. Convert expression to a string
There are actually two versions of symvar. There is symvar for string inputs and sym/symvar, in the Symbolic Math toolbox, for symbolic expressions. The two forms apparently behave differently in this case. So, another workaround is to convert the equation with int to a character string with sym/char before passing it to symvar and then converting the output back to a vector of symbolic variables:
syms x h4 t4 c13;
t = 0.6*sin(pi*x);
h1x = 0.5*(1 - t);
h0 = h1x;
h14x = -h4 -t4*(x - 0.5);
u13x = (-4*int(h14x, x, 0, x) + c13)/h0
p12x = -3*int(u13x, x, 0, x)
sym(symvar(char(p12x))).'
which also returns the expected [ c13, h4, t4, x] (note that order appears to be opposite of the first workaround above).
3. Call MuPAD function from Matlab
Lastly, you can call the MuPAD function indets that finds indeterminates in an expression.
syms x h4 t4 c13;
t = 0.6*sin(pi*x);
h1x = 0.5*(1 - t);
h0 = h1x;
h14x = -h4 -t4*(x - 0.5);
u13x = (-4*int(h14x, x, 0, x) + c13)/h0
p12x = -3*int(u13x, x, 0, x)
feval(symengine, 'x->indets(x) minus Type::ConstantIdents', p12x)
which returns [ x, c13, h4, t4]. This will work if the input p12x is class sym or symfun. You can also use:
evalin(symengine, ['indets(hold(' char(p12x) ')) minus Type::ConstantIdents'])
The reason that sym/symvar doesn't work in your case is because it is based on freeIndets under the hood, which explicitly ignores free variables in functions like int.

Passing matrix to function handle (MATLAB)

If I have a set of functions
f = #(x1,x2) ([x1 + x2; x1^2 + x2^2])
and I have a second matrix with
b = [x1,x2]
How do I evaluate f([b])? The only way I know how is to say f(b(1),b(2)) but I can't figure out how to automate that because the amount of variables could be up to n. I'm also wondering if there is a better way than going individually and plugging those in.
convertToAcceptArray.m:
function f = convertToAcceptArray(old_f)
function r = new_f(X)
X = num2cell(X);
r = old_f(X{:});
end
f = #new_f
end
usage.m:
f = #(x1,x2) ([x1 + x2; x1^2 + x2^2])
f2 = convertToAcceptArray(f);
f2([1 5])
You could rewrite your functions to take a vector as an input.
f = #(b)[b(1) + b(2); b(1)^2 + b(2)^2]
Then with, e.g., b=[2 3] the call f(b) gives [2+3; 2^2+3^2]=[5; 13].
Assuming that b is an N-by-2 matrix, you can invoke f for every pair of values in b as follows:
cell2mat(arrayfun(f, b(:, 1), b(:, 2), 'UniformOutput', 0)')'
The result would also be an N-by-2 matrix.
Alternatively, if you are allowed to modify f, you can redefine it to accept a vector as input so that you can obtain the entire result by simply calling f(b):
f = #(x)[sum(x, 2), sum(x .^ 2, 2)]