Mayavi combining two implicit 3d surfaces - python

I use Mayavi to plot implicit 3d surfaces and I would like to combine those surfaces into one plot. However, when I do this I get something similar like this:
The code I am using:
import numpy as np
from mayavi import mlab
A=0.24639243776
B=5.39100472027e-17
C=1.71555149594
D=1.72967325617
E=7.50535440036
F=-1.17072847143
G=1.0
x, y, z = np.ogrid[-3:1:100j, -10:1:100j, 0:3:100j]
def Fun (x,y,z, A,B,C,D,E,F,G):
F1 = (x - A + y - B) / 2 + np.sqrt(((x - A - y + B) / 2) ** 2 + C * z ** 2)
F2 = np.sqrt(x*((F*y)/2 + (E*x)/D) + y*((F*x)/2 + (D*y)/E) + G*z**2) - np.sqrt(D*E)
F1 [F1 < 0] = F2 [F1 < 0]
return F1
Fu = Fun (x,y,z,A,B,C,D,E,F,G)
mlab.contour3d(Fu, contours = [0])
mlab.show()
The problem is that no matter how I do it one of the surfaces is very low poly! I had plotted the same in Mathematica and it produces a perfect plot:
I do not expect to get the same quality plot as Mathematica is a commercial product. But at least I would like for both surfaces to be smooth.
If Mayavi is not the right tool for the task, maybe you could recommend something different with which I could achieve the desired result.
Thanks in advance!

I figured it out. The key is to set the surface to None for it to be trimmed. Then the other surface can be drawn in the same fashion. Furthermore for the surface to be plotted according to scale, x,y,z has to be also passed into the contour3d
import numpy as np
from mayavi import mlab
A=0.24639243776
B=5.39100472027e-17
C=1.71555149594
D=1.72967325617
E=7.50535440036
F=-1.17072847143
G=1.0
x, y, z = np.mgrid[-3:1:300j, -10:1:300j, 0:3:300j]
def Fun1 (x,y,z, A,B,C,D,E,F,G):
F1 = (x - A + y - B) / 2 + np.sqrt(((x - A - y + B) / 2) ** 2 + C * z ** 2)
F2 = np.sqrt(x*((F*y)/2 + (E*x)/D) + y*((F*x)/2 + (D*y)/E) + G*z**2) - np.sqrt(D*E)
F1 [F2 > 0.0] = None
return F1
def Fun2 (x,y,z, A,B,C,D,E,F,G):
F1 = (x - A + y - B) / 2 + np.sqrt(((x - A - y + B) / 2) ** 2 + C * z ** 2)
F2 = np.sqrt(x*((F*y)/2 + (E*x)/D) + y*((F*x)/2 + (D*y)/E) + G*z**2) - np.sqrt(D*E)
F2 [F1 > 0.0] = None
return F2
Fu1 = Fun1 (x,y,z,A,B,C,D,E,F,G)
Fu2 = Fun2 (x,y,z,A,B,C,D,E,F,G)
mlab.contour3d(x,y,z,Fu1, contours = [0])
mlab.contour3d(x,y,z,Fu2, contours = [0])
mlab.show()
And here is the result:

Related

Common tangent using python

