simple-mesh-py/mesh.py

175 lines
5.3 KiB
Python

#!/usr/bin/python3
import numpy as np
from scipy.spatial import Delaunay
import matplotlib.pyplot as plt
import shapely.geometry as geo
#import sys
#np.set_printoptions(threshold=sys.maxsize)
def get_mesh(fd,fh,h0,bbox,pfix):
'''
A simple mesh generator based on:
Persson, G. Strang, A Simple Mesh Generator in MATLAB.
SIAM Review, Volume 46 (2), pp. 329-345, June 2004
'''
geps = 0.001*h0
ttol = 0.1
Fscale = 1.2
deltat = 0.2
deps = np.sqrt(np.finfo(float).eps)*h0
dptol = 0.001
maxIter = 500
X, Y = np.meshgrid(np.arange(bbox[0,0],bbox[1,0]+h0,h0),\
np.arange(bbox[0,1],bbox[1,1]+h0*0.5*np.sqrt(3),h0*0.5*np.sqrt(3)))
X[1:-1:2,:] += (0.5*h0)
p = np.column_stack((X.flatten('F'),Y.flatten('F')))
p = p[np.nonzero(np.apply_along_axis(fd,1,p) < geps)].squeeze()
r0 = 1/np.apply_along_axis(fh,1,p)**2
R0 = np.max(r0)
p = p[np.random.rand(p.shape[0]) < r0/R0]
if len(pfix):
p = np.vstack((pfix, p))
N = p.shape[0]
pold = np.Inf
counter = 0
while True:
if np.max(np.sqrt(np.sum((p-pold)**2,1))/h0) > ttol:
pold = np.copy(p)
T = Delaunay(p)
tri = T.simplices
pmid = (p[tri[:,0],:]+p[tri[:,1],:]+p[tri[:,2],:])/3
inner = np.apply_along_axis(fd,1,pmid) < -geps
tri = tri[inner,:]
bars = np.vstack((tri[:,[0,1]],tri[:,[1,2]],tri[:,[0,2]]))
bars.sort(1)
bars = remove_duplicate_row(bars)
bvec = p[bars[:,0],:] - p[bars[:,1],:]
L = np.sqrt(np.sum(bvec**2,1))
hbars = np.apply_along_axis(fh, 1,(p[bars[:,0],:]+p[bars[:,1],:])/2)
L0 = hbars*Fscale*np.sqrt(np.sum(L**2)/np.sum(hbars**2))
F = [np.max(L0 - [L], 0)]
Fvec = (F/L).T * bvec
#sparse and back to full does not work the same in python as in matlab
#forced to do the loop, figure out better way
row = bars[:,[0,0,1,1]].flatten('F')
column = (np.ones(np.shape(F),dtype=np.int32).T @ [[0,1,0,1]]).flatten('F')
Ffrac = np.hstack((Fvec,-Fvec)).flatten('F')
Ftot = np.zeros([N,2])
for i in range(len(row)):
Ftot[row[i],column[i]] += Ffrac[i]
if len(pfix):
Ftot[0:pfix.shape[0]-1,:] = 0
p += (deltat*Ftot)
#bring back to boundary
d = np.apply_along_axis(fd,1,p)
ix = d>0 #index of points outside boundary
dx = np.column_stack((p[ix,0]+deps, p[ix,1]))
dy = np.column_stack((p[ix,0], p[ix,1]+deps))
gradx = (np.apply_along_axis(fd,1,dx)-d[ix])/deps
grady = (np.apply_along_axis(fd,1,dy)-d[ix])/deps
p[ix,:] -= np.column_stack((d[ix]*gradx,d[ix]*grady))
#print(np.max(np.sqrt(np.sum(deltat*Ftot[d<-geps,:]**2,1))/h0))
counter += 1
#print(counter)
if Ftot[d<-geps,:].size == 0 or counter > maxIter:
print(counter)
return p, tri
if np.max(np.sqrt(np.sum(deltat*Ftot[d<-geps,:]**2,1))/h0) < dptol:
print(counter)
return p, tri
def remove_duplicate_row(M):
#remove dupplicate rows of the array
A = np.ascontiguousarray(M).view(np.dtype((np.void,M.dtype.itemsize*M.shape[1])))
_, idx = np.unique(A, return_index=True)
return M[idx]
def print_mesh(p, tri, title='', output=''):
plt.figure()
plt.triplot(p[:,0], p[:,1], tri, color='black')
plt.axis('equal')
plt.axis(False)
if title:
plt.title(title)
if output:
plt.savefig(output)
plt.close()
return
plt.show()
return
def ddiff(d1, d2):
return max(d1, -d2)
def dunion(d1, d2):
return min(d1, d2)
def dintersect(d1, d2):
return max(d1, d2)
def dcircle(p, xc, yc, r):
return np.sqrt((p[0]-xc)**2+(p[1]-yc)**2)-r
def drectangle(p, x1, x2, y1, y2):
return -min(min(min(-y1+p[1],y2-p[1]),-x1+p[0]), x2-p[0])
def protate(p, phi):
A = np.array([[np.cos(phi),-np.sin(phi)],[np.sin(phi),np.cos(phi)]])
return (A@p.T).T
def _dpoly(p, poly, ring):
ds = ring.distance(p)
inside = ring.contains(p) or poly.contains(p)
return (-1)**inside*ds
def dpoly(p, poly):
return _dpoly(geo.asPoint(p), geo.asPolygon(poly),geo.asLinearRing(poly))
if __name__ == "__main__":
#print("test 1")
#fd = lambda x: ddiff(dcircle(x, 0, 0, 1), dcircle(x, 0.5, -0.2, 0.2))
#fh = lambda x: 0.8
#bbox = np.array([[-1, -1],[1, 1]])
#p, tri = get_mesh(fd, fh, 0.1, bbox, [])
#print_mesh(p, tri)
print("test 2")
import time
poly = np.array([[0,0],[0,1],[1,1],[1,2],[2,2],[2,1],[3,1],[3,0]])
polyG = geo.asPolygon(poly)
ringG = geo.asLinearRing(poly)
fd = lambda x: dpoly(x,poly)
fh = lambda x: 0.15-0.22*fd(x)
bbox = np.array([[0,0],[3,3]])
t = time.time()
p, tri = get_mesh(fd, fh, 0.1, bbox, poly)
elap = time.time()-t
print("prvi = {}s".format(elap))
fd = lambda x: _dpoly(geo.asPoint(x), polyG, ringG)
fh = lambda x: 0.15-0.22*fd(x)
t = time.time()
p, tri = get_mesh(fd, fh, 0.1, bbox, poly)
elap = time.time()-t
print("drugi = {}s".format(elap))
print_mesh(p, tri)
#print("test 3")
#fd = lambda x: drectangle(protate(x,np.pi/4),0,1,0,1)
#bbox = np.array([[-2,-2],[2,2]])
#p, tri = get_mesh(fd, fh, 0.08, bbox, [])
#print_mesh(p, tri)