How to get a decimal instead of a fraction in sympy? - python

I am making a chemistry project for school, I want to use sympy for solving some density and concentration problems.
solveset function returns the value as a fraction, for example:
mL = Symbol("mL")
density = Symbol("p")
mass = 115*g
volume = 100*ml
print(solveset(Eq(density, mass/volume), density))
The output is {23*g/(20*mL)} (fraction) but I want it in the decimal form ({1.15*g/ml})

You can use the n() method to evaluate the symbolic results to floats. Note that n is an alias of the evalf method. But first, you probably want to extract the result from the set of solutions:
sol = solveset(Eq(density, mass/volume), density)
sol = list(sol)[0].n()
print(sol)
# 1.15*g/mL

Related

Python Integration: to calculate area under the curve

I want to find the integral of output power Po in the following code:
Vo = 54.6
# defining a function for duty cycle, output current and output power
def duty_cycle(output_voltage, array_voltage):
duty_cycle = np.divide(output_voltage, array_voltage)
return duty_cycle
def output_current(array_current, duty_cycle):
output_current = np.divide(array_current, duty_cycle)
return output_current
def output_power(output_voltage, output_current):
output_power = np.multiply(output_voltage, output_current)
return output_power
#calculating duty cycle, output current and output power
D = duty_cycle(Vo, array_params['arr_v_mp'])
Io = output_current(array_params['arr_i_mp'], D)
Po = output_power(Vo, Io)
#plot ouput power
plt.ylabel('Output Power [W]')
Po.plot(style='r-')
The code above is just a part of a script. array_params is a pandas time-series data frame. When plotted pandas Series Po, it looks like this:
This is my first time calculating integral using python. After reading through the internet, I think Python's scipy module could be of help but don't really know how and which method to implement. I would appreciate your help in any manner with the above-explained problem.
To compute an integral of the form int y(x) dx from x0 to x1, with an array x_array with values from x0 to x1 and a corresponding y_array of same length, one can use numpy's trapezoidal integration:
integral = np.trapz(y_array, x_array)
which will work also for non-constant spacing x_array[i+1]-x_array[i].
If an indefinite integral (i.e. an integral F(t) = integral f(t) dt) is needed, use scipy.integrate.cumtrapz (instead of numpy.trapz for definite integrals).
integrated = scipy.integrate.cumtrapz(power, dx=timestep)
or
integrated = scipy.integrate.cumtrapz(power, x=timevalues)
To have integrated the same length as power, specify the initial value of the integral, via the optional parameter initial (e.g. initial=0) to scipy.integrate.cumtrapz.

Why can the solve() function in matlab solve this equation while the nsolve() function in sympy requires a guess?

I have a mode, a maximum and minimum value of X (Xmin and Xmax), and a percentage confidence (percentage).
I want to use the following functions in order to find the μ and σ of a theoretical log normal distribution:
The cumulative distribution function:
and the mode
I began with the following Matlab script:
function [mu, sigma] = DefLog(Mode, Percentage, Xmin, Xmax)
syms s
eqn = 1/2+1/2*erf((log(Xmax)-(log(Mode)+s^2))/(sqrt(2)*s))-(1/2+1/2*erf((log(Xmin)-(log(Mode)+s^2))/(sqrt(2)*s)))==Percentage;
sigma = solve(eqn,s)
mu=log(Mode)+sigma^2
end
And this gives me a single numerical solution for mu and sigma.
For example if I run DefLog(2, 0.95, 1, 4) I get sigma = 0.33 and mu = 0.80
I needed to translate this equation into Python, so I used sympy to solve the same equation. The only way I could get a single numerical solution with sympy was to use the nsolve function. My code is as follows:
from sympy import *
def CalcScaleParamOPT(mode, percentage, Xmin, Xmax):
s = Symbol('s', Real=True)
eqn = (1/2+1/2*erf((log(Xmax)-(log(mode)+s**2))/(sqrt(2)*s))-(1/2+1/2*erf((log(Xmin)-(log(mode)+s**2))/(sqrt(2)*s)))) - 0.95
sigma = nsolve(eqn, 0.6)
mu=log(mode)+sigma**2
print(sigma)
print(mu.evalf())
CalcScaleParamOPT(2, 0.95, 1, 4)
This gives the same solution as the matlab script, but unlike the matlab solve() function nsolve() requires a "guess" close enough to the answer I am looking for. How can matlab find a single solution without the guess?
Based on the documentation, MATLAB's solve falls back to a numerical solution automatically. Assumedly it generates a guess value automatically (it doesn't mention how), but it does say that you can use vpasolve to pass in a guess interval manually, since the default solve only returns one numerical solution (the first one it finds).