I am trying to find a common tangent to two curves using python but I am not able to solve it.
The equations to the two curves are complicated that involve logarithms.
Is there a way in python to compute the x coordinates of a tangent that is common to both the curves in general. If I have 2 curves f(x) and g(x), I want to find the x-coordinates x1 and x2 on a common tangent where x1 lies on f(x) and x2 on g(x). I am trying f'(x1) = g'(x2) and f'(x1) = f(x1) - f(x2) / (x1 - x2) to get x1 and x2 but I am not able to get values using nonlinsolve as the equations are too complicated.
I want to just find x-coordinates of the common tangent
Can anyone suggest a better way?
import numpy as np
import sympy
from sympy import *
from matplotlib import pyplot as plt
x = symbols('x')
a, b, c, d, e, f = -99322.50019502985, -86864.87072433547, -96876.05627516498, -89703.35055202093, -3390.863799999999, -20942.518
def func(x):
y1_1 = a - a*x + b*x
y1_2 = c - c*x + d*x
c1 = (1 - x) ** (1 - x)
c2 = (x ** x)
y2 = 12471 * (sympy.log((c1*c2)))
y3 = 2*f*x**3 - x**2*(e + 3*f) + x*(e + f)
eqn1 = y1_1 + y2 + y3
eqn2 = y1_2 + y2 + y3
return eqn1, eqn2
val = np.linspace(0, 1)
f1 = sympy.lambdify(x, func(x)[0])(val)
f2 = sympy.lambdify(x, func(x)[1])(val)
plt.plot(val, f1)
plt.plot(val, f2)
plt.show()
I am trying this
x1, x2 = sympy.symbols('x1 x2')
fun1 = func(x1)[0]
fun2 = func(x2)[0]
diff1 = diff(fun1,x1)
diff2 = diff(fun2,x2)
eq1 = diff1 - diff2
eq2 = diff1 - ((fun1 - fun2) / (x1 - x2))
sol = nonlinsolve([eq1, eq2], [x1, x2])
the first thing that needs to be done is to reduce the formulas
for example the first formula is actually this:
formula = x*(1 - x)*(17551.6542 - 41885.036*x) + x*(1 - x)*(41885.036*x - 24333.3818) + 12457.6294706944*x + log((x/(1 - x))**(12000*x)*(1 - x)**12000) - 99322.5001950298
formula = (x-x^2)*(17551.6542 - 41885.036*x) + (x-x^2)*(41885.036*x - 24333.3818) + 12457.6294706944*x + log((x/(1 - x))**(12000*x)*(1 - x)**12000) - 99322.5001950298
# constants
a = 41885.036
b = 17551.6542
c = 24333.3818
d = 12457.6294706944
e = 99322.5001950298
f = 12000
formula = (x-x^2)*(b - a*x) + (x-x^2)*(a*x - c) + d*x + log((x/(1 - x))**(f*x)*(1 - x)**f) - e
formula = (ax^3 -bx^2 + bx - ax^2) + (x-x^2)*(a*x - c) + d*x + log((x/(1 - x))**(f*x)*(1 - x)**f) - e
formula = ax^3 -bx^2 + bx - ax^2 -ax^3 + ax^2 + cx^2 -cx + d*x + log((x/(1 - x))**(f*x)*(1 - x)**f) - e
# collect x terms by power (note how the x^3 tern drops out, so its easier).
formula = (c-b)*x^2 + (b-c+d)*x + log((x/(1 - x))**(f*x)*(1 - x)**f) - e
which is much cleaner and is a quadratic with a log term.
i expect that you can do some work on the log term too, but this is an excercise for the original poster.
likewise the second formula can be reduced in the same way, which is again an excercise for the original poster.
From this, both equations need to be differentiated with respect to x to find the tangent. Then set both formulas to be equal to each other (for a common tangent).
This would completely solve the question.
I actually wonder if this is a python question at all or actually a pure maths question.....
The important point to note is that, since the derivatives are monotonic, for any value of derivative of fun1, there is a solution for fun2. This can be easily seen if you plot both derivatives.
Thus, we want a function that, given an x1, returns an x2 that matches it. I'll use numerical solution because the system is too cumbersome for numerical solution.
import scipy.optimize
def find_equal_value(f1, f2, x, x1):
goal = f1.subs(x, x1)
to_solve = sympy.lambdify(x, (f2 - goal)**2) # Quadratic functions tend to be better behaved, and the result is the same
sol = scipy.optimize.fmin(func=to_solve, x0=x1, ftol=1e-8, disp=False) # The value for f1 is a good starting guess
return sol[0]
I used fmin as the solver above because it worked and I knew how to use it by heart. Maybe root_scalar can give better results.
Using the function above, let's get some pairs (x1, x2) where the derivatives are equal:
df1 = sympy.diff(func(x)[0])
df2 = sympy.diff(func(x)[1])
x1 = 0.25236537 # Close to the zero derivative
x2 = find_equal_value(df1, df2, x, x1)
print(f'Derivative of f1 in x1: {df1.subs(x, x1)}')
print(f'Derivative of f2 in x2: {df2.subs(x, x2)}')
print(f'Error: {df1.subs(x, x1) - df2.subs(x, x2)}')
This results is:
Derivative of f1 in x1: 0.0000768765858083498
Derivative of f2 in x2: 0.0000681969431752805
Error: 0.00000867964263306931
If you want a x2 for several x1s (beware that in some cases the solver hits a value where the logs are invalid. Always check your result for validity):
x1s = np.linspace(0.2, 0.8, 50)
x2s = [find_equal_value(df1, df2, x, x1) for x1 in x1s]
plt.plot(x1s, x2s); plt.grid(); plt.show()

