Python optimization algorithm to maximize the ellipse inside arbitrary polygon - python
I'm trying to make biggest ellipse inside arbitrary polygon.
So, I made the equation of ellipse and constraint .
objective : minimize A*B (mimimaze 1/area)
st. A**2*(x-x0)**2+B**2*(y-y0)**2 = 0
x/100.0 + y/80.0 < 1
x/-20.0 + y/80.0 < 1
x/-40.0 + y/-40.0 > 1
x/100.0 + y/-60.0 > 1
then I made optimization code using Scipy like below
But my code makes False result. (Success : False).
Can you explain why my code cannot make successful result? due to false constraint? wrong coding? Note that I don't need tilted ellipse.
import numpy as np
from scipy.optimize import minimize
def objective(C):
A = C[0] ; B = C[1] ; x0 = C[2] ; y0 = C[3] ; x = C[4] ; y = C[5]
return A*B
def constraint1(C):
A = C[0] ; B = C[1] ; x0 = C[2] ; y0 = C[3] ; x = C[4] ; y = C[5]
return (x/100.0+y/80.0-1)*-1
def constraint2(C):
A = C[0] ; B = C[1] ; x0 = C[2] ; y0 = C[3] ; x = C[4] ; y = C[5]
return (x/-100.0+y/80.0-1)*-1
def constraint3(C):
A = C[0] ; B = C[1] ; x0 = C[2] ; y0 = C[3] ; x = C[4] ; y = C[5]
return (x/-100.0+y/-80.0-1)*-1
def constraint4(C):
A = C[0] ; B = C[1] ; x0 = C[2] ; y0 = C[3] ; x = C[4] ; y = C[5]
return (x/100.0+y/-80.0-1)*-1
def constraint5(C):
A = C[0] ; B = C[1] ; x0 = C[2] ; y0 = C[3] ; x = C[4] ; y = C[5]
return A**2*(x-x0)**2+B**2*(y-y0)**2-1
xeval = [1,1,0,0,0,0]
bnds = ( (0.001,1),(0.001,1),(-100,100),(-100,100),(-500,500),(-500,500) )
con1 = {'type': 'ineq','fun':constraint1}
con2 = {'type': 'ineq' , 'fun':constraint2}
con3 = {'type': 'ineq','fun':constraint3}
con4 = {'type': 'ineq' , 'fun':constraint4}
con5 = {'type': 'eq' , 'fun':constraint5}
cons = [con1,con2,con3,con4,con5]
sol = minimize(objective,xeval,method='SLSQP',bounds=bnds,constraints=cons)
I made poor answer for some arbitrary polygon. I found using convex optimization might be bad for non-ideal polygon. I made this application to calculate lithography pw window. I hope someone make better solution.
# -*- coding: utf-8 -*-
"""
Created on Sat Apr 14 00:20:43 2018
#author: hafss
"""
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse
import numpy as np
step = 0.3
px_old = [1,8,11,20,25,40, 30,5 ,2 ,1,1 ]
py_old = [1,2,5, 4 ,2, 8 , 15,14,11,5,1 ]
init_point = [5,4]
init_radius = step
px = []
py = []
for i in range(len(px_old)-1):
dx = px_old[i+1]-px_old[i]
dy = py_old[i+1]-py_old[i]
len1 = (dx*dx+dy*dy)**0.5
px.append(px_old[i])
py.append(py_old[i])
if len1 > step:
count = int(len1/step)
for ii in range(count):
#print px_old[i]+ 1.0*ii/count*dx
#print py_old[i]+ 1.0*ii/count*dy
px.append(px_old[i]+ 1.0*ii/count*dx )
py.append(py_old[i]+ 1.0*ii/count*dy )
px.append(px_old[-1])
py.append(py_old[-1])
plt.figure()
plt.plot(px,py,color='r')
#plt.plot(px_old,py_old,)
ax = plt.gca()
minrr = 6
def check_inner(poly_x,poly_y,a,b,x0,y0 ):
minrr = 5
for i in range(len(poly_x)):
x=poly_x[i]
y=poly_y[i]
rr = ((x-x0)/a)**2+((y-y0)/b)**2
if rr < minrr:
minrr = rr
#if rr < 1 :
# badpoint.append([x,y])
return minrr
def draw_ellips(handle,xc,yc,a,b,color,lw):
ellipse = Ellipse(xy=(xc, yc), width=a*2, height=b*2,
edgecolor=color, fc='None', lw=lw)
handle.add_patch(ellipse)
def get_jumperpoint(poly_x,poly_y,sizeup,a,b,x0,y0 ):
badx = []
bady = []
for i in range(len(poly_x)):
x=poly_x[i]
y=poly_y[i]
rr = ((x-x0)/(a+sizeup))**2+((y-y0)/(b+sizeup))**2
if rr < 1 :
badx.append(x)
bady.append(y)
xmean = np.asarray(badx).mean()
ymean = np.asarray(bady).mean()
xmove = x0-xmean
ymove = y0-ymean
return [xmove,ymove]
rx = init_radius
ry = init_radius
xc = init_point[0]
yc = init_point[1]
maxarea = step*step
for iterat in range(90):
print "iterate start %s (%s,%s)->(%s,%s)" % (iterat,xc,yc,rx,ry)
s1 = step
s2 = step
s3 = step
draw_ellips(ax,xc,yc,rx,ry,'blue',0.5)
rxprev = rx
ryprev = ry
xcprev = xc
ycprev = yc
if check_inner(px,py,rx+s1,ry+s2,xc,yc) >= 1 :
rx = rx+s1
ry = ry+s2
# enlarge
if check_inner(px,py,rx+s1,ry,xc,yc) >= 1 :
rx = rx+s1
if check_inner(px,py,rx,ry+s1,xc,yc) >= 1 :
ry = ry+s1
#distortion
if check_inner(px,py,rx+s1,ry-s2,xc,yc) >= 1 and (rx+s1)*(ry-s2) > rx*ry :
rx = rx+s1
ry = ry-s2
if check_inner(px,py,rx-s1,ry+s2,xc,yc) >= 1 and (rx-s1)*(ry+s2) > rx*ry :
rx = rx-s1
ry = ry+s2
#shift # enlarge (right dir )
if check_inner(px,py,rx+s1,ry,xc+s2,yc) >= 1 :
rx = rx+s1
xc = xc+s2
if check_inner(px,py,rx+s1,ry,xc-s2,yc) >= 1 :
rx = rx+s1
xc = xc-s2
if check_inner(px,py,rx,ry+s1,xc,yc+s2) >= 1 :
ry = ry+s1
yc = yc+s2
if check_inner(px,py,rx,ry+s1,xc,yc-s2) >= 1 :
ry = ry+s1
yc = yc-s2
if check_inner(px,py,rx,ry,xc,yc) < 1 :
rx = rx-2*step-0.001
ry = ry-2*step-0.001
if rx < 0.001 : rx = 0.001
if ry < 0.001 : ry = 0.001
print "reduce"
print "iterate end %s (%s,%s)->(%s,%s) best %s " % (iterat,xc,yc,rx,ry,maxarea)
if rx == rxprev and ry == ryprev and xcprev == xc and ycprev== yc and check_inner(px,py,rx,ry,xc,yc) >= 1 :
[jx,jy] = get_jumperpoint(px,py,step*5,rx,ry,xc,yc )
print "jump %s %s -> %s %s" % (xc,yc,xc+jx,yc+jy)
xc = xc+jx
yc = yc+jy
if rx*ry > maxarea :
bestiter = iterat
bestx = xc
besty = yc
besta = rx
bestb = ry
maxarea = rx*ry
print "best iter %s center %s %s radius %s %s" % (bestiter,bestx,besty,besta,besty)
draw_ellips(ax,bestx,besty,besta,bestb,'red',2) # best ellipse
draw_ellips(ax,xc,yc,rx,ry,'green',3) # final iter
check_inner(px,py,11.8,4.3,13.6,9.4)
Related
Setting limits. I want QH = 1 if QH > 1 but I don't know how to do it or where to put it
The code is solving an integral using the trapezium rule. I need to set limits for QH so if QH > 1 then QH = 1. I cant seem to get what I've done below to work properly. ## Solve ODE QH = odeint(model, QH0, z, atol = 1.0e-8, rtol = 1.0e-8) QHe = odeint(model1, QHe0, z, atol = 1.0e-8, rtol = 1.8e-8) if QH > 1: QH == 1 if QHe > 1: QHe == 1 #Solving Thomson Optical Depth Integral for Hydrogen def f_hydrogen(z_in): Hz = H0*math.sqrt(OMEGAm*((1+z_in)**3)+OMEGAlam) flatQH = QH.flatten() QH_int = np.interp(z_in, z[::-1], flatQH[::-1]) return QH_int*(((1+z_in)**2)/Hz) a = 0 z1 = 7 n = 1000 hei = (z1-a)/n k = 0 #sum = 0 sum = np.zeros(n+1) while (k<n): x_in = a + (k*hei) if k < n-1 : sum[k + 1] = sum[k] + f_hydrogen(x_in) k = k + 1 int_a = (hei/2)*((f_hydrogen(a) + f_hydrogen(z1)) + (2*sum)) tH = (c)*(sigma)*(nbarH)*(1+(y/(4*x)))*(int_a) for index, val in enumerate(tH): print("Thomson Optical Depth - Hydrogen = ", index, val)
Solve waterhammer PDE in numpy/scipy
I’ve been trying to solve the water hammer PDE’s from the Maple example linked below in python (numpy/scipy). I’m getting very unstable results. Can anyone see my mistake? Guessing something is wrong with the boundary conditions. https://www.maplesoft.com/support/help/view.aspx?path=applications/WaterHammer import numpy as np from scipy.integrate import odeint import matplotlib.pyplot as plt ## Parameters Dia = 0.1 V = 14.19058741 # Stead state p = 1000 # Liquid density u = 0.001 # Viscosity L = 25 e = 0.0001 # Roughness Psource = 0.510E6 thick = 0.001 E= 7010*10**9 K=20010E6 Vsteady= 14.19058741 Ks = 1/((1/K)+(Dia/E*thick)) # Darcy-Weisbach def Friction(V): Rey = ((Dia*V*p)/u) fL = 64/Rey fT = 1/((1.8*np.log10((6.9/Rey) + (e/(3.7*Dia))**1.11))**2) if Rey >= 0 and Rey < 2000: return fL if Rey >= 2000 and Rey<4000: return fL + ((fT-fL)*(Rey-2000))/(4000-2000) if Rey >= 4000: return fT return 0 def model(D, t): V = D[:N] P = D[N:] dVdt = np.zeros(N) for i in range(1, len(dVdt)-1): dVdt[i] = -(1/p)*((P[i+1]-P[i-1])/2*dx)-((Friction(np.abs(V[i]))*(np.abs(V[i])**2))/(2*Dia)) dPdt = np.zeros(N) for i in range(1, len(dPdt)-1): dPdt[i] = -((V[i+1]-V[i-1])/(2*dx))*Ks if t < 2: dVdt[29] = 0 else: dVdt[29] = -1 dPdt[29] = 0 dVdt[0] = dVdt[1] return np.append(dVdt,dPdt) N = 30 x = np.linspace(0, L, N) dx = x[1] - x[0] ## Initial conditions Vi_0 = np.ones(N)*Vsteady Pi_0 = np.arange(N) for i in Pi_0: Pi_0[i] = Psource - (i*dx/L)*Psource # initial condition y0 = np.append(Vi_0, Pi_0) # time points t = np.linspace(0,3,10000) # solve ODE y = odeint(model,y0,t) Vr = y[:,0:N] Pr = y[:,N:] plt.plot(t,Pr[:,5])
solve_ivp error : Required step size is less than spacing between numbers
I have been trying to implement a model of unstable glacier flow in Python, solving the ODEs in scipy, with the RK45 method. The original model publication can be found here. Now, I think I understand what is going on with the error but I cannot find a way to fix it. I don't know if it comes from my implementation or from the ODEs themselves. I've been through the units several times, checking that all times were in seconds, all distances in meters and so on. I've tried with different t_eval and even different values of certain constants, but not been able to solve my problem. I started by creating a class with all constants. import numpy as np import scipy.integrate import matplotlib.pyplot as plt import astropy.units as u SECONDS_PER_YEAR = 3600*24*365.15 class Cst: #Glenn's flow Law A = 2.4e-25 n = 3. #Standard physical constants g = 10.#*(u.m)*(u.second**-2) rho = 916#*(u.kilogram*(u.m**-3)) #Thermodynamics cp = 2000#**(u.Joule)*(u.kilogram**-1)*(u.Kelvin**-1) L = 3.3e5#*(u.Joule)*(u.kilogram**-1) k = 2.1 #*(u.Watt)*(u.m**-1)*'(u.Kelvin**-1)' DDF = 0.1/SECONDS_PER_YEAR #*(u.m)*(u.yr**-1)*'(u.Kelvin**-1) K = 2.3e-47#*((3600*24*365.15)**9)#*((u.kilogram**-5)*(u.m**2)*(u.second**9)) C = 9.2e13#*((u.Pascal)*(u.Joule)*(u.m**-2)) #Weertman friction law q = 1 p = 1/3 R = 15.7#*((u.m**(-1/3))*(u.second**(1/3))) d = 10#*u.m sin_theta = 0.05 Tm = 0+273.15 #*u.Kelvin T_offset = -10+273.15#*u.Kelvin w = 0.6 #u.m Wc = 1000.#*u.m #Velocities u1 = 0/SECONDS_PER_YEAR #m/s u2 = 100/SECONDS_PER_YEAR # m/s #Dimensionless parameters alpha = 5. Then I declared the problem-specific parameters specified in the paper: #All values are from Table 1 a0 = 1./SECONDS_PER_YEAR#* m/s (u.meter*((u.second)**-1)) l0 = 10000#*(u.meter) E0 = 1.8e8#(Cst.g*Cst.sin_theta*a0*(l0**2))/(Cst.L*Cst.K))**(1/Cst.alpha)#*(u.Joule/u.m**2) T0 = 10#E0/(Cst.rho*Cst.cp*Cst.d)#*u.Kelvin w0 = 0.6#E0/(Cst.rho*Cst.L)#*u.m N0 = 0.5#Cst.C/E0#*u.Pascal H0 = 200 #((Cst.R*(Cst.C**Cst.q)*(a0**Cst.p)*(l0**Cst.p))/(Cst.rho*Cst.g*Cst.sin_theta*(E0**Cst.q)))**(1/(Cst.p+1)) t0 = 200 #H0/a0 u0 = 50/SECONDS_PER_YEAR#((Cst.rho*Cst.g*Cst.sin_theta*(E0**Cst.q)*a0*l0)/(Cst.R*(Cst.C**Cst.q)))**(1/(Cst.p+1)) Q0 = (Cst.g*Cst.sin_theta*a0*(l0**2))/Cst.L S0 = ((Cst.g*Cst.sin_theta*a0*(l0**2)*Cst.Wc)/(Cst.L*Cst.K*((Cst.rho*Cst.g*Cst.sin_theta)**(1/2))))**(3/4) lamb = ((2.*Cst.A*(Cst.rho*Cst.g*Cst.sin_theta)**Cst.n)*(H0**(Cst.n+1)))/((Cst.n+2)*u0) chi = N0/(Cst.rho*Cst.g*H0) gamma = 0.41 kappa = 0.7 phi = 0.2 delta = 66 mu = 0.2 Define the model : def model(t, x): #Initial values H_hat = x[0] E_hat = x[1] #Thickness H = H_hat*H0 #Enthalpy E_hat_plus = max(E_hat, 0) E_hat_minus = min(E_hat, 0) E_plus = E_hat_plus*E0 E_minus = E_hat_minus*E0 a_hat = 1. theta_hat = Cst.sin_theta/Cst.sin_theta l_hat =l0/l0 T_a = 0+273.15 T = -10+273.15 # Equation 3 m_hat = (Cst.DDF*(T_a-Cst.T_offset))/a0 S_hat = 0. T_a_hat = T_a/T0 #Equation A7 if E_plus > 0: N = min(H/chi, 1./E_plus) else: N = H/chi phi = min(1., E_plus/(H/chi)) #Equation 8 inv_p = 1./Cst.p u = (Cst.rho*Cst.g*Cst.sin_theta/Cst.R * H * (N**(-Cst.q)))**inv_p #Equation A7 beta = min(max(0, (u-Cst.u1)/(Cst.u2-Cst.u1)), 1) #Equation A4 dHdt_hat = ( a_hat - m_hat + 1./l_hat*( theta_hat**inv_p * H_hat**(1.+inv_p) * N**(-Cst.q*inv_p) + lamb*(theta_hat**Cst.n) ) ) #Equation A5 dEdt_hat = 1./mu*( theta_hat**(1+inv_p) * H_hat**(1.+inv_p) * N**(-Cst.q*inv_p) + gamma + kappa*(E_hat_minus - T_a_hat)/H_hat - 1./l_hat * ( theta_hat * E_hat_plus**Cst.alpha + phi * theta_hat**(1./2) * S_hat**(4/3.) ) + delta * beta * m_hat ) return [dHdt_hat, dEdt_hat] And finally call it : tmax = 200*SECONDS_PER_YEAR# *u.years t = np.linspace(0, tmax, 10000) sol = scipy.integrate.solve_ivp(model, t_span=[t[0], t[-1]], y0=[1, 1], t_eval=t, method='RK23') print(sol) Which yields message: 'Required step size is less than spacing between numbers.' nfev: 539 njev: 0 nlu: 0 sol: None status: -1 success: False t: array([0.]) t_events: None y: array([[1.], [1.]]) y_events: None
What is wrong with my Implementation of 4th Order runge kutta in python for nonholonomic constraints?
I am trying to implement 4th order Runge Kutta for nonholonomic motion for car-like robots. I don't know what I am doing wrong,essentially I am passing +-Pi/4 to calculate hard left and right turns to get different trajectories. But no matter if I pass +pi/4 or -pi/4 to it, I get the same answer. I cannot figure out what I am doing wrong. The constraint equations that I am using are: thetadot = (s/L)*tan(phi) xdot = s*cos(theta) ydot = s*sin(theta) Where s is the speed and L is the length of the car like robot. #! /usr/bin/env python import sys, random, math, pygame from pygame.locals import * from math import sqrt,cos,sin,atan2,tan import numpy as np import matplotlib.pyplot as plt XDIM = 640 YDIM = 480 WINSIZE = [XDIM, YDIM] PHI = 45 s = 0.5 white = 255, 240, 200 black = 20, 20, 40 red = 255, 0, 0 green = 0, 255, 0 blue = 0, 0, 255 cyan = 0,255,255 pygame.init() screen = pygame.display.set_mode(WINSIZE) X = XDIM/2 Y = YDIM/2 THETA = 45 def main(): nodes = [] nodes.append(Node(XDIM/2.0,YDIM/2.0,0.0)) plt.plot(runge_kutta(nodes[0], (3.14/4))) #Hard Left turn plt.plot(runge_kutta(nodes[0], 0)) #Straight ahead plt.plot(runge_kutta(nodes[0], -(3.14/4))) #Hard Right turn plt.show() class Node: x = 0 y = 0 theta = 0 distance=0 parent=None def __init__(self,xcoord, ycoord, theta): self.x = xcoord self.y = ycoord self.theta = theta def rk4(f, x, y, n): x0 = y0 = 0 vx = [0]*(n + 1) vy = [0]*(n + 1) h = 0.8 vx[0] = x = x0 vy[0] = y = y0 for i in range(1, n + 1): k1 = h*f(x, y) k2 = h*f(x + 0.5*h, y + 0.5*k1) k3 = h*f(x + 0.5*h, y + 0.5*k2) k4 = h*f(x + h, y + k3) vx[i] = x = x0 + i*h vy[i] = y = y + (k1 + k2 + k2 + k3 + k3 + k4)/6 print "1" print vy return vy def fun1(x,y): x = (0.5/2)*tan(y) print "2" print x return x def fun2(x,y): x = 0.5*cos(y) print "3" print x return x def fun3(x,y): x = 0.5*sin(y) print "4" print x return x def runge_kutta(p, phi): x1 = p.x y1 = p.y theta1 = p.theta fi = phi for i in range(0,5): x2 = rk4(fun2, x1, theta1, 5) y2 = rk4(fun3, y1, theta1, 5) theta2 = rk4(fun1, theta1 ,fi, 5) theta1 = theta2 print "5" print zip(x2,y2) return zip(x2,y2) # if python says run, then we should run if __name__ == '__main__': main() running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False
I can't really say much about the algorithm, but the way you set up our rk4 function, the x, and y arguments will never have any effect: def rk4(f, x, y, n): x0 = y0 = 0 # x0 and y0 will both be 0 after this vx = [0]*(n + 1) vy = [0]*(n + 1) h = 0.8 vx[0] = x = x0 # now x will be 0 vy[0] = y = y0 # and y will be 0 too ... The rest of the function will use x=0 and y=0 in any case. Also, I don't know if that's intentional, but the other functions fun1, fun2 and fun3 don't ever use the parameter passed as x, they only use y. They change x locally, but that won't reflect outside the function.
Mesh Generation for Computational Science in Python
I have a need for a Python module/package that provides a mesh on which I can do computational science? I am not doing graphics, so I don't think the blender package is what I want. Does anyone know of a good package?
The most useful packages out there are perhaps mshr, pygalmesh, dmsh, pygmsh, and MeshPy, meshzoo. In addition, there is optimesh for improving the quality of any mesh. (Disclaimer: I'm the author of pygmsh, pygalmesh, dmsh, meshzoo, and optimesh.)
If you're trying to solve FE or CFD style equations on a mesh you can use MeshPy in 2 and 3 dimensions. Meshpy is a nice wrapper around the existing tools tetgen and triangle. If you're looking for more typical graphics style meshes, there was an interesting talk at PyCon 2011 "Algorithmic Generation of OpenGL Geometry", which described a pragmatic approach to procedural mesh generation. The code from the presentation is available online If you're interested in reconstruction of surfaces from data, you can't go past the Standford 3D Scanning Repository, home of the Stanford Bunny Edit: A dependancy free alternative may be to use something like gmsh, which is platform independent, and uses similar tools to meshpy in its back-end.
I recommend using NumPy (especially if you've used MATLAB before). Many computational scientists / mechanical engineers working in python might agree, but I'm biased as it found it's way into much of the last year of my research. It's part of SciPy: http://numpy.scipy.org/ I was fond of numpy.linspace(a,b,N) which makes an N length vector of equally spaced values from a to b. You can use numpy.ndarray to make a N x M matrix, or if you want 2D arrays use numpy.meshgrid.
Here is code adapted from Kardontchik's port, import numpy as np from numpy import pi as pi from scipy.spatial import Delaunay import matplotlib.pylab as plt from scipy.optimize import fmin import matplotlib.pylab as plt def ktrimesh(p,bars,pflag=0): # create the (x,y) data for the plot xx1 = p[bars[:,0],0]; yy1 = p[bars[:,0],1] xx2 = p[bars[:,1],0]; yy2 = p[bars[:,1],1] xmin = np.min(p[:,0]) xmax = np.max(p[:,0]) ymin = np.min(p[:,1]) ymax = np.max(p[:,1]) xmin = xmin - 0.05*(xmax - xmin) xmax = xmax + 0.05*(xmax - xmin) ymin = ymin - 0.05*(ymax - ymin) ymax = ymax + 0.05*(ymax - ymin) plt.figure() for i in range(len(xx1)): xp = np.array([xx1[i],xx2[i]]) yp = np.array([yy1[i],yy2[i]]) plt.plot(xmin,ymin,'.',xmax,ymax,'.',markersize=0.1) plt.plot(xp,yp,'k') plt.axis('equal') if pflag == 0: stitle = 'Triangular Mesh' if pflag == 1: stitle = 'Visual Boundary Integrity Check' #plt.title('Triangular Mesh') plt.title(stitle) plt.xlabel('x') plt.ylabel('y') plt.show() return 1 def ccw_tri(p,t): """ orients all the triangles counterclockwise """ # vector A from vertex 0 to vertex 1 # vector B from vertex 0 to vertex 2 A01x = p[t[:,1],0] - p[t[:,0],0] A01y = p[t[:,1],1] - p[t[:,0],1] B02x = p[t[:,2],0] - p[t[:,0],0] B02y = p[t[:,2],1] - p[t[:,0],1] # if vertex 2 lies to the left of vector A the component z of # their vectorial product A^B is positive Cz = A01x*B02y - A01y*B02x a = t[np.where(Cz<0)] b = t[np.where(Cz>=0)] a[:,[1,2]] = a[:,[2,1]] t = np.concatenate((a, b)) return t def triqual_flag(p,t): # a(1,0), b(2,0), c(2,1) a = np.sqrt((p[t[:,1],0] - p[t[:,0],0])**2 + (p[t[:,1],1] - p[t[:,0],1])**2) b = np.sqrt((p[t[:,2],0] - p[t[:,0],0])**2 + (p[t[:,2],1] - p[t[:,0],1])**2) c = np.sqrt((p[t[:,2],0] - p[t[:,1],0])**2 + (p[t[:,2],1] - p[t[:,1],1])**2) A = 0.25*np.sqrt((a+b+c)*(b+c-a)*(a+c-b)*(a+b-c)) R = 0.25*(a*b*c)/A r = 0.5*np.sqrt( (a+b-c)*(b+c-a)*(a+c-b)/(a+b+c) ) q = 2.0*(r/R) min_edge = np.minimum(np.minimum(a,b),c) min_angle_deg = (180.0/np.pi)*np.arcsin(0.5*min_edge/R) min_q = np.min(q) min_ang = np.min(min_angle_deg) return min_q, min_ang def triqual(p,t,fh,qlim=0.2): # a(1,0), b(2,0), c(2,1) a = np.sqrt((p[t[:,1],0] - p[t[:,0],0])**2 + (p[t[:,1],1] - p[t[:,0],1])**2) b = np.sqrt((p[t[:,2],0] - p[t[:,0],0])**2 + (p[t[:,2],1] - p[t[:,0],1])**2) c = np.sqrt((p[t[:,2],0] - p[t[:,1],0])**2 + (p[t[:,2],1] - p[t[:,1],1])**2) A = 0.25*np.sqrt((a+b+c)*(b+c-a)*(a+c-b)*(a+b-c)) R = 0.25*(a*b*c)/A r = 0.5*np.sqrt( (a+b-c)*(b+c-a)*(a+c-b)/(a+b+c) ) q = 2.0*(r/R) pmid = (p[t[:,0]] + p[t[:,1]] + p[t[:,2]])/3.0 hmid = fh(pmid) Ah = A/hmid Anorm = Ah/np.mean(Ah) min_edge = np.minimum(np.minimum(a,b),c) min_angle_deg = (180.0/np.pi)*np.arcsin(0.5*min_edge/R) plt.figure() plt.subplot(3,1,1) plt.hist(q) plt.title('Histogram;Triangle Statistics:q-factor,Minimum Angle and Area') plt.subplot(3,1,2) plt.hist(min_angle_deg) plt.ylabel('Number of Triangles') plt.subplot(3,1,3) plt.hist(Anorm) plt.xlabel('Note: for equilateral triangles q = 1 and angle = 60 deg') plt.show() indq = np.where(q < qlim) # indq is a tuple: len(indq) = 1 if list(indq[0]) != []: print ('List of triangles with q < %5.3f and the (x,y) location of their nodes' % qlim) print ('') print ('q t[i] t[nodes] [x,y][0] [x,y][1] [x,y][2]') for i in indq[0]: print ('%.2f %4d [%4d,%4d,%4d] [%+.2f,%+.2f] [%+.2f,%+.2f] [%+.2f,%+.2f]' % \ (q[i],i,t[i,0],t[i,1],t[i,2],p[t[i,0],0],p[t[i,0],1],p[t[i,1],0],p[t[i,1],1],p[t[i,2],0],p[t[i,2],1])) print ('') # end of detailed data on worst offenders return q,min_angle_deg,Anorm class Circle: def __init__(self,xc,yc,r): self.xc, self.yc, self.r = xc, yc, r def __call__(self,p): xc, yc, r = self.xc, self.yc, self.r d = np.sqrt((p[:,0] - xc)**2 + (p[:,1] - yc)**2) - r return d class Rectangle: def __init__(self,x1,x2,y1,y2): self.x1, self.x2, self.y1, self.y2 = x1,x2,y1,y2 def __call__(self,p): x1,x2,y1,y2 = self.x1, self.x2, self.y1, self.y2 d1 = p[:,1] - y1 # if p inside d1 > 0 d2 = y2 - p[:,1] # if p inside d2 > 0 d3 = p[:,0] - x1 # if p inside d3 > 0 d4 = x2 - p[:,0] # if p inside d4 > 0 d = -np.minimum(np.minimum(np.minimum(d1,d2),d3),d4) return d class Polygon: def __init__(self,verts): self.verts = verts def __call__(self,p): verts = self.verts # close the polygon cverts = np.zeros((len(verts)+1,2)) cverts[0:-1] = verts cverts[-1] = verts[0] # initialize inside = np.zeros(len(p)) dist = np.zeros(len(p)) Cz = np.zeros(len(verts)) # z-components of the vectorial products dist_to_edge = np.zeros(len(verts)) in_ref = np.ones(len(verts)) # if np.sign(Cz) == in_ref then point is inside for j in range(len(p)): Cz = (cverts[1:,0] - cverts[0:-1,0])*(p[j,1] - cverts[0:-1,1]) - \ (cverts[1:,1] - cverts[0:-1,1])*(p[j,0] - cverts[0:-1,0]) dist_to_edge = Cz/np.sqrt( \ (cverts[1:,0] - cverts[0:-1,0])**2 + \ (cverts[1:,1] - cverts[0:-1,1])**2) inside[j] = int(np.array_equal(np.sign(Cz),in_ref)) dist[j] = (1 - 2*inside[j])*np.min(np.abs(dist_to_edge)) return dist class Union: def __init__(self,fd1,fd2): self.fd1, self.fd2 = fd1, fd2 def __call__(self,p): fd1,fd2 = self.fd1, self.fd2 d = np.minimum(fd1(p),fd2(p)) return d class Diff: def __init__(self,fd1,fd2): self.fd1, self.fd2 = fd1, fd2 def __call__(self,p): fd1,fd2 = self.fd1, self.fd2 d = np.maximum(fd1(p),-fd2(p)) return d class Intersect: def __init__(self,fd1,fd2): self.fd1, self.fd2 = fd1, fd2 def __call__(self,p): fd1,fd2 = self.fd1, self.fd2 d = np.maximum(fd1(p),fd2(p)) return d class Protate: def __init__(self,phi): self.phi = phi def __call__(self,p): phi = self.phi c = np.cos(phi) s = np.sin(phi) temp = np.copy(p[:,0]) rp = np.copy(p) rp[:,0] = c*p[:,0] - s*p[:,1] rp[:,1] = s*temp + c*p[:,1] return rp class Pshift: def __init__(self,x0,y0): self.x0, self.y0 = x0,y0 def __call__(self,p): x0, y0 = self.x0, self.y0 p[:,0] = p[:,0] + x0 p[:,1] = p[:,1] + y0 return p def Ellipse_dist_to_minimize(t,p,xc,yc,a,b): x = xc + a*np.cos(t) # coord x of the point on the ellipse y = yc + b*np.sin(t) # coord y of the point on the ellipse dist = (p[0] - x)**2 + (p[1] - y)**2 return dist class Ellipse: def __init__(self,xc,yc,a,b): self.xc, self.yc, self.a, self.b = xc, yc, a, b self.t, self.verts = self.pick_points_on_shape() def pick_points_on_shape(self): xc, yc, a, b = self.xc, self.yc, self.a, self.b c = np.array([xc,yc]) t = np.linspace(0,(7.0/4.0)*pi,8) verts = np.zeros((8,2)) verts[:,0] = c[0] + a*np.cos(t) verts[:,1] = c[1] + b*np.sin(t) return t, verts def inside_ellipse(self,p): xc, yc, a, b = self.xc, self.yc, self.a, self.b c = np.array([xc,yc]) r, phase = self.rect_to_polar(p-c) r_ellipse = self.rellipse(phase) in_ref = np.ones(len(p)) inside = 0.5 + 0.5*np.sign(r_ellipse-r) return inside def rect_to_polar(self,p): r = np.sqrt(p[:,0]**2 + p[:,1]**2) phase = np.arctan2(p[:,1],p[:,0]) # note: np.arctan2(y,x) order; phase in +/- pi (+/- 180deg) return r, phase def rellipse(self,phi): a, b = self.a, self.b r = a*b/np.sqrt((b*np.cos(phi))**2 + (a*np.sin(phi))**2) return r def find_closest_vertex(self,point): t, verts = self.t, self.verts dist = np.zeros(len(t)) for i in range(len(t)): dist[i] = (point[0] - verts[i,0])**2 + (point[1] - verts[i,1])**2 ind = np.argmin(dist) t0 = t[ind] return t0 def __call__(self,p): xc, yc, a, b = self.xc, self.yc, self.a, self.b t, verts = self.t, self.verts dist = np.zeros(len(p)) inside = self.inside_ellipse(p) for j in range(len(p)): t0 = self.find_closest_vertex(p[j]) # initial guess to minimizer opt = fmin(Ellipse_dist_to_minimize,t0, \ args=(p[j],xc,yc,a,b),full_output=1,disp=0) # add full_output=1 so we can retrieve the min dist(squared) # (2nd argument of opt array, 1st argument is the optimum t) min_dist = np.sqrt(opt[1]) dist[j] = min_dist*(1 - 2*inside[j]) return dist def distmesh(fd,fh,h0,xmin,ymin,xmax,ymax,pfix,ttol=0.1,dptol=0.001,Iflag=1,qmin=1.0): geps = 0.001*h0; deltat = 0.2; Fscale = 1.2 deps = h0 * np.sqrt(np.spacing(1)) random_seed = 17 h0x = h0; h0y = h0*np.sqrt(3)/2 # to obtain equilateral triangles Nx = int(np.floor((xmax - xmin)/h0x)) Ny = int(np.floor((ymax - ymin)/h0y)) x = np.linspace(xmin,xmax,Nx) y = np.linspace(ymin,ymax,Ny) # create the grid in the (x,y) plane xx,yy = np.meshgrid(x,y) xx[1::2] = xx[1::2] + h0x/2.0 # shifts even rows by h0x/2 p = np.zeros((np.size(xx),2)) p[:,0] = np.reshape(xx,np.size(xx)) p[:,1] = np.reshape(yy,np.size(yy)) p = np.delete(p,np.where(fd(p) > geps),axis=0) np.random.seed(random_seed) r0 = 1.0/fh(p)**2 p = np.concatenate((pfix,p[np.random.rand(len(p))<r0/max(r0),:])) pold = np.inf Num_of_Delaunay_triangulations = 0 Num_of_Node_movements = 0 # dp = F*dt while (1): Num_of_Node_movements += 1 if Iflag == 1 or Iflag == 3: # Newton flag print ('Num_of_Node_movements = %3d' % (Num_of_Node_movements)) if np.max(np.sqrt(np.sum((p - pold)**2,axis = 1))) > ttol: Num_of_Delaunay_triangulations += 1 if Iflag == 1 or Iflag == 3: # Delaunay flag print ('Num_of_Delaunay_triangulations = %3d' % \ (Num_of_Delaunay_triangulations)) pold = p tri = Delaunay(p) # instantiate a class t = tri.vertices pmid = (p[t[:,0]] + p[t[:,1]] + p[t[:,2]])/3.0 t = t[np.where(fd(pmid) < -geps)] bars = np.concatenate((t[:,[0,1]],t[:,[0,2]], t[:,[1,2]])) bars = np.unique(np.sort(bars),axis=0) if Iflag == 4: min_q, min_angle_deg = triqual_flag(p,t) print ('Del iter: %3d, min q = %5.2f, min angle = %3.0f deg' \ % (Num_of_Delaunay_triangulations, min_q, min_angle_deg)) if min_q > qmin: break if Iflag == 2 or Iflag == 3: ktrimesh(p,bars) # move mesh points based on bar lengths L and forces F barvec = p[bars[:,0],:] - p[bars[:,1],:] L = np.sqrt(np.sum(barvec**2,axis=1)) hbars = 0.5*(fh(p[bars[:,0],:]) + fh(p[bars[:,1],:])) L0 = hbars*Fscale*np.sqrt(np.sum(L**2)/np.sum(hbars**2)) F = np.maximum(L0-L,0) Fvec = np.column_stack((F,F))*(barvec/np.column_stack((L,L))) Ftot = np.zeros((len(p),2)) n = len(bars) for j in range(n): Ftot[bars[j,0],:] += Fvec[j,:] # the : for the (x,y) components Ftot[bars[j,1],:] -= Fvec[j,:] # force = 0 at fixed points, so they do not move: Ftot[0: len(pfix),:] = 0 # update the node positions p = p + deltat*Ftot # bring outside points back to the boundary d = fd(p); ix = d > 0 # find points outside (d > 0) dpx = np.column_stack((p[ix,0] + deps,p[ix,1])) dgradx = (fd(dpx) - d[ix])/deps dpy = np.column_stack((p[ix,0], p[ix,1] + deps)) dgrady = (fd(dpy) - d[ix])/deps p[ix,:] = p[ix,:] - np.column_stack((dgradx*d[ix], dgrady*d[ix])) # termination criterium: all interior nodes move less than dptol: if max(np.sqrt(np.sum(deltat*Ftot[d<-geps,:]**2,axis=1))/h0) < dptol: break final_tri = Delaunay(p) # another instantiation of the class t = final_tri.vertices pmid = (p[t[:,0]] + p[t[:,1]] + p[t[:,2]])/3.0 # keep the triangles whose geometrical center is inside the shape t = t[np.where(fd(pmid) < -geps)] bars = np.concatenate((t[:,[0,1]],t[:,[0,2]], t[:,[1,2]])) # delete repeated bars #bars = unique_rows(np.sort(bars)) bars = np.unique(np.sort(bars),axis=0) # orient all the triangles counterclockwise (ccw) t = ccw_tri(p,t) # graphical output of the current mesh ktrimesh(p,bars) triqual(p,t,fh) return p,t,bars def boundary_bars(t): # create the bars (edges) of every triangle bars = np.concatenate((t[:,[0,1]],t[:,[0,2]], t[:,[1,2]])) # sort all the bars data = np.sort(bars) # find the bars that are not repeated Delaunay_bars = dict() for row in data: row = tuple(row) if row in Delaunay_bars: Delaunay_bars[row] += 1 else: Delaunay_bars[row] = 1 # return the keys of Delaunay_bars whose value is 1 (non-repeated bars) bbars = [] for key in Delaunay_bars: if Delaunay_bars[key] == 1: bbars.append(key) bbars = np.asarray(bbars) return bbars def plot_shapes(xc,yc,r): # circle for plotting t_cir = np.linspace(0,2*pi) x_cir = xc + r*np.cos(t_cir) y_cir = yc + r*np.sin(t_cir) plt.figure() plt.plot(x_cir,y_cir) plt.grid() plt.title('Shapes') plt.xlabel('x') plt.ylabel('y') plt.axis('equal') #plt.show() return plt.close('all') xc = 0; yc = 0; r = 1.0 x1,y1 = -1.0,-2.0 x2,y2 = 2.0,3.0 plot_shapes(xc,yc,r) xmin = -1.5; ymin = -1.5 xmax = 1.5; ymax = 1.5 h0 = 0.4 pfix = np.zeros((0,2)) # null 2D array, no fixed points provided fd = Circle(xc,yc,r) fh = lambda p: np.ones(len(p)) p,t,bars = distmesh(fd,fh,h0,xmin,ymin,xmax,ymax,pfix,Iflag=4)