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)

Categories

Resources