Finding the intersect between a quadratic and line

I am trying to find the intersect between a straight line and a quadratic curve, however the result I am getting appears to be imaginary although I don't see how this can be the case as I can see them intersect on real axes:
Import numpy
#quadratic coefficients
a,b,c = (-3.09363812e-04, 1.52138019e+03, -1.87044961e+09)
# y = ax^2 + bx + c
#line coefficients
m,d = (1.06446434e-03, -2.61660911e+03)
#y = mx + d
intersect = (-(b-m)+((b-m)**2 - 4*a*(c-d))**0.5)/(2*a)
print(intersect)
The output of this is 2458883.4674943495-107.95731226786134j
I am trying to find the intersect between the yellow curve over the blue points and the black dotted line
The graphed curves you presented vs. your equations are not the same, and your equations do not intersect.
I rewrote some example code for you. numpy isn't needed, and an exact solution is possible.
import math
import collections
def calculateIntersection(p, l):
b = p.B - l.slope
c = p.C - l.yInt
discriminant = b**2 - (4 * p.A * c)
if discriminant > 0.0:
# 2 points of intersection
x1 = (-b + math.sqrt(discriminant)) / (2.0 * p.A)
x2 = (-b - math.sqrt(discriminant)) / (2.0 * p.A)
return discriminant, [(x1, l.slope * x1 + l.yInt), (x2, l.slope * x2 + l.yInt)]
elif discriminant == 0.0:
# 1 point of intersection
x1 = -b / (2.0 * p.A)
return discriminant, [(x1, slope * x1 + l.yInt)]
else:
# no points of intersection
return discriminant, []
Line = collections.namedtuple('Line', 'slope yInt')
Poly = collections.namedtuple('Poly', 'A B C')
p = Poly(A=-3.09363812e-04, B=1.52138019e+03, C=-1.87044961e+09)
print(p)
l = Line(slope=1.06446434e-03, yInt=-2.61660911e+03)
print(l)
(discriminant, points) = calculateIntersection(p, l)
if (len(points) > 0):
print("Intersection: {}".format(points))
else:
print("No intersection: {}".format(discriminant))

Solving a boundary value problem DE in python

I am trying to solve the following set of DE's:
dx' = cos(a)
dy' = sin(a)
dF' = - b * x * cos(a) + sin(a)
da' = (b * x * sin(a) + cos(a)) / F
with the conditions:
x(0) = y(0) = x(1) = 0
y(1) = 0.6
F(0) = 0.38
a(0) = -0.5
I tried following a similar problem, but I just can't get it to work. Is it possible, that my F(0) and a(0) are completely off, I am not even sure about them.
import numpy as np
from scipy.integrate import solve_bvp
import matplotlib.pyplot as plt
beta = 5
def fun(x, y):
x, dx, y, dy, F, dF, a, da, = y;
dxds=np.cos(a)
dyds=np.sin(a)
dFds=-beta * x * np.cos(a) + np.sin(a)
dads=(beta * x * np.sin(a) + np.cos(a) ) / F
return dx, dxds, dy, dyds, dF, dFds, da, dads
def bc(ya, yb):
return ya[0], yb[0], ya[2], yb[2] + 0.6, ya[4] + 1, yb[4] + 1, ya[6], yb[6]
x = np.linspace(0, 0.5, 10)
y = np.zeros((8, x.size))
y[4] = 0.38
y[6] = 2.5
res = solve_bvp(fun, bc, x, y)
print(res.message)
x_plot = np.linspace(0, 0.5, 200)
plt.plot(x_plot, res.sol(x_plot)[0])
I think that you have foremost a physics problem, translating the physical situation into an ODE system.
x(s) and y(s) are the coordinates of the rope where s is the length along the rope. Consequently, (x'(s),y'(s)) is a unit vector that is uniquely characterized by its angle a(s), giving
x'(s) = cos(a(s))
y'(s) = sin(a(s))
To get the shape, one now has to consider the mechanics. The assumption seems to be that the rope rotates without spiraling around the rotation axis, staying in one plane. Additionally, from the equilibrium of forces you also get that the other two equations are indeed first order, not second order equations. So your state only has 4 components and the ODE system function thus has to be
def fun(s, u):
x, y, F, a = u;
dxds=np.cos(a)
dyds=np.sin(a)
dFds=-beta * x * np.cos(a) + np.sin(a)
dads=(beta * x * np.sin(a) + np.cos(a) ) / F
return dxds, dyds, dFds, dads
Now there are only 4 boundary condition slots available, which are the coordinates of the start and end of the rope.
def bc(ua, ub):
return ua[0], ub[0], ua[1], ub[1] - 0.6
Additionally, the interval length for s is also the rope length, so a value of 0.5 is impossible for the given coordinates on the pole, try 1.0. There is some experimentation needed to get an initial guess that does not lead to a singular Jacobian in the BVP solver. In the end I get the solution in the x-y plane
with the components

