python scientific programming why blank occur when plotting with right results? - python-3.x

I want to plot the integration results as described below , but it seems turned out to be a blank sheet, what' s the reason ? please help me!
# -*- coding: utf-8 -*-
import matplotlib.pylab as plt
import numpy as np
import scipy as sp
from scipy.integrate import quad, dblquad, tplquad
x = np.arange(0,1,0.1)
print ("x = ", x)
def f(x):
return x
print ("f(x) = ", f(x))
x_lower = 0
for x_upper in x :
val, abserr = quad(f, x_lower, x_upper)
print ("integral value =", val, ", x_upper = ", x_upper ,", absolute error =", abserr)
plt.plot(x_upper, val, ' b--' )
plt.show()
The output, but plot is blank!
x = [ 0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9]
f(x) = [ 0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9]
integral value = 0.0 , x_upper = 0.0 , absolute error = 0.0
integral value = 0.005000000000000001 , x_upper = 0.1 , absolute error = 5.551115123125784e-17
integral value = 0.020000000000000004 , x_upper = 0.2 , absolute error = 2.2204460492503136e-16
integral value = 0.04500000000000001 , x_upper = 0.3 , absolute error = 4.996003610813205e-16
integral value = 0.08000000000000002 , x_upper = 0.4 , absolute error = 8.881784197001254e-16
integral value = 0.125 , x_upper = 0.5 , absolute error = 1.3877787807814457e-15
integral value = 0.18000000000000005 , x_upper = 0.6 , absolute error = 1.998401444325282e-15
integral value = 0.24500000000000005 , x_upper = 0.7 , absolute error = 2.720046410331634e-15
integral value = 0.32000000000000006 , x_upper = 0.8 , absolute error = 3.552713678800502e-15
integral value = 0.40499999999999997 , x_upper = 0.9 , absolute error = 4.496403249731884e-15

The reason you do not see anything in the plot is that you are plotting several line plots of only one single point. Since lines would need a start and and end (meaning at least two points) the graph stays blank.
The easiest way of showing your points would be to replace ' b--' in your call to plt.plot() by marker="o":
plt.plot(x_upper, val, marker="o", color="b")
A different option is to first collect all the integration results in a list, and then plot the complete list in a line plot:
import matplotlib.pylab as plt
import numpy as np
from scipy.integrate import quad
x = np.arange(0,1,0.1)
def f(x):
return x
x_lower = 0
vals = []
for x_upper in x :
val, abserr = quad(f, x_lower, x_upper)
vals.append(val)
plt.plot(x, vals, "b--")
plt.show()

I find the answer perhaps, for each time you plot only one dot, but not lines . If you set 'bs' ,then you find it really not a blank, but some dots.

Related

Truncation error vs dt and truncation error vs dx graph of Crank Nicolson scheme for the Nagumo's equation in python

