Using Scipy Multiple integration with a single variable - python

I have a function for acceleration a(t) which I know if I integrate twice with respect to t, I can find position x(t). I am trying to find position at t = 10 seconds.
Since a(t) is not multivariable I am having trouble using the Scipy dblquad function to calculate the double integral I need. Please see what I have so far:
def a(t):
return (2.5 / (1 + math.exp((t-8)/0.8)))
def upperbound():
return 10
def lowerbound():
return 0
x = dblquad(a,0,10,lowerbound,upperbound)
This does not work as from what I can gather dblquad needs a multivariabled a(t). Can anyone help?

You can use scipy's single variable integration twice to accomplish this
import math
from scipy.integrate import quad
def a(t):
return (2.5 / (1 + math.exp((t-8)/0.8)))
lb, ub = 0, 10
integral = quad(lambda t: quad(a, 0, t)[0], lb, ub)[0]
print(integral)
# 86.28470375472537
This is necessary because what you want to achieve is not to integrate a function of two variables but rather to integrate a function of one variable twice. In the integral statement above, the inner quad integrates the function once but maintains the integral as a function of t. The outer quad integrates the function the second time over the defined limits.
Note that it is necessary to take the first argument from the output of quad because it outputs a tuple. The second argument is an upper bound on the error of the numerical integration.

Related

Solver does the integration without calling the derivative callback function

I have a python code (example from Cantera.org) that uses scipy.integrate.ode to solve a system of ODE. The code works fine and the results are reasnoable. However, I noticed something about the ode solver that does not make sense to me.
I have a put a print function inside (print("t inside ODE function", t)) the function the calculates the derivative vector (__call__(self, t, y)), and outside that function in the while loop (print("t outside ODE function", solver.t);).
I expect that inside print has to be called when the solver does the time integration, and then the outside print is called. In other words, two "t outside ODE function" cannot appear right after another without "t inside ODE function" in between. However, this occurs in some of iterations in the while loop, which mean the solver does the integration without calculating the derivatives.
I am wondering how this is possible
import cantera as ct
import numpy as np
import scipy.integrate
class ReactorOde:
def __init__(self, gas):
# Parameters of the ODE system and auxiliary data are stored in the
# ReactorOde object.
self.gas = gas
self.P = gas.P
def __call__(self, t, y):
"""the ODE function, y' = f(t,y) """
# State vector is [T, Y_1, Y_2, ... Y_K]
self.gas.set_unnormalized_mass_fractions(y[1:])
self.gas.TP = y[0], self.P
rho = self.gas.density
print("t inside ODE function", t)
wdot = self.gas.net_production_rates
dTdt = - (np.dot(self.gas.partial_molar_enthalpies, wdot) /
(rho * self.gas.cp))
dYdt = wdot * self.gas.molecular_weights / rho
return np.hstack((dTdt, dYdt))
gas = ct.Solution('gri30.yaml')
# Initial condition
P = ct.one_atm
gas.TPX = 1001, P, 'H2:2,O2:1,N2:4'
y0 = np.hstack((gas.T, gas.Y))
# Set up objects representing the ODE and the solver
ode = ReactorOde(gas)
solver = scipy.integrate.ode(ode)
solver.set_integrator('vode', method='bdf', with_jacobian=True)
solver.set_initial_value(y0, 0.0)
# Integrate the equations, keeping T(t) and Y(k,t)
t_end = 1e-3
states = ct.SolutionArray(gas, 1, extra={'t': [0.0]})
dt = 1e-5
while solver.successful() and solver.t < t_end:
solver.integrate(solver.t + dt)
gas.TPY = solver.y[0], P, solver.y[1:]
states.append(gas.state, t=solver.t)
print("t outside ODE function", solver.t);
print("\n")
The solver has an adaptive step size. Which means that it proceeds in internal steps that are adapted to the given error tolerances. In the segment from one step point to the next, the solution values get interpolated. Thus it can happen that a sequence of the external steps of the time loop falls into the same internal segment. If you set the error tolerances to smaller levels as the default ones, it can happen that the situation reverses, that several internal steps are required per external value request.