computing smooth color map using interpolation recursively

I am computing the mandelbrot set recursively and attempting to perform linear interpolation using the smooth coloring algorithm. However, this returns floating point RGB values which I can't put into the ppm image I am using so I am having to round off using int(), creating a smoother but yet still banded image.
Are there any simpler ways that will produce a better non-banded image?
The second function is an extremely bad hack just playing around with ideas as the smooth algorithim seems to be producing rgb values in the range 256**3
Commented out the linear interpolation I was doing.
Here are my three functions:
def linear_interp(self, color_1, color_2, i):
r = (color_1[0] * (1 - i)) + (color_2[0] * i)
g = (color_1[1] * (1 - i)) + (color_2[1] * i)
b = (color_1[2] * (1 - i)) + (color_2[2] * i)
return (int(abs(r)), int(abs(g)), int(abs(b)))
def mandel(self, x, y, z, iteration = 0):
mod_z = sqrt((z.real * z.real) + (z.imag * z.imag))
#If its not in the set or we have reached the maximum depth
if abs(z) >= 2.00 or iteration == DEPTH:
if iteration == DEPTH:
mu = iteration
else:
mu = iteration + 1 - log(log(mod_z)) / log(2)
else:
mu = 0
z = (z * z) + self.c
self.mandel(x, y, z, iteration + 1)
return mu
def create_image(self):
begin = time.time() #For computing how long it took (start time)
self.rgb.palette = []
for y in range(HEIGHT):
self.rgb.palette.append([]) #Need to create the rows of our ppm
for x in range(WIDTH):
self.c = complex(x * ((self.max_a - self.min_a) / WIDTH) + self.min_a,
y * ((self.max_b - self.min_b) / HEIGHT) + self.min_b)
z = self.c
q = (self.c.real - 0.25)**2 + (self.c.imag * self.c.imag)
x = self.c.real
y2 = self.c.imag * self.c.imag
if not (q*(q + (x - 0.25)) < y2 / 4.0 or (x + 1.0)**2 + y2 <0.0625):
mu = self.mandel(x, y, z, iteration = 0)
rgb = self.linear_interp((255, 255, 0), (55, 55, 0), mu)
self.rgb.palette[y].append(rgb)
else:
self.rgb.palette[y].append((55, 55, 0))
if self.progress_bar != None:
self.progress_bar["value"] = y
self.canvas.update()
The image I am getting is below:
I think this is the culprit:
else:
mu = 0
self.mandel(x, y, z, iteration + 1)
return mu
This isn't passing down the value of mu from the recursive call correctly, so you're getting black for everything that doesn't bottom out after 1 call. Try
else:
...
mu = self.mandel(x, y, z, iteration + 1)
return mu

Calculate the Fourier series with the trigonometry approach