For a problem, I implemented the Nagumo equation via Crank-Nicolson's scheme. Now the problem is that I plotted the truncation error vs dt but the graph should give me a line with a slope of about 62° while the other graph should give a line with a negative slope (angle of 117°).
With the code below I get the following graphs:
# -*- coding: utf-8 -*-
"""
Created on Sun Feb 5 13:21:29 2023
#author: theha
"""
import numpy as np
#import itertools
import numpy.linalg as l
import matplotlib.pyplot as plt
def generateMatrix(N, sigma):
""" Computes the matrix for the Nagumo's equation with Crank-Nicolson
Dirichlet condition at i=0 and at i=1
Parameters:
----------
N: int
Number of discretization points
sigma: float
dt/(2*dx^2)
Returns:
-------
A: 2D numpy array of float
Matrix for Nagumo's equation
"""
# Setup the diagonal
d = np.diag(np.ones(N+1)*(2*r+1))
# Setup upper diagonal
ud = np.diag(np.ones(N)*-r, 1)
# Setup lower diagonal
ld = np.diag(np.ones(N)*-r, -1)
A = d + ud + ld
return A
def generateRHS(u1, sigma,alpha):
""" Computes right-hand side of linear system for Nagumo's equation
with Crank-Nicolson scheme
Parameters:
----------
T: array of float
Nagumo's solution at current time step
sigma: float
dt/(2*dx^2)
Returns:
-------
b: array of float
Right-hand side of Nagumo's equation
with Crank-Nicolson scheme
"""
N=len(u1)
F=((1-2*sigma)*u1)+sigma*((np.append(0,u1[0:N-1]))+np.append(u1[1:N+1],1))+ dt* ((u1)*(1-u1)*(u1-alpha))
F[0]=0
F[-1]=1
return F
def CrankNicolson(T, A, nt, sigma,alpha):
""" Nagumo's equation in time with Crank-Nicolson
Parameters:
----------
T: array of float
initial Nagumo profile
A: 2D array of float
Matrix with discretized Nagumo equation
nt: int
number of time steps
sigma: float
dt/(2*(dx^2))
Returns:
-------
T: array of floats
Nagumo profile after nt time steps
"""
for t in range(nt):
Tn = T.copy()
b = generateRHS(Tn, sigma,alpha)
# Use numpy.linalg.solve
T_interior = np.linalg.solve(A,b)
T = T_interior
return T
#Domain x in [-L,L]
L = 100
nx = 400 # Partition in x
alpha = 0.25
dx = 2*(L/nx) # Step size
dt= .00001 #Time step
r = dt/(2*(dx**2))
sigma = r
nt = 5 # Partition in time
x = np.linspace(-L,L,nx+1)
u0=np.zeros(nx)
t=0
gamma1 = np.sqrt(2)/2*x + (0.5-alpha)*t
gamma2 = (np.sqrt(2)/2*alpha*x) + alpha*(alpha-2)*t/2
u0=(np.exp(gamma1)+alpha*np.exp(gamma2))/(np.exp(gamma1)+np.exp(gamma2)+1) #Initial condition u(x,0)
#initial time step
Ti = u0;
A = generateMatrix(nx, sigma) # A matrix
T = CrankNicolson(Ti.copy(), A, nt, sigma, alpha) #Solution of system Ax=b
def T_analytical(x, t,n_max, alpha):
"""Computes the exact solution for Nagumo's equation
Paramters:
---------
x : array of float
Spatial position
t : float
Evaluation time
n_max: int
Number of terms to evaluate expression
alpha: float
r coefficient of A matrix associated
L : float
Size of rod
Returns:
-------
T : array of float
u(x,t) at each location x
"""
#T = 100
for n in range(1,n_max+1):
gamma1=(np.sqrt(2)*(x))/(2) + (0.5- alpha)*(t)
gamma2=(np.sqrt(2)*(alpha*x))/2 + (alpha*(alpha-2))*((t)/2)
ue=(np.exp(gamma1)+(alpha*np.exp(gamma2)))/(np.exp(gamma1)+np.exp(gamma2)+1)
T=ue
return T
"Graph of approximate solution and exact solution"
T_exact = T_analytical(x, dt*nt, 100, alpha)
fig=plt.figure()
plt.plot(x,T,'x-', label='Aproximada',color='#003366')
plt.plot(x,T_exact,'c|-' ,label='Solucion exacta',color='red')
plt.xlabel('x (espacio)', fontsize = 12)
plt.ylabel('u', fontsize = 12)
plt.xticks(fontsize = 12)
plt.yticks(fontsize = 12)
plt.axis([-L,L,0,1])
plt.grid(True)
plt.legend()
plt.show()
def L2_error(T, T_exact):
"""Computes L2 norm of error
Parameters:
----------
T : array of float
array with numerical solution
T_exact: array of float
array with exact solution
Returns:
-------
e: L2 norm of error
"""
e = l.norm(T_exact-T)
return e
"Calculation of the error in time"
nx = 5
t_final = 1
t_initial = 0
dt_values = np.asanyarray([2.0000e-01,4.0000e-01,6.0000e-01,8.0000e-01,1.0000]) #Values of dt
error = np.zeros(len(dt_values)) #error's array
x = np.linspace(-L,L,nx+1) #Discretization in space
Ti = T_analytical(x, t_initial, 100, alpha) #Simulation of Initial condition, u(x,0) = u0(x)
T_exact = T_analytical(x, t_final, 100, alpha) #Simulation of analytical solution
"Loop for the error calculation in time"
for i,dt in enumerate(dt_values):
#print(i, dt)
sigma = dt/(2*(dx**2))
nt = int((t_final-t_initial)/dt)
A = generateMatrix(nx, sigma)
T = CrankNicolson(Ti.copy(), A, nt, sigma,alpha)
error[i] = L2_error(T,T_exact)
"Plot of error vs dt on logarithmic scale"
plt.figure(figsize=(8,8))
plt.xlabel(r'$\Delta t$', fontsize=18)
plt.ylabel(r'norma $L_2$ del error', fontsize=18)
plt.axis('equal')
plt.loglog(dt_values, error, color='k', ls='--', lw=2, marker='o')
plt.xticks(dt_values,dt_values)
plt.grid(True)
plt.legend(['Crank-Nicolson']);
#loop=nx
#rate =np.log(error[1:loop-1]/error[2:loop])/np.log(dt_values[1:loop-1]/dt_values[2:loop])
"Loop for the error calculation in space"
dx_values = np.asanyarray([0.5,0.25,0.2,0.125,0.0625]) #Values of dx
#dx_values = np.asanyarray([0.0625,0.125,0.2,0.25,0.5]) #Values of dx
error_x = np.zeros(len(dx_values)) #error's array
nt = 5
dt = 0.01
for i,dx in enumerate(dx_values):
sigma = dt/(2*(dx_values[i]**2))
nx = int((2*L)/dx_values[i])
#int((t_final-t_initial)/dt)
x =np.linspace(-L,L,nx+1)
Ti = T_analytical(x, t_initial, nx+1, alpha) #Simulation of Initial condition, u(x,0) = u0(x)
T_exact = T_analytical(x, t_final, nx+1, alpha) #Simulation of analytical solution
A = generateMatrix(nx, sigma)
T = CrankNicolson(Ti.copy(), A, nt, sigma,alpha)
error_x[i] = round(l.norm(T_exact - T), 2)
error_x
"Plot of error vs dx on logarithmic scale"
plt.figure(figsize=(8,8))
plt.xlabel(r'$\Delta x$', fontsize=18)
plt.ylabel(r'norma $L_2$ del error', fontsize=18)
plt.axis('equal')
plt.loglog(dx_values,error_x , color='k', ls='--', lw=2, marker='o')
plt.xticks(np.round(dx_values,2),np.round(dx_values,2))
plt.grid(True)
plt.legend(['Crank-Nicolson']);
The graphs that I got
I would like someone to tell me what the error is in the propagation or if indeed the results I obtained are correct.