Something like de-lambdify

Suppose I have the following lambda function
import numpy as np
f = lambda x,t : np.cos(t)
Now I want to obtain a symbolic expression of f (the ultimate goal is to obtain a primitive of that function f). So
import sympy as sym
x_s = sym.Symbol('x')
t_s = sym.Symbol('t')
sym.integrate(f(x_s,t_s), t_s)
But that fails with the error:
TypeError: loop of ufunc does not support argument 0 of type Symbol
which has no callable cos method
I want to be able to translate any Closed-form expression to sympy.
Thanks.
Edit: What I am trying to achieve at the end is to numerically solve a PDE using Dedalus. For the moment I just need a linear reaction diffusion equation with non-constant coefficients. That function f(x,t) is one of the coefficients. Since the equation is linear, I have the analytic solution, which is a function that depends on the primitive with respect to t of that function f(x,t). So I want to input the function f(x,t) in one place, and then use it to construct the numerical solution (in this case I just need to call the f(x,t) and also the analytic solution (in this case I need to call the primitive of f(x,t)).
So bottom line:
I need to define a lambda function that depends on 2 arguments x and t for the following purpose:
1) compute values given x and t.
2) convert that function definition to text to be used to solve the numerical equation using dedalus (and replace every reference to sym to np, otherwise dedalus will give error).
3) be able to obtain the primitive of that function (that's why I am using sympy) and then define a lambda function of that primitive in order to compute values for getting the analytical solution.
You are getting error because numpy`s cos is not compatible with sympy.Symbol. To get rid of this error you should rewrite your lambda like this:
import sympy as sym
f = lambda x,t : sym.cos(t)
De-lambdify is performed like this:
x_s = sym.Symbol('x')
t_s = sym.Symbol('t')
sym.integrate(f(x_s,t_s), t_s)
When you call f, it returns sympy cos function.
P.S. Check if you really need x parameter in f function.
So, full code will be:
import sympy as sym
f = lambda x,t : sym.cos(t)
x_s = sym.Symbol('x')
t_s = sym.Symbol('t')
sym.integrate(f(x_s,t_s), t_s)

Integrating a gaussian over a very long interval

I want to integrate a Gaussian function over a very large interval. I chose spicy.integrate.quad function for the integration. The function seems to work only when I select a small enough interval. When I use the codes below,
from scipy.integrate import quad
from math import pi, exp, sqrt
def func(x, mean, sigma):
return 1/(sqrt(2*pi)*sigma) * exp(-1/2*((x-mean)/sigma)**2)
print(quad(func, 0, 1e+31, args=(1e+29, 1e+28))[0]) # case 1
print(quad(func, 0, 1e+32, args=(1e+29, 1e+28))[0]) # case 2
print(quad(func, 0, 1e+33, args=(1e+29, 1e+28))[0]) # case 3
print(quad(func, 1e+25, 1e+33, args=(1e+29, 1e+28))[0]) # case 4
then the followings are printed.
1.0
1.0000000000000004
0.0
0.0
To obtain a reasonable result, I had to try and change the lower/upper bounds of the integral several times and empirically determine it to [0, 1e+32]. This seems risky to me, as when the mean and sigma of the gaussian function changes, then I always have to try different bounds.
Is there a clear way to integrate the function from 0 to 1e+50 without bothering with bounds? If not, how do you expect from beginning which bounds would give non-zero value?
In short, you can't.
On this long interval, the region where the gaussian is non-zero is tiny, and the adaptive procedure which works under the hood of integrate.quad fails to see it. And so would pretty much any adaptive routine, unless by chance.
Notice,
and the CDF of a normal random variable is known as ϕ(x) as it can not be expressed by an elementary function. So take ϕ((b-m)/s) - ϕ((a-m)/s). Also note that ϕ(x) = 1/2(1 + erf(x/sqrt(2))) so you need not call .quad to actually perform an integration and may have better luck with erf from scipy.
from scipy.special import erf
def prob(mu, sigma, a, b):
phi = lambda x: 1/2*(1 + erf((x - mu)/(sigma*np.sqrt(2))))
return phi(b) - phi(a)
This may give more accurate results (it does than the above)
>>> print(prob(0, 1e+31, 0, 1e+50))
0.5
>>> print(prob(0, 1e+32, 1e+28, 1e+29))
0.000359047985937333
>>> print(prob(0, 1e+33, 1e+28, 1e+29))
3.5904805169684195e-05
>>> print(prob(1e+25, 1e+33, 1e+28, 1e+29))
3.590480516979522e-05
and avoid the intense floating point error you are experiencing. However, the regions you integrate are so small in area that you may still see 0.

Python: Find principal value of an integral numerically

I'm solving the integral numerically using python:
where a(x) can take on any value; positive, negative, inside or outside the the [-1;1] and eta is an infinitesimal positive quantity. There is a second outer integral of which changes the value of a(x)
I'm trying to solve this using the Sokhotski–Plemelj theorem:
However this involves determining the principle value, which I can't find any method to in python. I know it's implemented in Matlab, but does anyone know of either a library or some other way of the determining the principal value in python (if a principle value exists)?
You can use sympy to evaluate the integral directly. Its real part with eta->0 is the principal value:
from sympy import *
x, y, eta = symbols('x y eta', real=True)
re(integrate(1/(x - y + I*eta), (x, -1, 1))).simplify().subs({eta: 0})
# -> log(Abs(-y + 1)/Abs(y + 1))
Matlab's symbolic toolbox int gives you the same result, of course (I'm not aware of other relevant tools in Matlab for this --- please specify if you know a specific one).
You asked about numerical computation of a principal value. The answer there is that if you only have a function f(y) whose analytical form or behavior you don't know, it's in general impossible to compute them numerically. You need to know things such as where the poles of the integrand are and what order they are.
If you on the other hand know your integral is of the form f(y) / (y - y_0), scipy.integrate.quad can compute the principal value for you, for example:
import numpy as np
from scipy import integrate, special
# P \int_{-1}^1 dx 1/(x - wvar) * (1 + sin(x))
print(integrate.quad(lambda x: 1 + np.sin(x), -1, 1, weight='cauchy', wvar=0))
# -> (1.8921661407343657, 2.426947531830592e-13)
# Check against known result
print(2*special.sici(1)[0])
# -> 1.89216614073
See here for details.

How do I solve for x with a known function in equation using numpy/scipy?

I have a lognorm distribution from scipy and its parameters are known.
import scipy
log_norm_obj = scipy.stats.lognorm([log_mu], shape=sigma)
I need to solve for a x which satisfies the following equation:
x = (1 - log_norm_obj.cdf(x)) / log_norm_obj.pdf(x)
How could I do this using numpy/scipy? Thanks!
You use scipy.optimize. From scipy 0.11 and later, you can use the new functions minimize or minimize_scalar. Assuming your x is a scalar, here's some example code on how to do it:
from scipy.optimize import minimize_scalar
def f(x):
return (1 - log_norm_obj.cdf(x)) / log_norm_obj.pdf(x) - x
result = minimize_scalar(f)
print result.x
# this would print your result
The above uses Brent's method, the default. You can also the Golden method, or a bounded version of Brent's method. The latter could be useful if your function is only defined in a given domain or you want a solution in a specific interval. An example of this:
result = minimize_scalar(f, bounds=(0, 10.), method='bounded')
If your function takes a vector instead of a scalar, a similar approach can be taken using minimize. If your scipy is older than version 0.11, just use a flavour of fmin.

Categories

Resources