Skip to content

Commit 8bf666c

Browse files
gertjanvanzwietenjoostvanzwieten
authored andcommitted
remove locate args ischeme and scale, add maxdist
Formerly locate made an initial selection of candidate elements based on (scaled) bounding boxes. The new implementation simply orders elements by centroid proximity, with an option to set a cutoff radius in case fast failure is required.
1 parent bdd429b commit 8bf666c

File tree

2 files changed

+26
-18
lines changed

2 files changed

+26
-18
lines changed

nutils/topology.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ def select(self, indicator, ischeme='bezier2', **kwargs):
495495
return self[selected]
496496

497497
@log.withcontext
498-
def locate(self, geom, coords, *, ischeme='vertex', scale=1, tol=None, eps=0, maxiter=100, arguments=None):
498+
def locate(self, geom, coords, *, tol, eps=0, maxiter=100, arguments=None, weights=None, maxdist=None, ischeme=None, scale=None):
499499
'''Create a sample based on physical coordinates.
500500
501501
In a finite element application, functions are commonly evaluated in points
@@ -526,39 +526,36 @@ def locate(self, geom, coords, *, ischeme='vertex', scale=1, tol=None, eps=0, ma
526526
Array of coordinates with ``ndims`` columns.
527527
tol : :class:`float`
528528
Maximum allowed distance between original and located coordinate.
529-
ischeme : :class:`str` (default: "vertex")
530-
Sample points used to determine bounding boxes.
531-
scale : :class:`float` (default: 1)
532-
Bounding box amplification factor, useful when element shapes are
533-
distorted. Setting this to >1 can increase computational effort but is
534-
otherwise harmless.
535529
eps : :class:`float` (default: 0)
536530
Epsilon radius around element within which a point is considered to be
537531
inside.
538532
maxiter : :class:`int` (default: 100)
539533
Maximum allowed number of Newton iterations.
540534
arguments : :class:`dict` (default: None)
541535
Arguments for function evaluation.
536+
weights : :class:`float` array (default: None)
537+
Optional weights, in case ``coords`` are quadrature points.
538+
maxdist : :class:`float` (default: None)
539+
Speed up failure by setting a distance between point and element
540+
centroid above which the element is rejected immediately. If all points
541+
are expected to be located then this can safely be left unspecified.
542542
543543
Returns
544544
-------
545545
located : :class:`nutils.sample.Sample`
546546
'''
547547

548-
if tol is None:
549-
warnings.deprecation('locate without tol argument is deprecated, please provide an explicit tolerance')
550-
tol = 1e-12
548+
if ischeme is not None:
549+
warnings.deprecation('the ischeme argument is deprecated and will be removed in future')
550+
if scale is not None:
551+
warnings.deprecation('the scale argument is deprecated and will be removed in future')
551552
coords = numpy.asarray(coords, dtype=float)
552553
if geom.ndim == 0:
553554
geom = geom[_]
554555
coords = coords[...,_]
555556
if not geom.shape == coords.shape[1:] == (self.ndims,):
556557
raise Exception('invalid geometry or point shape for {}D topology'.format(self.ndims))
557-
bboxsample = self.sample(*element.parse_legacy_ischeme(ischeme))
558-
vertices = map(bboxsample.eval(geom, **arguments or {}).__getitem__, bboxsample.indexiter)
559-
bboxes = numpy.array([numpy.mean(v,axis=0) * (1-scale) + numpy.array([numpy.min(v,axis=0), numpy.max(v,axis=0)]) * scale
560-
for v in vertices]) # nelems x {min,max} x ndims
561-
vref = element.getsimplex(0)
558+
centroids = self.elem_mean(geom, geometry=geom, degree=2)
562559
ielems = parallel.shempty(len(coords), dtype=int)
563560
xis = parallel.shempty((len(coords),len(geom)), dtype=float)
564561
subsamplemetas = function.SubsampleMeta(roots=self.roots, ndimsnormal=sum(root.ndims for root in self.roots)-self.ndims, ndimspoints=self.ndims),
@@ -567,8 +564,9 @@ def locate(self, geom, coords, *, ischeme='vertex', scale=1, tol=None, eps=0, ma
567564
with parallel.ctxrange('locating', len(coords)) as ipoints:
568565
for ipoint in ipoints:
569566
coord = coords[ipoint]
570-
ielemcandidates, = numpy.logical_and(numpy.greater_equal(coord, bboxes[:,0,:]), numpy.less_equal(coord, bboxes[:,1,:])).all(axis=-1).nonzero()
571-
for ielem in sorted(ielemcandidates, key=lambda i: numpy.linalg.norm(bboxes[i].mean(0)-coord)):
567+
dist = numpy.linalg.norm(centroids - coord, axis=1)
568+
for ielem in numpy.argsort(dist) if maxdist is None \
569+
else sorted((dist < maxdist).nonzero()[0], key=dist.__getitem__):
572570
converged = False
573571
ref = self.references[ielem]
574572
p = ref.getpoints('gauss', 1)

tests/test_topology.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from nutils import *
22
from nutils.testing import *
3-
import numpy, copy, sys, pickle, subprocess, base64, itertools, os
3+
from nutils.elementseq import References
4+
import numpy, copy, sys, pickle, subprocess, base64, itertools, os, unittest
45

56
class TopologyAssertions:
67

@@ -326,6 +327,15 @@ def test(self):
326327
located = sample.eval(self.geom)
327328
self.assertAllAlmostEqual(located, target)
328329

330+
@parametrize.enable_if(lambda etype, mode, **kwargs: etype != 'square' or mode == 'nonlinear')
331+
def test_maxdist(self):
332+
target = numpy.array([(.2,.3), (.1,.9), (0,1)])
333+
with self.assertRaises(topology.LocateError):
334+
self.domain.locate(self.geom, [(0, .3)], eps=1e-15, tol=1e-12, maxdist=.001)
335+
sample = self.domain.locate(self.geom, target, eps=1e-15, tol=1e-12, maxdist=.5)
336+
located = sample.eval(self.geom)
337+
self.assertAllAlmostEqual(located, target)
338+
329339
def test_invalidargs(self):
330340
target = numpy.array([(.2,), (.1,), (0,)])
331341
with self.assertRaises(Exception):

0 commit comments

Comments
 (0)