I want my Python functions to accept numpy ndarrays

I'm writing a script to plot a 3D representation of some thermodynamic property, Z = f(pr, Tr). pr and Tr are created with [numpy.]arange() and then they are mapped with [numpy.]meshgrid() the following way:
Tr = arange(1.0, 2.6, 0.10)
pr = arange(0.5, 9.0, 0.25)
Xpr, YTr = meshgrid(pr, Tr)
Xpr and YTr are then passed to the function that calculates the aforementioned property:
Z = function(Xpr, YTr)
("function" is just a generic name that is later replaced by the actual function name).
The values stored in Z are finally plotted:
fig = plt.figure(1, figsize=(7, 6))
ax = fig.add_subplot(projection='3d')
surf = ax.plot_surface(Xpr, YTr, Z, cmap=cm.jet, linewidth=0, antialiased=False)
Everything works fine when "function" is something quite straightforward like:
def zshell(pr_, Tr_):
A = -0.101 - 0.36*Tr_ + 1.3868*sqrt(Tr_ - 0.919)
B = 0.021 + 0.04275/(Tr_ - 0.65)
E = 0.6222 - 0.224*Tr_
F = 0.0657/(Tr_ - 0.85) - 0.037
G = 0.32*exp(-19.53*(Tr_ - 1.0))
D = 0.122*exp(-11.3*(Tr_ - 1.0))
C = pr_*(E + F*pr_ + G*pr_**4)
z = A + B*pr_ + (1.0 - A)*exp(-C) - D*(pr_/10.0)**4
return z
But it fails when the function is something like this:
def zvdw(pr_, Tr_):
A = 0.421875*pr_/Tr_**2 # 0.421875 = 27.0/64.0
B = 0.125*pr_/Tr_ # 0.125 = 1.0/8.0
z = 9.5e-01
erro = 1.0
while erro >= 1.0e-06:
c2 = -(B + 1.0)
c1 = A
c0 = -A*B
f = z**3 + c2*z**2 + c1*z + c0
df = 3.0e0*z**2 + 2.0e0*c2*z + c1
zf = z - f/df
erro = abs((zf - z)/z)
z = zf
return z
I strongly suspect that the failure is caused by the iterative method inside function zvdw(pr_, Tr_) (zvdw has been previously tested and works perfectly well when float arguments are passed to it). That is the error message I get:
Traceback (most recent call last):
File "/home/fausto/.../TestMesh.py", line 81, in <module>
Z = zvdw(Xpr, YTr)
File "/home/fausto/.../TestMesh.py", line 63, in zvdw
while erro >= 1.0e-06:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Yet the error message doesn't seem (to me) to be directly related to the while statement.
Any ideas?
Tl;dr
There is a numpy functionality for that. You just replace
def zvdw(pr_, Tr_):
by
#np.vectorize
def zvdw(pr_, Tr_):
And it works.
Get it faster
Unfortunately the resulting picture looked ugly since your mesh is to sparse. I replaced your step size in TR and pr. Unfortunately here we run into the limitation of np.vectorize. From the numpy documentation
The vectorize function is provided primarily for convenience, not for
performance. The implementation is essentially a for loop.
I.e. it is painfully slow. Already at 0.0010 and 0.0025 it took 17.5 seconds. So it is not realistic to go a factor of 10 each smaller since that would take ~100 times longer. Fortunately your code is simple enough that I can use #numba.vectorize which on my machine was a factor of ~23 faster.
Notice that this only works for some python code. Numba compiles the python code to llvm code so it can run fast. But it is unable to do that for arbitrary python code. See https://numba.pydata.org/numba-doc/dev/user/vectorize.html
And here is your picture
Code seems not to work with np.vectorize/numba.vectorize
That is very odd. Here is a literal copy paste of code that works on my machine:
import numpy as np
import matplotlib.pyplot as plt
Tr = np.arange(1.0, 2.6, 0.10)
pr = np.arange(0.5, 9.0, 0.25)
Xpr, YTr = np.meshgrid(pr, Tr)
def zshell(pr_, Tr_):
A = -0.101 - 0.36*Tr_ + 1.3868*sqrt(Tr_ - 0.919)
B = 0.021 + 0.04275/(Tr_ - 0.65)
E = 0.6222 - 0.224*Tr_
F = 0.0657/(Tr_ - 0.85) - 0.037
G = 0.32*exp(-19.53*(Tr_ - 1.0))
D = 0.122*exp(-11.3*(Tr_ - 1.0))
C = pr_*(E + F*pr_ + G*pr_**4)
z = A + B*pr_ + (1.0 - A)*exp(-C) - D*(pr_/10.0)**4
return z
#np.vectorize
def zvdw(pr_, Tr_):
A = 0.421875*pr_/Tr_**2 # 0.421875 = 27.0/64.0
B = 0.125*pr_/Tr_ # 0.125 = 1.0/8.0
z = 9.5e-01
erro = 1.0
while erro >= 1.0e-06:
c2 = -(B + 1.0)
c1 = A
c0 = -A*B
f = z**3 + c2*z**2 + c1*z + c0
df = 3.0e0*z**2 + 2.0e0*c2*z + c1
zf = z - f/df
erro = abs((zf - z)/z)
z = zf
return z
Z = zvdw(Xpr, YTr)
fig = plt.figure(1, figsize=(7, 6))
ax = fig.add_subplot(projection='3d')
surf = ax.plot_surface(Xpr, YTr, Z, cmap=plt.cm.jet, linewidth=0, antialiased=False)