I try to implement the Fourier series function according to the following formulas:
...where...
...and...
Here is my approach to the problem:
import numpy as np
import pylab as py
# Define "x" range.
x = np.linspace(0, 10, 1000)
# Define "T", i.e functions' period.
T = 2
L = T / 2
# "f(x)" function definition.
def f(x):
return np.sin(np.pi * 1000 * x)
# "a" coefficient calculation.
def a(n, L, accuracy = 1000):
a, b = -L, L
dx = (b - a) / accuracy
integration = 0
for i in np.linspace(a, b, accuracy):
x = a + i * dx
integration += f(x) * np.cos((n * np.pi * x) / L)
integration *= dx
return (1 / L) * integration
# "b" coefficient calculation.
def b(n, L, accuracy = 1000):
a, b = -L, L
dx = (b - a) / accuracy
integration = 0
for i in np.linspace(a, b, accuracy):
x = a + i * dx
integration += f(x) * np.sin((n * np.pi * x) / L)
integration *= dx
return (1 / L) * integration
# Fourier series.
def Sf(x, L, n = 10):
a0 = a(0, L)
sum = 0
for i in np.arange(1, n + 1):
sum += ((a(i, L) * np.cos(n * np.pi * x)) + (b(i, L) * np.sin(n * np.pi * x)))
return (a0 / 2) + sum
# x axis.
py.plot(x, np.zeros(np.size(x)), color = 'black')
# y axis.
py.plot(np.zeros(np.size(x)), x, color = 'black')
# Original signal.
py.plot(x, f(x), linewidth = 1.5, label = 'Signal')
# Approximation signal (Fourier series coefficients).
py.plot(x, Sf(x, L), color = 'red', linewidth = 1.5, label = 'Fourier series')
# Specify x and y axes limits.
py.xlim([0, 10])
py.ylim([-2, 2])
py.legend(loc = 'upper right', fontsize = '10')
py.show()
...and here is what I get after plotting the result:
I've read the How to calculate a Fourier series in Numpy? and I've implemented this approach already. It works great, but it use the expotential method, where I want to focus on trigonometry functions and the rectangular method in case of calculating the integraions for a_{n} and b_{n} coefficients.
Thank you in advance.
UPDATE (SOLVED)
Finally, here is a working example of the code. However, I'll spend more time on it, so if there is anything that can be improved, it will be done.
from __future__ import division
import numpy as np
import pylab as py
# Define "x" range.
x = np.linspace(0, 10, 1000)
# Define "T", i.e functions' period.
T = 2
L = T / 2
# "f(x)" function definition.
def f(x):
return np.sin((np.pi) * x) + np.sin((2 * np.pi) * x) + np.sin((5 * np.pi) * x)
# "a" coefficient calculation.
def a(n, L, accuracy = 1000):
a, b = -L, L
dx = (b - a) / accuracy
integration = 0
for x in np.linspace(a, b, accuracy):
integration += f(x) * np.cos((n * np.pi * x) / L)
integration *= dx
return (1 / L) * integration
# "b" coefficient calculation.
def b(n, L, accuracy = 1000):
a, b = -L, L
dx = (b - a) / accuracy
integration = 0
for x in np.linspace(a, b, accuracy):
integration += f(x) * np.sin((n * np.pi * x) / L)
integration *= dx
return (1 / L) * integration
# Fourier series.
def Sf(x, L, n = 10):
a0 = a(0, L)
sum = np.zeros(np.size(x))
for i in np.arange(1, n + 1):
sum += ((a(i, L) * np.cos((i * np.pi * x) / L)) + (b(i, L) * np.sin((i * np.pi * x) / L)))
return (a0 / 2) + sum
# x axis.
py.plot(x, np.zeros(np.size(x)), color = 'black')
# y axis.
py.plot(np.zeros(np.size(x)), x, color = 'black')
# Original signal.
py.plot(x, f(x), linewidth = 1.5, label = 'Signal')
# Approximation signal (Fourier series coefficients).
py.plot(x, Sf(x, L), '.', color = 'red', linewidth = 1.5, label = 'Fourier series')
# Specify x and y axes limits.
py.xlim([0, 5])
py.ylim([-2.2, 2.2])
py.legend(loc = 'upper right', fontsize = '10')
py.show()
Consider developing your code in a different way, block by block. You should be surprised if a code like this would work at the first try. Debugging is one option, as #tom10 said. The other option is rapid prototyping the code step by step in the interpreter, even better with ipython.
Above, you are expecting that b_1000 is non-zero, since the input f(x) is a sinusoid with a 1000 in it. You're also expecting that all other coefficients are zero right?
Then you should focus on the function b(n, L, accuracy = 1000) only. Looking at it, 3 things are going wrong. Here are some hints.
the multiplication of dx is within the loop. Sure about that?
in the loop, i is supposed to be an integer right? Is it really an integer? by prototyping or debugging you would discover this
be careful whenever you write (1/L) or a similar expression. If you're using python2.7, you're doing likely wrong. If not, at least use a from __future__ import division at the top of your source. Read this PEP if you don't know what I am talking about.
If you address these 3 points, b() will work. Then think of a in a similar fashion.

Categories

Resources