###################################################################################
# Copyright 2021 National Technology & Engineering Solutions of Sandia, #
# LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the #
# U.S. Government retains certain rights in this software. #
# If you want to use this code, please refer to the README.rst and LICENSE files. #
###################################################################################
import numpy as np
from PyNucleus_base import INDEX, REAL
from . mesh import mesh2d
from meshpy.triangle import MeshInfo, build
from scipy.spatial import cKDTree
import logging
LOGGER = logging.getLogger(__name__)
[docs]
class segment:
[docs]
def __init__(self, points, facets, holes=[]):
self.points = points
self.facets = facets
self.holes = holes
self.meshTransformations = []
def __add__(self, other):
if isinstance(other, (tuple, np.ndarray)):
newPoints = [(other[0]+p[0], other[1]+p[1]) for p in self.points]
newHoles = [(other[0]+p[0], other[1]+p[1]) for p in self.holes]
newSegment = segment(newPoints, self.facets, newHoles)
for t in self.meshTransformations:
def transform(x1, x2, xNew):
xTemp = xNew-other
t(x1-other, x2-other, xTemp)
xNew[:] = other+xTemp
newSegment.meshTransformations.append(transform)
return newSegment
elif isinstance(other, segment):
points = self.points+other.points
holes = self.holes+other.holes
facets = []
offset = len(self.points)
for f in self.facets:
facets.append(f)
for f in other.facets:
f2 = (f[0]+offset, f[1]+offset)
facets.append(f2)
kd = cKDTree(points)
idx = -np.ones((len(points)), dtype=INDEX)
idxUnique = -np.ones((len(points)), dtype=INDEX)
for t in kd.query_pairs(1e-6):
idx[max(t)] = min(t)
k = 0
for i in range(idx.shape[0]):
if idx[i] == -1:
idx[i] = k
idxUnique[k] = i
k += 1
else:
idx[i] = idx[idx[i]]
idxUnique = idxUnique[:k]
points = [points[i] for i in idxUnique]
facets = [(idx[f[0]], idx[f[1]]) for f in facets]
sumSeg = segment(points, facets, holes)
sumSeg.meshTransformations = self.meshTransformations+other.meshTransformations
return sumSeg
else:
raise NotImplementedError(other)
def __mul__(self, other):
if isinstance(other, tuple):
c = np.array(other[0])
angle = other[1]
rot = np.array([[np.cos(angle), -np.sin(angle)],
[np.sin(angle), np.cos(angle)]])
points = [c+rot.dot(p-c) for p in self.points]
holes = [c+rot.dot(p-c) for p in self.holes]
newSegment = segment(points, self.facets, holes)
for t in self.meshTransformations:
def transform(x1, x2, xNew):
xTemp = c+rot.T.dot(xNew-c)
t(c+rot.T.dot(x1-c),
c+rot.T.dot(x2-c),
xTemp)
xNew[:] = c+rot.dot(xTemp-c)
newSegment.meshTransformations.append(transform)
return newSegment
else:
raise NotImplementedError()
[docs]
def plot(self, plotArrows=False):
import matplotlib.pyplot as plt
plt.scatter([p[0] for p in self.points], [p[1] for p in self.points])
for f in self.facets:
plt.plot([self.points[f[0]][0], self.points[f[1]][0]],
[self.points[f[0]][1], self.points[f[1]][1]])
if plotArrows:
plt.arrow(self.points[f[0]][0], self.points[f[0]][1],
0.5*(self.points[f[1]][0]-self.points[f[0]][0]),
0.5*(self.points[f[1]][1]-self.points[f[0]][1]),
head_width=0.05, head_length=0.1)
[docs]
def get_num_points(self):
return len(self.points)
[docs]
def get_num_facets(self):
return len(self.facets)
[docs]
def get_num_holes(self):
return len(self.holes)
num_points = property(fget=get_num_points)
num_facets = property(fget=get_num_facets)
num_holes = property(fget=get_num_holes)
num_mesh_transformations = property(fget=get_num_mesh_transformations)
[docs]
def mesh(self, **kwargs):
mesh_info = MeshInfo()
mesh_info.set_points(self.points)
mesh_info.set_facets(self.facets)
mesh_info.set_holes(self.holes)
if 'min_angle' not in kwargs:
kwargs['min_angle'] = 30
if 'h' in kwargs:
h = kwargs.pop('h')
if 'href' in kwargs:
href = kwargs.pop('href')
for k in range(href):
fraction = 0.8**k
kwargs['max_volume'] = 0.5 * h**2 * fraction
mesh_meshpy = build(mesh_info, **kwargs)
mesh = mesh2d(np.array(mesh_meshpy.points, dtype=REAL),
np.array(mesh_meshpy.elements, dtype=INDEX))
if mesh.h <= h:
break
else:
LOGGER.warn("Meshed {} times, but could not achieve h={}. Instead h={}.".format(href, h, mesh.h))
else:
kwargs['max_volume'] = 0.5 * h**2
mesh_meshpy = build(mesh_info, **kwargs)
mesh = mesh2d(np.array(mesh_meshpy.points, dtype=REAL),
np.array(mesh_meshpy.elements, dtype=INDEX))
else:
mesh_meshpy = build(mesh_info, **kwargs)
mesh = mesh2d(np.array(mesh_meshpy.points, dtype=REAL),
np.array(mesh_meshpy.elements, dtype=INDEX))
mesh.setMeshTransformation(self.getMeshTransformer())
return mesh
[docs]
class circularSegment(segment):
[docs]
def __init__(self, center, radius, start_angle, stop_angle, num_points_per_unit_len=None, num_points=None):
if num_points_per_unit_len is None and num_points is None:
num_points = 9
elif num_points is None:
num_points = int(np.ceil(radius*(stop_angle-start_angle) * num_points_per_unit_len))+1
if stop_angle-start_angle < 1e-9:
points = []
facets = []
else:
if abs(stop_angle-start_angle-2*np.pi) < 1e-9:
points = [(center[0]+radius*np.cos(theta),
center[1]+radius*np.sin(theta)) for theta in np.linspace(start_angle, stop_angle, num_points-1, endpoint=False)]
facets = [(i, i+1) for i in range(num_points-2)]+[(num_points-2, 0)]
else:
points = [(center[0]+radius*np.cos(theta),
center[1]+radius*np.sin(theta)) for theta in np.linspace(start_angle, stop_angle, num_points)]
facets = [(i, i+1) for i in range(num_points-1)]
self.center = center
self.radius = radius
self.start_angle = start_angle
self.stop_angle = stop_angle
super(circularSegment, self).__init__(points, facets)
self.meshTransformations = [self.meshTransformation]
[docs]
class circle(circularSegment):
[docs]
def __init__(self, center, radius, num_points_per_unit_len=None, num_points=None):
super(circle, self).__init__(center, radius, 0, 2*np.pi, num_points_per_unit_len, num_points)
self.points.append(center)
[docs]
class line(segment):
[docs]
def __init__(self, start, end, num_points=None, num_points_per_unit_len=None):
length2 = (end[0]-start[0])**2 + (end[1]-start[1])**2
if num_points_per_unit_len is None and num_points is None:
num_points = 2
elif num_points_per_unit_len is not None:
length = np.sqrt(length2)
num_points = int(np.ceil(length*num_points_per_unit_len))+1
if length2 < 1e-9:
points = []
facets = []
else:
points = [(start[0]+t*(end[0]-start[0]),
start[1]+t*(end[1]-start[1])) for t in np.linspace(0, 1, num_points)]
facets = [(i, i+1) for i in range(num_points-1)]
super(line, self).__init__(points, facets)
[docs]
def polygon(points, doClose=True, num_points=None, num_points_per_unit_len=None):
if num_points is None:
num_points = [None]*len(points)
elif doClose:
assert len(num_points) == len(points)
else:
assert len(num_points) == len(points)-1
segments = line(points[0], points[1], num_points=num_points[0], num_points_per_unit_len=num_points_per_unit_len)
for i in range(1, len(points)-1):
segments += line(points[i], points[i+1], num_points=num_points[i], num_points_per_unit_len=num_points_per_unit_len)
if doClose:
segments += line(points[len(points)-1], points[0], num_points=num_points[len(points)-1], num_points_per_unit_len=num_points_per_unit_len)
return segments
[docs]
def rectangle(a, b, num_points=None, num_points_per_unit_len=None):
assert a[0] < b[0]
assert a[1] < b[1]
points = [a, (b[0], a[0]), b, (a[0], b[0])]
rect = polygon(points, doClose=True, num_points=num_points, num_points_per_unit_len=num_points_per_unit_len)
def meshTransformation(x1, x2, xNew):
eps = 1e-10
if ((a[0]-eps <= x1[0] <= b[0]+eps) and (a[1]-eps <= x1[1] <= b[1]+eps) and
(a[0]-eps <= x2[0] <= b[0]+eps) and (a[1]-eps <= x2[1] <= b[1]+eps)):
xNew[:] = 0.5*(x1+x2)
return True
rect.meshTransformation = [meshTransformation]
return rect