Added a delaunay triangulating grid that uses scipy.spatial faculties

This commit is contained in:
Stephen M. McQuay 2011-09-17 19:23:21 -06:00
parent 2f6c87dd4f
commit fcb2bf2931
2 changed files with 30 additions and 314 deletions

View File

@ -1,184 +1,27 @@
from collections import defaultdict
import pickle import pickle
from xml.dom.minidom import Document
import numpy as np
from scipy.spatial import KDTree
from interp.baker import interpolate from interp.baker import interpolate
from interp.baker import get_phis from interp.baker import get_phis
import interp import interp
import logging
log = logging.getLogger("interp")
MAX_SEARCH_COUNT = 256
TOL = 1e-8
__version__ = interp.__version__ __version__ = interp.__version__
class grid(object): class grid(object):
def __init__(self, verts=None, q=None): def __init__(self):
""" """
verts = array of arrays (if passed in, will convert to numpy.array) Child classes should populate at a minimum the points and values
[ arrays, and a method for getting a simplex and extra points.
[x0,y0 <, z0>],
[x1,y1 <, z1>],
...
]
q = array (1D) of physical values
""" """
pass
if verts != None: def get_simplex_extra_points(self, X, extra_points=8):
self.verts = np.array(verts) pass
self.tree = KDTree(self.verts)
if q != None:
self.q = np.array(q)
self.cells = {}
self.cells_for_vert = defaultdict(list)
def get_containing_simplex(self, X):
if not self.cells:
raise Exception("cell connectivity is not set up")
# get closest point
(dist, indicies) = self.tree.query(X, 2)
closest_point = indicies[0]
log.debug('X: %s' % X)
log.debug('point index: %d' % closest_point)
log.debug('actual point %s' % self.verts[closest_point])
log.debug('distance = %0.4f' % dist[0])
simplex = None
checked_cells = []
cells_to_check = list(self.cells_for_vert[closest_point])
attempts = 0
while not simplex and cells_to_check:
attempts += 1
if attempts > MAX_SEARCH_COUNT:
raise Exception("Is the search becoming exhaustive?'\
'(%d attempts)" % attempts)
cur_cell = cells_to_check.pop(0)
checked_cells.append(cur_cell)
if cur_cell.contains(X, self):
simplex = cur_cell
continue
for neighbor in cur_cell.neighbors:
if (neighbor not in checked_cells) \
and (neighbor not in cells_to_check):
cells_to_check.append(neighbor)
if not simplex:
raise Exception('no containing simplex found')
log.debug("simplex vert indicies: %s" % simplex.verts)
R = self.create_mesh(simplex.verts)
log.debug("R:\n%s", R)
log.debug('total attempts before finding simplex: %d' % attempts)
return R
def create_mesh(self, indicies):
"""
this function takes a list of indicies, and then creates and
returns a grid object (collection of verts and q).
note: the input is indicies, the grid contains verts
"""
return grid(self.verts[indicies], self.q[indicies])
def get_simplex_and_nearest_points(self, X, extra_points=3):
"""
this returns two grid objects: R and S.
R is a grid object that is a containing simplex around point X
S : some verts from all points that are not the simplex
"""
simplex_size = self.dim + 1
log.debug("extra verts: %d" % extra_points)
log.debug("simplex size: %d" % simplex_size)
r_mesh = self.get_containing_simplex(X)
# and some UNIQUE extra verts
(dist, indicies) = self.tree.query(X, simplex_size + extra_points)
log.debug("extra indicies: %s" % indicies)
unique_indicies = []
for index in indicies:
close_point_in_R = False
for rvert in r_mesh.verts:
if all(rvert == self.verts[index]):
close_point_in_R = True
break
if not close_point_in_R:
unique_indicies.append(index)
else:
log.debug('throwing out %s: %s' % (index, self.verts[index]))
log.debug("indicies: %s" % indicies)
log.debug("unique indicies: %s" % unique_indicies)
s_mesh = self.create_mesh(unique_indicies)
return (r_mesh, s_mesh)
def interpolate(self, X, order=2, extra_points=3): def interpolate(self, X, order=2, extra_points=3):
(R, S) = self.get_simplex_and_nearest_points(X, extra_points) r, s = self.get_simplex_extra_points(X, extra_points=extra_points)
answer = interpolate(X, R, S, order) return interpolate(X, self.points[r], self.values[r],
return answer self.points[s], self.values[s], order=order)
def for_qhull_generator(self):
"""
this returns a generator that should be fed into qdelaunay
"""
yield str(len(self.verts[0]))
yield '%d' % len(self.verts)
for p in self.verts:
yield "%f %f %f" % tuple(p)
def for_qhull(self):
"""
this returns a single string that should be fed into qdelaunay
"""
r = '%d\n' % len(self.verts[0])
r += '%d\n' % len(self.verts)
for p in self.verts:
# r += "%f %f %f\n" % tuple(p)
r += "%s\n" % " ".join("%f" % i for i in p)
return r
def __str__(self):
r = ''
assert(len(self.verts) == len(self.q))
for c, i in enumerate(zip(self.verts, self.q)):
r += "%d vert(%s): q(%0.4f)" % (c, i[0], i[1])
cell_str = ", ".join([str(f.name) for f in self.cells_for_vert[c]])
r += " cells: [%s]" % cell_str
r += "\n"
if self.cells:
for v in self.cells.itervalues():
r += "%s\n" % v
return r
def normalize_q(self, new_max=0.1):
largest_number = np.max(np.abs(self.q))
self.q *= new_max / largest_number
def dump_to_blender_files(self, def dump_to_blender_files(self,
pfile='/tmp/points.p', cfile='/tmp/cells.p'): pfile='/tmp/points.p', cfile='/tmp/cells.p'):
@ -192,81 +35,13 @@ class grid(object):
pickle.dump([f.verts for f in self.cells.itervalues()], pickle.dump([f.verts for f in self.cells.itervalues()],
open(cfile, 'w')) open(cfile, 'w'))
def get_xml(self):
doc = Document()
ps = doc.createElement("points")
doc.appendChild(ps)
for i in zip(self.verts, self.q):
p = doc.createElement("point")
p.setAttribute("x", str(i[0][0]))
p.setAttribute('y', str(i[0][1]))
p.setAttribute('z', str(i[0][2]))
p.setAttribute('q', str(i[1]))
ps.appendChild(p)
return doc
def toxml(self):
return self.get_xml().toxml()
def toprettyxml(self):
return self.get_xml().toprettyxml()
class cell(object):
def __init__(self, name):
self.name = name
self.verts = []
self.neighbors = []
def add_vert(self, v):
"""
v should be an index into grid.verts
"""
self.verts.append(v)
def add_neighbor(self, n):
"""
reference to another cell object
"""
self.neighbors.append(n)
def contains(self, X, G):
"""
X = point of interest
G = corrensponding grid object (G.verts)
because of the way i'm storing things, a cell simply stores
indicies, and so one must pass in a reference to the grid object
containing real verts.
this simply calls grid.simplex.contains
"""
return contains(X, [G.verts[i] for i in self.verts])
def __str__(self):
# neighbors = [str(i.name) for i in self.neighbors]
return '<cell %s: verts: %s neighbor count: %s>' %\
(
self.name,
self.verts,
len(self.neighbors),
# ", ".join(neighbors)
)
__repr__ = __str__
def contains(X, R): def contains(X, R):
""" """
tests if X (point) is in R tests if X (point) is in R
R is a simplex, represented by a list of n-degree coordinates R is a simplex, represented by a list of N-degree coordinates
""" """
phis = get_phis(X, R) phis = get_phis(X, R)
any_negatives = any(map(lambda x: x < 0, phis))
r = True return not any_negatives
if [i for i in phis if i < 0.0 - TOL]:
r = False
return r