How to convert floating point number to ratio of integers in python with high accuracy? [duplicate]

This question already has answers here:
How to convert a decimal number into fraction?
(6 answers)
Is there a way to return a fully reduced ratio when calling .as_integer_ratio()?
(2 answers)
Is there an alternative to the: as_integer_ratio(), for getting "cleaner" fractions?
(2 answers)
Closed 1 year ago.
QUESTION:
I would like to convert floats into a ratio of integers in simplest form. (Not a duplicate of this question, see "EDIT" below). For example, 0.1 = 1, 10, 0.66666... = 2, 3, etc. In the code snippet below, I try doing this for x = 0.1, 0.2, ..., 1.0 using this default function; the method only works successfully for x = 0.5 and x = 1.0. Why does this algorithm fail for other values of x and what is a better method to do this? In case it is relevant, my use-case will be for dx ~ 0.0005 = x[1] - x[0] for 0.0005 < x 10.0.
CODE:
import numpy as np
f = np.vectorize(lambda x : x.as_integer_ratio())
x = np.arange(0.1, 1.1, 0.1)
nums, dens = f(x)
for xi, numerator, denominator in zip(x, nums, dens):
print("\n .. {} = {} / {}\n".format(xi, numerator, denominator))
OUTPUT:
.. 0.1 = 3602879701896397 / 36028797018963968
.. 0.2 = 3602879701896397 / 18014398509481984
.. 0.30000000000000004 = 1351079888211149 / 4503599627370496
.. 0.4 = 3602879701896397 / 9007199254740992
.. 0.5 = 1 / 2
.. 0.6 = 5404319552844595 / 9007199254740992
.. 0.7000000000000001 = 6305039478318695 / 9007199254740992
.. 0.8 = 3602879701896397 / 4503599627370496
.. 0.9 = 8106479329266893 / 9007199254740992
.. 1.0 = 1 / 1
EDIT:
This is not really a duplicate. Both methods of the accepted answer in the original question fail a basic use-case from my MWE. To show that the Fraction module gives the same error:
import numpy as np
from fractions import Fraction
f = np.vectorize(lambda x : Fraction(x))
x = np.arange(0.1, 1.1, 0.1)
y = f(x)
print(y)
## OUTPUT
[Fraction(3602879701896397, 36028797018963968)
Fraction(3602879701896397, 18014398509481984)
Fraction(1351079888211149, 4503599627370496)
Fraction(3602879701896397, 9007199254740992) Fraction(1, 2)
Fraction(5404319552844595, 9007199254740992)
Fraction(6305039478318695, 9007199254740992)
Fraction(3602879701896397, 4503599627370496)
Fraction(8106479329266893, 9007199254740992) Fraction(1, 1)]

