So pretty much, I am aiming to achieve a function f(x)
My problem is that my function has an integral in it, and I only know how to construct definite integrals, so my question is how does one create an indefinite integral in a function (or there may be some other method I am currently unaware of)
My function is defined as :
(G is gravitational constant, although you can leave G out of your answer for simplicity, I'll add it in my code)
Here is the starting point, but I don't know how to do the integral portion
import numpy as np
def f(x):
rho = 5*(1/(1+((x**2)/(3**2))))
function_result = rho * 4 * np.pi * x**2
return function_result
Please let me know if I need to elaborate on something.
EDIT-----------------------------------------------------
I made some major progress, but I still have one little error.
Pretty much, I did this:
from sympy import *
x = Symbol('x')
rho = p0()*(1/(1+((x**2)/(rc()**2))))* 4 * np.pi * x**2
fooply = integrate(rho,x)
def f(rx):
function_result = fooply.subs({x:rx})
return function_result
Which works fine when I plug in one number for f; however, when I plug in an array (as I need to later), I get the error:
raise SympifyError(a)
sympy.core.sympify.SympifyError: SympifyError: [3, 3, 3, 3, 3]
(Here, I did print(f([3,3,3,3,3]))). Usually, the function returns an array of values. So if I did f([3,2]) it should return [f(3),f(2)]. Yet, for some reason, it doesn't for my function....
Thanks in advance
how about:
from sympy import *
x, p0, rc = symbols('x p0 rc', real=True, positive=True)
rho = p0*(1/(1+((x**2)/(rc))))* 4 * pi * x**2
fooply = integrate(rho,x)/x
rho, fooply
(4*pi*p0*x**2/(1 + x**2/rc),
4*pi*p0*rc*(-sqrt(rc)*atan(x/sqrt(rc)) + x)/x)
fooply = fooply.subs({p0: 2.0, rc: 3.0})
np_fooply = lambdify(x, fooply, 'numpy')
print(np_fooply(np.array([3,3,3,3,3])))
[ 29.81247362 29.81247362 29.81247362 29.81247362 29.81247362]
To plug in an array to a SymPy expression, you need to use lambdify to convert it to a NumPy function (f = lambdify(x, fooply)). Just using def and subs as you have done will not work.
Also, in general, when using symbolic computations, it's better to use sympy.pi instead of np.pi, as the former is symbolic and can simplify. It will automatically be converted to the numeric pi by lambdify.
Related
I'm a student of mechanical engineering, and this is the first year I've met with the Python environment, or the distribution of it Anaconda.
I was given a task to find the zeroes of this function:
π·β
sin(πΌ)cos(πΌ)+πβ
cos(πΌ)sin(πΌ)2βπβ
cos(πΌ)βββ
sin(πΌ)=0
With the parameters:
D = 220mm,
h = 1040mm,
l = 1420mm,where
n = 81
is the number of equally distanced points on the function
and the function is limited to :
πΌβ[0,2π] where πΌ is a np.array.
plotted function
The issue is, when I try to insert the function in bisect(fun, a, b), the error says
'numpy.ndarray' object is not callable
Can someone aid a noob programer ? Thanks.
The question is not clear, you should share your code and the title should say scipy, not simpy, if I am correct.
Apart from this, I do not get the same plot of the function, can you check if it is correct?
If you want to use the bisection method you should do something like this:
import numpy as np
from scipy.optimize import bisect
def fun(x, D, h, l):
return D * np.sin(x) * np.cos(x) + l * np.cos(x) * np.sin(x) * 2 - l * np.cos(x) - h * np.sin(x)
D = 220
h = 1040
l = 1420
print(bisect(lambda x: fun(x, D, h, l), 0, 2*np.pi))
Note that the bisection method only finds one zero, and this does not work at all because the two extremes of the function have the same sign. For this particular function, you could run the bisect in the intervals (0, pi) and (pi, 2pi) to find both zeros.
On another thread, I saw someone manage to integrate the length of a arc using mathematica.They wrote:
In[1]:= ArcTan[3.05*Tan[5Pi/18]/2.23]
Out[1]= 1.02051
In[2]:= x=3.05 Cos[t];
In[3]:= y=2.23 Sin[t];
In[4]:= NIntegrate[Sqrt[D[x,t]^2+D[y,t]^2],{t,0,1.02051}]
Out[4]= 2.53143
How exactly could this be transferred to python using the imports of numpy and scipy? In particular, I am stuck on line 4 in his code with the "NIntegrate" function. Thanks for the help!
Also, if I already have the arc length and the vertical axis length, how would I be able to reverse the program to spit out the original paremeters from the known values? Thanks!
To my knowledge scipy cannot perform symbolic computations (such as symbolic differentiation). You may want to have a look at http://www.sympy.org for a symbolic computation package. Therefore, in the example below, I compute derivatives analytically (the Dx(t) and Dy(t) functions).
>>> from scipy.integrate import quad
>>> import numpy as np
>>> Dx = lambda t: -3.05 * np.sin(t)
>>> Dy = lambda t: 2.23 * np.cos(t)
>>> quad(lambda t: np.sqrt(Dx(t)**2 + Dy(t)**2), 0, 1.02051)
(2.531432761012828, 2.810454936566873e-14)
EDIT: Second part of the question - inverting the problem
From the fact that you know the value of the integral (arc) you can now solve for one of the parameters that determine the arc (semi-axes, angle, etc.) Let's assume you want to solve for the angle. Then you can use one of the non-linear solvers in scipy, to revert the equation quad(theta) - arcval == 0. You can do it like this:
>>> from scipy.integrate import quad
>>> from scipy.optimize import broyden1
>>> import numpy as np
>>> a = 3.05
>>> b = 2.23
>>> Dx = lambda t: -a * np.sin(t)
>>> Dy = lambda t: b * np.cos(t)
>>> arc = lambda theta: quad(lambda t: np.sqrt(Dx(t)**2 + Dy(t)**2), 0, np.arctan((a / b) * np.tan(np.deg2rad(theta))))[0]
>>> invert = lambda arcval: float(broyden1(lambda x: arc(x) - arcval, np.rad2deg(arcval / np.sqrt((a**2 + b**2) / 2.0))))
Then:
>>> arc(50)
2.531419526553662
>>> invert(arc(50))
50.000031008458365
If you prefer a pure numerical approach, you could use the following barebones solution. This worked well for me given that I had two input numpy.ndarrays, x and y with no functional form available.
import numpy as np
def arclength(x, y, a, b):
"""
Computes the arclength of the given curve
defined by (x0, y0), (x1, y1) ... (xn, yn)
over the provided bounds, `a` and `b`.
Parameters
----------
x: numpy.ndarray
The array of x values
y: numpy.ndarray
The array of y values corresponding to each value of x
a: int
The lower limit to integrate from
b: int
The upper limit to integrate to
Returns
-------
numpy.float64
The arclength of the curve
"""
bounds = (x >= a) & (y <= b)
return np.trapz(
np.sqrt(
1 + np.gradient(y[bounds], x[bounds])
) ** 2),
x[bounds]
)
Note: I spaced the return variables out that way just to make it more readable and clear to understand the operations taking place.
As an aside, recall that the arc-length of a curve is given by:
I want to be able to use one function in another function, in this case the function nu in the expression for integrand. At the moment this is my code:
from trapezium import trap
import scipy as sp
# mass_enc returns the mass enclosed within a radius R
# nu is the density function
def mass_enc(q, R):
integrand = lambda r: 4 * sp.pi * r**2 * q(r)
return trap(integrand, 0, R, 100)
def nu(a):
return a
print(mass_enc(nu, 10))
However this seems messy - is there a better way to do it?
I currently have a system of odes with a time-dependent constant. E.g.
def fun(u, t, a, b, c):
x = u[0]
y = u[1]
z = u[2]
dx_dt = a * x + y * z
dy_dt = b * (y-z)
dz_dt = -x*y+c*y-z
return [dx_dt, dy_dt, dz_dt]
The constants are "a", "b" and "c". I currently have a list of "a"s for every time-step which I would like to insert at every time-step, when using the scipy ode solver...is this possible?
Thanks!
Yes, this is possible. In the case where a is constant, I guess you called scipy.integrate.odeint(fun, u0, t, args) where fun is defined as in your question, u0 = [x0, y0, z0] is the initial condition, t is a sequence of time points for which to solve for the ODE and args = (a, b, c) are the extra arguments to pass to fun.
In the case where a depends on time, you simply have to reconsider a as a function, for example (given a constant a0):
def a(t):
return a0 * t
Then you will have to modify fun which computes the derivative at each time step to take the previous change into account:
def fun(u, t, a, b, c):
x = u[0]
y = u[1]
z = u[2]
dx_dt = a(t) * x + y * z # A change on this line: a -> a(t)
dy_dt = b * (y - z)
dz_dt = - x * y + c * y - z
return [dx_dt, dy_dt, dz_dt]
Eventually, note that u0, t and args remain unchanged and you can again call scipy.integrate.odeint(fun, u0, t, args).
A word about the correctness of this approach. The performance of the approximation of the numerical integration is affected, I don't know precisely how (no theoretical guarantees) but here is a simple example which works:
import matplotlib.pyplot as plt
import numpy as np
import scipy as sp
import scipy.integrate
tmax = 10.0
def a(t):
if t < tmax / 2.0:
return ((tmax / 2.0) - t) / (tmax / 2.0)
else:
return 1.0
def func(x, t, a):
return - (x - a(t))
x0 = 0.8
t = np.linspace(0.0, tmax, 1000)
args = (a,)
y = sp.integrate.odeint(func, x0, t, args)
fig = plt.figure()
ax = fig.add_subplot(111)
h1, = ax.plot(t, y)
h2, = ax.plot(t, [a(s) for s in t])
ax.legend([h1, h2], ["y", "a"])
ax.set_xlabel("t")
ax.grid()
plt.show()
I Hope this will help you.
No, that is not possible in the literal sense of
"I currently have a list of "a"s for every time-step which I would like to insert at every time-step"
as the solver has adaptive step size control, that is, it will use internal time steps that you have no control over, and each time step uses several evaluations of the function. Thus there is no connection between the solver time steps and the data time steps.
In the extended sense that the given data defines a piecewise constant step function however, there are several approaches to get to a solution.
You can integrate from jump point to jump point, using the ODE function with the constant parameter for this time segment. After that use numpy array operations like concatenate to assemble the full solution.
You can use interpolation functions like numpy.interp or scipy.interpolate.interp1d. The first gives a piecewise linear interpolation, which may not be desired here. The second returns a function object that can be configured to be a "zero-order hold", which is a piecewise constant step function.
You could implement your own logic to go from the time t to the correct values of those parameters. This mostly applies if there is some structure to the data, for instance, if they have the form f(int(t/h)).
Note that the approximation order of the numerical integration is not only bounded by the order of the RK (solve_ivp) or multi-step (odeint) method, but also by the differentiability order of the (parts of) the differential equation. If the ODE is much less smooth than the order of the method, the implicit assumptions for the step size control mechanism are violated, which may result in a very small step size requiring a huge number of integration steps.
I also encountered similar problem. In my case, parameters a, b, and c are not in direct function with time, but determined by x, y, and z at that time. So I have to get x, y, z at time t, and calculate a, b, c for the integration calculation for x, y, z at t+dt. It turns out that if I change dt value, the whole integration result will change dramatically, even to something unreasonable.
The full mathematical problem is here.
Briefly I want to integrate a function with a double integral. The inner integral has boundaries 20 and x-2, while the outer has boundaries 22 and 30.
I know that with Scipy I can compute the double integral with scipy.integrate.nquad. I would like to do something like this:
def f(x, y):
return (x ** 2 + y ** 2)
res = sp.integrate.nquad(f, [[22, 30], [20, x-2]])
Is it possible? Maybe using also sympy?
I solved with sympy:
from sympy import *
x, y = symbols("x y")
f = (x ** 2 + y ** 2)
res = integrate(f, (y, 20, x-2), (x, 22, 30))
Basically sympy.integrate is able to deal with multiple integrations, also with variable boundaries.
If you need the numerical integration and sympy is not an option. Then you could try something like the following.
For this example it seems quick, but I have a suspicion you may run into problems in general, see how well it does for your use case.Perhaps this possibly imperfect answer will prompt someone to submit something better.
I use the fact that we can do the integrations one after the other, integrating out the y first, to get a function of x, then integrating that.
from scipy.integrate import quad
def integrand(x, y):
return (x ** 2 + y ** 2)
def y_integral(x):
# Note scipy will pass args as the second argument
# we can fiddle it to work correctly, but by symmetry we don't need to here.
return quad(integrand, 20, x-2, args=(x))[0]
We then use this y_integral function as the result function of the inner integral.
res = quad(y_integral, 22, 30)
print res
You could wrap this in a function if you use it regularly.