View File

@ -1,86 +1,27 @@
import re import scipy.spatial
import logging
log = logging.getLogger("interp") from interp.grid import grid as basegrid
from interp.grid import grid as basegrid, cell
from subprocess import Popen, PIPE def get_simplex_extra_points(X, points, triangulation, kdtree, extra_points=8):
simplex_id = triangulation.find_simplex(X)
simplex_verts_ids = set(triangulation.vertices[simplex_id])
def get_qdelaunay_dump(g): distances, kdt_ids = kdtree.query(X, extra_points + len(simplex_verts_ids))
""" kdt_ids = set(kdt_ids)
pass in interp.grid g, and get back lines from a qhull triangulation:
qdelaunay Qt f simplex_ids = list(simplex_verts_ids)
""" extra_points_ids = list(kdt_ids - simplex_verts_ids)
cmd = 'qdelaunay Qt f'
p = Popen(cmd.split(), bufsize=1, stdin=PIPE, stdout=PIPE)
so, se = p.communicate(g.for_qhull())
for i in so.splitlines():
yield i
def get_qdelaunay_dump_str(g): return simplex_ids, extra_points_ids
return "\n".join(get_qdelaunay_dump(g))
def get_index_only(g):
cmd = 'qdelaunay Qt i'
p = Popen(cmd.split(), bufsize=1, stdin=PIPE, stdout=PIPE)
so, se = p.communicate(g.for_qhull())
for i in so.splitlines():
yield i
def get_index_only_str(g):
return "\n".join(get_index_only(g))
class dgrid(basegrid): class dgrid(basegrid):
cell_re = re.compile(r''' def __init__(self, points, values):
-\s+(?P<cell>f\d+).*? self.points = points
vertices:\s(?P<verts>.*?)\n.*? self.values = values
neighboring\s facets:\s+(?P<neigh>[\sf\d]*) self.triangulation = scipy.spatial.Delaunay(points)
''', re.S|re.X) self.kdtree = scipy.spatial.KDTree(points)
vert_re = re.compile(r''' def get_simplex_extra_points(self, X, extra_points=8):
(p\d+) return get_simplex_extra_points(X, self.points, self.triangulation,
''', re.S|re.X) self.kdtree, extra_points=extra_points)
def __init__(self, verts, q = None):
self.dim = len(verts[0])
basegrid.__init__(self, verts,q)
self.construct_connectivity()
def construct_connectivity(self):
"""
a call to this method prepares the internal connectivity structure.
"""
log.info('start')
qdelaunay_string = get_qdelaunay_dump_str(self)
with open('/tmp/qdel.out', 'w') as of:
of.write(qdelaunay_string)
cell_to_cells = []
for matcher in dgrid.cell_re.finditer(qdelaunay_string):
d = matcher.groupdict()
cell_name = d['cell']
verticies = d['verts']
neighboring_cells = d['neigh']
cur_cell = cell(cell_name)
self.cells[cell_name] = cur_cell
for v in dgrid.vert_re.findall(verticies):
vertex_index = int(v[1:])
cur_cell.add_vert(vertex_index)
self.cells_for_vert[vertex_index].append(cur_cell)
nghbrs = [(cell_name, i) for i in neighboring_cells.split()]
cell_to_cells.extend(nghbrs)
log.debug(cell_to_cells)
for rel in cell_to_cells:
if rel[1] in self.cells:
self.cells[rel[0]].add_neighbor(self.cells[rel[1]])
log.debug(self.cells)
log.info('end')