"""
Global interpolation functions.
"""
import numpy as nm
from sfepy.base.base import assert_, output, get_default_attr
from sfepy.base.timing import Timer
from sfepy.discrete.fem.geometry_element import create_geometry_elements
import sfepy.discrete.common.extmods.crefcoors as crc
[docs]
def get_ref_coors_convex(field, coors, close_limit=0.1, cache=None,
verbose=False):
"""
Get reference element coordinates and elements corresponding to given
physical coordinates.
Parameters
----------
field : Field instance
The field defining the approximation.
coors : array
The physical coordinates.
close_limit : float, optional
The maximum limit distance of a point from the closest
element allowed for extrapolation.
cache : Struct, optional
To speed up a sequence of evaluations, the field mesh and other data
can be cached. Optionally, the cache can also contain the reference
element coordinates as `cache.ref_coors`, `cache.cells` and
`cache.status`, if the evaluation occurs in the same coordinates
repeatedly. In that case the mesh related data are ignored.
verbose : bool
If False, reduce verbosity.
Returns
-------
ref_coors : array
The reference coordinates.
cells : array
The cell indices corresponding to the reference coordinates.
status : array
The status: 0 is success, 1 is extrapolation within `close_limit`, 2 is
extrapolation outside `close_limit`, 3 is failure, 4 is failure due to
non-convergence of the Newton iteration in tensor product cells.
Notes
-----
Outline of the algorithm for finding xi such that X(xi) = P:
1. make inverse connectivity - for each vertex have cells it is in.
2. find the closest vertex V.
3. choose initial cell: i0 = first from cells incident to V.
4. while not P in C_i, change C_i towards P, check if P in new C_i.
"""
timer = Timer()
ref_coors = get_default_attr(cache, 'ref_coors', None)
if ref_coors is None:
extrapolate = close_limit > 0.0
ref_coors = nm.empty_like(coors)
cells = nm.empty((coors.shape[0],), dtype=nm.int32)
status = nm.empty((coors.shape[0],), dtype=nm.int32)
cmesh = get_default_attr(cache, 'cmesh', None)
if cmesh is None:
timer.start()
mesh = field.create_mesh(extra_nodes=False)
cmesh = mesh.cmesh
gels = create_geometry_elements()
cmesh.set_local_entities(gels)
cmesh.setup_entities()
centroids = cmesh.get_centroids(cmesh.tdim)
if field.gel.name != '3_8':
normals0 = cmesh.get_facet_normals()
normals1 = None
else:
normals0 = cmesh.get_facet_normals(0)
normals1 = cmesh.get_facet_normals(1)
output('cmesh setup: %f s' % timer.stop(), verbose=verbose)
else:
centroids = cache.centroids
normals0 = cache.normals0
normals1 = cache.normals1
kdtree = get_default_attr(cache, 'kdtree', None)
if kdtree is None:
from scipy.spatial import cKDTree as KDTree
timer.start()
kdtree = KDTree(cmesh.coors)
output('kdtree: %f s' % timer.stop(), verbose=verbose)
timer.start()
ics = kdtree.query(coors)[1]
output('kdtree query: %f s' % timer.stop(), verbose=verbose)
ics = nm.asarray(ics, dtype=nm.int32)
coors = nm.ascontiguousarray(coors)
ctx = field.create_basis_context()
timer.start()
crc.find_ref_coors_convex(ref_coors, cells, status, coors, cmesh,
centroids, normals0, normals1, ics,
extrapolate, 1e-15, close_limit, ctx)
output('ref. coordinates: %f s' % timer.stop(), verbose=verbose)
else:
cells = cache.cells
status = cache.status
return ref_coors, cells, status
[docs]
def get_potential_cells(coors, cmesh, centroids=None, extrapolate=True):
"""
Get cells that potentially contain points with the given physical
coordinates.
Parameters
----------
coors : array
The physical coordinates.
cmesh : CMesh instance
The cmesh defining the cells.
centroids : array, optional
The centroids of the cells.
extrapolate : bool
If True, even the points that are surely outside of the
cmesh are considered and assigned potential cells.
Returns
-------
potential_cells : array
The indices of the cells that potentially contain the points.
offsets : array
The offsets into `potential_cells` for each point: a point ``ip`` is
potentially in cells ``potential_cells[offsets[ip]:offsets[ip+1]]``.
"""
from scipy.spatial import cKDTree as KDTree
if centroids is None:
centroids = cmesh.get_centroids(cmesh.tdim)
kdtree = KDTree(coors)
conn = cmesh.get_cell_conn()
cc = conn.indices.reshape(cmesh.n_el, -1)
cell_coors = cmesh.coors[cc]
rays = cell_coors - centroids[:, None]
radii = nm.linalg.norm(rays, ord=nm.inf, axis=2).max(axis=1)
potential_cells = [[]] * coors.shape[0]
for ic, centroid in enumerate(centroids):
ips = kdtree.query_ball_point(centroid, radii[ic], p=nm.inf)
if len(ips):
for ip in ips:
if not len(potential_cells[ip]):
potential_cells[ip] = []
potential_cells[ip].append(ic)
lens = nm.array([0] + [len(ii) for ii in potential_cells], dtype=nm.int32)
if extrapolate:
# Deal with the points outside of the field domain - insert elements
# incident to the closest mesh vertex.
iin = nm.where(lens[1:] == 0)[0]
if len(iin):
kdtree = KDTree(cmesh.coors)
ics = kdtree.query(coors[iin])[1]
cmesh.setup_connectivity(0, cmesh.tdim)
conn = cmesh.get_conn(0, cmesh.tdim)
oo = conn.offsets
for ii, ip in enumerate(iin):
ik = ics[ii]
potential_cells[ip] = conn.indices[oo[ik]:oo[ik+1]]
lens[ip+1] = len(potential_cells[ip])
offsets = nm.cumsum(lens, dtype=nm.int32)
potential_cells = nm.concatenate(potential_cells).astype(nm.int32)
return potential_cells, offsets
[docs]
def get_ref_coors_general(field, coors, close_limit=0.1, get_cells_fun=None,
cache=None, verbose=False):
"""
Get reference element coordinates and elements corresponding to given
physical coordinates.
Parameters
----------
field : Field instance
The field defining the approximation.
coors : array
The physical coordinates.
close_limit : float, optional
The maximum limit distance of a point from the closest
element allowed for extrapolation.
get_cells_fun : callable, optional
If given, a function with signature ``get_cells_fun(coors, cmesh,
**kwargs)`` returning cells and offsets that potentially contain points
with the coordinates `coors`. When not given,
:func:`get_potential_cells()` is used.
cache : Struct, optional
To speed up a sequence of evaluations, the field mesh and other data
can be cached. Optionally, the cache can also contain the reference
element coordinates as `cache.ref_coors`, `cache.cells` and
`cache.status`, if the evaluation occurs in the same coordinates
repeatedly. In that case the mesh related data are ignored.
verbose : bool
If False, reduce verbosity.
Returns
-------
ref_coors : array
The reference coordinates.
cells : array
The cell indices corresponding to the reference coordinates.
status : array
The status: 0 is success, 1 is extrapolation within `close_limit`, 2 is
extrapolation outside `close_limit`, 3 is failure, 4 is failure due to
non-convergence of the Newton iteration in tensor product cells. If
close_limit is 0, then status 5 indicates points outside of the field
domain that had no potential cells.
"""
timer = Timer()
ref_coors = get_default_attr(cache, 'ref_coors', None)
if ref_coors is None:
extrapolate = close_limit > 0.0
get = get_potential_cells if get_cells_fun is None else get_cells_fun
ref_coors = nm.empty_like(coors)
cells = nm.empty((coors.shape[0],), dtype=nm.int32)
status = nm.empty((coors.shape[0],), dtype=nm.int32)
cmesh = get_default_attr(cache, 'cmesh', None)
if cmesh is None:
timer.start()
mesh = field.create_mesh(extra_nodes=False)
cmesh = mesh.cmesh
if get_cells_fun is None:
centroids = cmesh.get_centroids(cmesh.tdim)
else:
centroids = None
output('cmesh setup: %f s' % timer.stop(), verbose=verbose)
else:
centroids = cache.centroids
timer.start()
potential_cells, offsets = get(coors, cmesh, centroids=centroids,
extrapolate=extrapolate)
output('potential cells: %f s' % timer.stop(), verbose=verbose)
coors = nm.ascontiguousarray(coors)
ctx = field.create_basis_context()
eval_cmesh = get_default_attr(cache, 'eval_cmesh', None)
if eval_cmesh is None:
timer.start()
mesh = field.create_eval_mesh()
if mesh is None:
eval_cmesh = cmesh
else:
eval_cmesh = mesh.cmesh
output('eval_cmesh setup: %f s'
% timer.stop(), verbose=verbose)
timer.start()
crc.find_ref_coors(ref_coors, cells, status, coors, eval_cmesh,
potential_cells, offsets, extrapolate,
1e-15, close_limit, ctx)
if extrapolate:
assert_(nm.all(status < 5))
output('ref. coordinates: %f s' % timer.stop(), verbose=verbose)
else:
cells = cache.cells
status = cache.status
return ref_coors, cells, status
[docs]
def get_ref_coors(field, coors, strategy='general', close_limit=0.1,
get_cells_fun=None, cache=None, verbose=False):
"""
Get reference element coordinates and elements corresponding to given
physical coordinates.
Parameters
----------
field : Field instance
The field defining the approximation.
coors : array
The physical coordinates.
strategy : {'general', 'convex'}, optional
The strategy for finding the elements that contain the coordinates. For
convex meshes, the 'convex' strategy might be faster than the 'general'
one.
close_limit : float, optional
The maximum limit distance of a point from the closest
element allowed for extrapolation.
get_cells_fun : callable, optional
If given, a function with signature ``get_cells_fun(coors, cmesh,
**kwargs)`` returning cells and offsets that potentially contain points
with the coordinates `coors`. Applicable only when `strategy` is
'general'. When not given, :func:`get_potential_cells()` is used.
cache : Struct, optional
To speed up a sequence of evaluations, the field mesh and other data
can be cached. Optionally, the cache can also contain the reference
element coordinates as `cache.ref_coors`, `cache.cells` and
`cache.status`, if the evaluation occurs in the same coordinates
repeatedly. In that case the mesh related data are ignored.
verbose : bool
If False, reduce verbosity.
Returns
-------
ref_coors : array
The reference coordinates.
cells : array
The cell indices corresponding to the reference coordinates.
status : array
The status: 0 is success, 1 is extrapolation within `close_limit`, 2 is
extrapolation outside `close_limit`, 3 is failure, 4 is failure due to
non-convergence of the Newton iteration in tensor product cells. If
close_limit is 0, then for the 'general' strategy the status 5
indicates points outside of the field domain that had no potential
cells.
"""
if strategy == 'general':
return get_ref_coors_general(field, coors, close_limit=close_limit,
get_cells_fun=get_cells_fun,
cache=cache, verbose=verbose)
elif strategy == 'convex':
return get_ref_coors_convex(field, coors, close_limit=close_limit,
cache=cache, verbose=verbose)
else:
raise ValueError('unsupported strategy! (%s)' % strategy)