Why sympy cannot calculate fraction power formula like (6-x*x)**(1.5)?

I used sympy to calculate some integral as follows.
#Calculate Calculus
import sympy
x = sympy.Symbol('x')
f = (6-x*x)**(1.5)
f.integrate()
This will fail and throw excepiton like:
ValueError: gamma function pole
It works fine if I just use an integer as power num
f = (6-x*x)**(2)
#result: x**5/5 - 4*x**3 + 36*x
My guess is a 1.5 expression is treated as a floating point, which is imprecise. You'd want a symbolic (exact) representation, instead. (I would guess if you were after a computational integral a floating point would probably be okay, generally, as a math library that supports a computational integral would typically use an integral approximation method to compute the integral.) If you need to do arbitrary rational exponents, consider using sympy.Rational. Here's a relevant answer on StackOverflow that seems to support this. I think the documentation for sympy.Rational is here.
You can try this modified code here:
#Calculate Calculus
import sympy
frac = sympy.Rational
x = sympy.Symbol('x')
f = (6-x*x)**(frac('1.5'))
f.integrate()
The previous answer is right, I'm just posting my final result here
import sympy
frac = sympy.Rational
x = sympy.symbols('x')
f1 = (x+3)/(6-x**2)**(frac('1.5'))
f1.integrate()

Calculate the exact integral in Python

I need to write a python code to calculate the exact value of the integral (-5, 5) of 1/(1+x^2).
I know the answer is 2arctan(5) which is roughly equivalent to 2.746801...
I have below the code I have written, however I am getting a slightly different answer and I was wondering if there is anything I can do to make this code more accurate? Thanks for any help!
## The function to be integrated
def func(x):
return 1/(1 + x**2)
## Defining variables
a = -5.0
b = 5.0
dx = 1.0
Area = 0
## Number of trapezoids
n = int((b-a)/dx)
## Loop to calculate area and sum
for i in range(1, n+1):
x0 = a + (i-1)*dx
x1 = a + i*dx
## Area of each trapezoid
Ai = dx*(func(x0) + func(x1))/2.0
## Cumulative sum of areas
Area = Area + Ai
print("The exact value is: ", Area)
The answer I am getting is 2.756108...
I know it's a small difference, however, it is a difference and I would like to try for something more exact.
The reason you are getting an approximate value for the integral is because you are using an approximation technique (a first-order approximation to compute the value of the definite integral).
There are two ways to evaluate an integral: analytically or numerically (by approximation). Your method is of the second variety, and since it's an approximation it will generate a value that is within a certain margin of error of the real value.
The point of my answer is that there is no way for you to calculate the exact value of the integral using a numeric approach (definitely not in the case of this function). So you will have to settle for a certain margin of error that you're willing to accept and then choose a delta-x sufficiently small to get you within that range.

Model I-V in Python