Condition indexing numpy array of floats

import numpy as np
n = 10
xmin = 0
xmax = 1
dx = 1/n
x = np.arange(xmin-dx, xmax + 2*dx, dx)
print(x)
print(x <= 0.3)
The output of this code is following :
[-0.1 0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. 1.1]
[ True True True True False False False False False False False False
False]
Why the element in array with value 0.3 is not smaller or equal than 0.3?
​I tried the same with other comparasions and i saw that -0.1 <= -0.1 and 0.1 <= 0.1 while 0.2 is not less or equal 0.2.
Really do not understand what is happening here.
I got it. Never compare float numbers, because of round-off error.
This will work:
print(x <= 0.3 + np.finfo(np.float64).eps)

How to specify y values in histogram?

I'm trying to write a program that takes in two arrays, one for y values and the other one for the bins, and generate a histogram that put the y values into the corresponding bins' range.
e.g. y = 0.5 when 0 < x< 0.1, y = 0.2 when 0.1 < x <0.2
I cloudn't find any ways to sepcify the y-values in histogram with matplotlib, or is it possible to do so ?
Ps. Can I use bar chart to do so ? but I didn't find a way to specify bins in bar chart , Thanks!
I think you need a bar plot, where you can specify the x values and "height". For instance:
import matplotlib.pyplot as plt
import numpy as np
a=np.array([1,2,3])
b=np.array([4,5,6])
plt.bar(a,b)
plt.show()
For example, you can fill the arrays with an if condition:
a=np.array([0.01,0.15,0.18,0.05,0.15])
b=np.zeros(len(a))
for i in range(len(a)):
if a[i] > 0. and a[i] < 0.1:
b[i] = 0.5
elif a[i] > 0.1 and a[i] < 0.2:
b[i] = 0.2

Resources