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?
Related
I wrote the code below in Python to create a function that integrates func(x+t**(1/2) * z)*... with respect to z, where I want to call the function for some x.
This is part of a bigger code and in order to make the whole program go faster I wondered if there was a way to optimize the way pregau works.
def gauss(func, t):
def gauss(x):
def pregau(z):
arg = x + t ** (1 / 2) * z
return func(arg) * math.exp(-(z**2)/2)
integration = integrate.quad(pregau, -np.inf, np.inf, epsabs=0.001)[0]
return (2 * math.pi) ** (-1/2) * integration
return gauss
I thought about interpolating it on the whole space, but maybe any of you have better ideas to solve this problem?
I want to do a plot of this equation below:
Problem 1: You see... since my function is a function of ν I have to calculate my integral to each ν in my domain. My question is: what is the best way to do that?
I thought about using scipy to do the integral and a for loop to calculate it several times to each ν, but it seems a very inelegant way to solve my problem. Does someone know a better alternative? Does someone have a different idea?
Problem 2: When I write my code I get some errors, mainly because I think that the exponential has a very small expoent. Do you have any ideas of how should I change it so I can do this plot using Python?
Oh, if you try with a different method, it is supposed to look like this
Here is the code I was working on. I'm coming back to Python now, so maybe there is some errors. The plot I'm getting is very different from the one that this is supposed to look.
from scipy.integrate import quad
from scipy.constants import c, Planck, k, pi
import numpy as np
import matplotlib.pyplot as plt
def luminosity_integral(r, x):
T_est = 4000
R_est = 2.5 * (696.34*1e6)
Temp = ((2/(3*pi))**(1/4)) * T_est * ((R_est/r)**(3/4))
termo1 = ((4 * (pi**2) * Planck * (x**4) ) / (c**2))
termo2 = ((Planck * x) / (k*Temp))
return ((termo1 * r ) / (np.exp(termo2) - 1))
freqs = np.linspace(1e10, 1e16)
y = np.array([])
for i in freqs:
I = quad(luminosity_integral, (6 * 2.5 * (696.34*1e6)), (7e4 * 2.5 * (696.34*1e6)), args = (i))
temp = np.array([I[0]])
y = np.concatenate((y, temp))
plt.loglog(freqs, y)
plt.show()
Reuse the term R_est instead instead of writing its expression 3 times (better if you want to change that parameter).
you used a pi**2 in the constant multiplying the integral (don't affect the shape)
The shape resembles what you put as reference, but not in the suggested range.
You are using the value of T as T_*, are you sure about that?
Try this version of the code
from scipy.integrate import quad
from scipy.constants import c, Planck, k, pi
import numpy as np
import matplotlib.pyplot as plt
R_est = 2.5 * (696.34e6)
def luminosity_integral(r, x):
T_est = 4000
termo1 = ((4 * pi * Planck * (x**4) ) / (c**2))
termo2 = ((Planck * x) / (k*T_est)) * (3*pi/2 * (r/R_est)**3)**0.25
termo3 = np.exp(-termo2)
return ((termo1 * r ) * termo3 / (1 - termo3))
freqs = np.logspace(6, 16)
y = np.zeros_like(freqs)
for i, nu in enumerate(freqs):
y[i] = quad(luminosity_integral, (6* R_est), (7e4 * R_est), args = (nu))[0]
plt.loglog(freqs, y)
plt.ylim([1e6, 1e25])
plt.show()
I have three dimensional function randomly defined by a user Z=f(x,y)
def f(x,y):
return ((7*x*y)/(np.exp(x**2+y**2)))
I converted this function to polar coordinates by substituting:
x_c = r*np.cos(theta)+x1; y_c = r*np.sin(theta)+y1;
where
theta = np.linspace(0, 2*np.pi, 30);
r,x1 and y1 are constant
Now I need to find the roots for the function Z(theta) in the polar form using NR method:
from scipy.optimize import newton
Z_func = f(x_c,y_c);
root = newton(Z_func,theta.any());
the error message shows:
q0 = func(*((p0,) + args))
TypeError: 'numpy.ndarray' object is not callable
newton method accepts only a callable function how can I make the function f(x_c,y_c) callable?
I do not want to substitute as shown below and call it in newton method
7*(r*np.cos(theta)+x1)*(r*np.sin(theta)+y1))/(np.exp((r*np.cos(theta)+x1)**2+(r*np.sin(theta)+y1)**2)
because I do not know the function f(x,y) that will be given.
Any idea will be appreciated
Well you are right about your issue. The scipy.optimize.newton() only accepts a callable function. So You can simply define z_func(): a function that takes in your optimization variable (in this case it is theta) and returns the output of the function whose zero is wanted. I chose to separate the functions and keep your structure but with the use of the lambda operator (see code below).
import numpy as np
from scipy import optimize
# define functions
f = lambda x, y : (7 * x * y) / np.exp(x**2 + y**2)
x_c = lambda r, theta, x1 : r * np.cos(theta) + x1
y_c = lambda r, theta, y1 : r * np.sin(theta) + y1
z_f = lambda theta : f(x_c(1, theta, 1), y_c(1, theta, 1))
# define theta
theta = np.linspace(0, 2*np.pi, 30)
# optimize
root = optimize.newton(z_f, theta.any())
print("root: ", root)
Remark: I am not sure why are you using theta.any() as a starting value, as it returns a True and that translates to 1, which makes me question the definition of the theta array since all you need is just a starting value (which is nevertheless important as it will change the root the code will converge to or might even cause a RuntimeError if it fails to converge).
In the equation above I want to solve for f and pass in Re, D, and epsilon. Here is my code below:
import math
from scipy.optimize import fsolve
# Colebrook Turbulent Friction Factor Correlation
def colebrook(Re, D, eps):
return fsolve(-(1 / math.sqrt(f)) - 2 * math.log10(((eps / D) / 3.7) + (2.51 / Re * math.sqrt(f))), f)
Would I use fsolve() or solve()? I read up on fsolve() on Python's main site, however I don't understand some of the inputs it wants. Thank you in advance!
Also, I am using Spyder (Python 3.6)
The wikipedia page on "Darcy friction factor formulae" has a section on the Colebrook equation, and shows how f can be expressed in terms of the other parameters using the Lambert W function.
SciPy has an implementation of the Lambert W function, so you can use that to compute f without using a numerical solver:
import math
from scipy.special import lambertw
def colebrook(Re, D, eps):
"""
Solve the Colebrook equation for f, given Re, D and eps.
See
https://en.wikipedia.org/wiki/Darcy_friction_factor_formulae#Colebrook%E2%80%93White_equation
for more information.
"""
a = 2.51 / Re
b = eps / (3.7*D)
p = 1/math.sqrt(10)
lnp = math.log(p)
x = -lambertw(-lnp/a * math.pow(p, -b/a))/lnp - b/a
if x.imag != 0:
raise ValueError('x is complex')
f = 1/x.real**2
return f
For example,
In [84]: colebrook(125000, 0.315, 0.00015)
Out[84]: 0.019664137795383934
For comparison, the calculator at https://www.engineeringtoolbox.com/colebrook-equation-d_1031.html gives 0.0197.
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.