Model I-V.
Method:
Perform an integral, as a function of E, which outputs Current for each Voltage value used. This is repeated for an array of v_values. The equation can be found below.
Although the limits in this equation range from -inf to inf, the limits must be restricted so that (E+eV)^2-\Delta^2>0 and E^2-\Delta^2>0, to avoid poles. (\Delta_1 = \Delta_2). Therefore there are currently two integrals, with limits from -inf to -gap-e*v and gap to inf.
However, I keep returning a math range error although I believe I have excluded the troublesome E values by using the limits stated above. Pastie of errors: http://pastie.org/private/o3ugxtxai8zbktyxtxuvg
Apologies for the vagueness of this question. But, can anybody see obvious mistakes or code misuse?
My attempt:
from scipy import integrate
from numpy import *
import scipy as sp
import pylab as pl
import numpy as np
import math
e = 1.60217646*10**(-19)
r = 3000
gap = 400*10**(-6)*e
g = (gap)**2
t = 0.02
k = 1.3806503*10**(-23)
kt = k*t
v_values = np.arange(0,0.001,0.0001)
I=[]
for v in v_values:
val, err = integrate.quad(lambda E:(1/(e*r))*(abs(E)/np.sqrt(abs(E**2-g)))*(abs(E+e*v)/(np.sqrt(abs((E+e*v)**2-g))))*((1/(1+math.exp((E+e*v)/kt)))-(1/(1+math.exp(E/k*t)))),-inf,(-gap-e*v)*0.9)
I.append(val)
I = array(I)
I2=[]
for v in v_values:
val2, err = integrate.quad(lambda E:(1/(e*r))*(abs(E)/np.sqrt(abs(E**2-g)))*(abs(E+e*v)/(np.sqrt(abs((E+e*v)**2-g))))*((1/(1+math.exp((E+e*v)/kt)))-(1/(1+math.exp(E/k*t)))),gap*0.9,inf)
I2.append(val2)
I2 = array(I2)
I[np.isnan(I)] = 0
I[np.isnan(I2)] = 0
pl.plot(v_values,I,'-b',v_values,I2,'-b')
pl.show()
This question is better suited for the Computational Science site. Still here are some points for you to think about.
First, the range of integration is the intersection of (-oo, -eV-gap) U (-eV+gap, +oo) and (-oo, -gap) U (gap, +oo). There are two possible cases:
if eV < 2*gap then the allowed energy values are in (-oo, -eV-gap) U (gap, +oo);
if eV > 2*gap then the allowed energy values are in (-oo, -eV-gap) U (-eV+gap, -gap) U (gap, +oo).
Second, you are working in a very low temperature region. With t equal to 0.02 K, the denominator in the Boltzmann factor is 1.7 µeV, while the energy gap is 400 µeV. In this case the value of the exponent is huge for positive energies and it soon goes off the limits of the double precision floating point numbers, used by Python. As this is the minimum possible positive energy, things would not get any better at higher energies. With negative energies the value would always be very close to zero. Note that at this temperature, the Fermi-Dirac distribution has a very sharp edge and resembles a reflected theta function. At E = gap you would have exp(E/kT) of approximately 6.24E+100. You would run out of range when E/kT > 709.78 or E > 3.06*gap.
Yet it makes no sense to go to such energies since at that temperature the difference between the two Fermi functions very quickly becomes zero outside the [-eV, 0] interval which falls entirely inside the gap for the given temperature when V < (2*gap)/e (0.8 mV). That's why one would expect that the current would be very close to zero when the bias voltage is less than 0.8 mV. When it is more than 0.8 mV, then the main value of the integral would come from the integrand in (-eV+gap, -gap), although some non-zero value would come from the region near the singularity at E = gap and some from the region near the singularity at E = -eV-gap. You should not avoid the singularities in the DoS, otherwise you would not get the expected discontinuities (vertical lines) in the I(V) curve (image taken from Wikipedia):
Rather, you have to derive equivalent approximate expressions in the vicinity of each singularity and integrate them instead.
As you can see, there are many special cases for the value of the integrand and you have to take them all into account when computing numerically. If you don't want to do that, you should probably turn to some other mathematical package like Maple or Mathematica. These have much more sophisticated numerical integration routines and might be able to directly handle your formula.
Note that this is not an attempt to answer your question but rather a very long comment that would not fit in any comment field.
The reason for the math range error is that your exponential goes to infinity. Taking v = 0.0009 and E = 5.18e-23, the expression exp((E + e*v) / kt) (I corrected the typo pointed out by Hristo Liev in your Python expression) is exp(709.984..) which is beyond the range you can represent with double precision numbers (up to ca. 1E308).
Two additional notes:
As noted by others, you should probably rescale your equation by using a unit system which delivers numbers in a smaller range. Maybe, atomic units are a possible choice as it would set e = 1, but I did not try to convert your equation into it. (Probably, your timestep would then become quite large, as in atomic units the time unit is about is 1/40 fs).
Usually, one uses the exponential notation for float point numbers: e = 1.60217E-19 instead of e = 1.60217*10**(-19).
The best way to approach this problem in the end was to use a heaviside function to preventE variable from exceeding \Delta variable.

Categories