Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/matplotlib/tri/trirefine.py : 13%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2Mesh refinement for triangular grids.
3"""
5import numpy as np
7from matplotlib import cbook
8from matplotlib.tri.triangulation import Triangulation
9import matplotlib.tri.triinterpolate
12class TriRefiner:
13 """
14 Abstract base class for classes implementing mesh refinement.
16 A TriRefiner encapsulates a Triangulation object and provides tools for
17 mesh refinement and interpolation.
19 Derived classes must implements:
21 - ``refine_triangulation(return_tri_index=False, **kwargs)`` , where
22 the optional keyword arguments *kwargs* are defined in each
23 TriRefiner concrete implementation, and which returns:
25 - a refined triangulation
26 - optionally (depending on *return_tri_index*), for each
27 point of the refined triangulation: the index of
28 the initial triangulation triangle to which it belongs.
30 - ``refine_field(z, triinterpolator=None, **kwargs)`` , where:
32 - *z* array of field values (to refine) defined at the base
33 triangulation nodes
34 - *triinterpolator* is a
35 :class:`~matplotlib.tri.TriInterpolator` (optional)
36 - the other optional keyword arguments *kwargs* are defined in
37 each TriRefiner concrete implementation
39 and which returns (as a tuple) a refined triangular mesh and the
40 interpolated values of the field at the refined triangulation nodes.
42 """
43 def __init__(self, triangulation):
44 cbook._check_isinstance(Triangulation, triangulation=triangulation)
45 self._triangulation = triangulation
48class UniformTriRefiner(TriRefiner):
49 """
50 Uniform mesh refinement by recursive subdivisions.
52 Parameters
53 ----------
54 triangulation : :class:`~matplotlib.tri.Triangulation`
55 The encapsulated triangulation (to be refined)
56 """
57# See Also
58# --------
59# :class:`~matplotlib.tri.CubicTriInterpolator` and
60# :class:`~matplotlib.tri.TriAnalyzer`.
61# """
62 def __init__(self, triangulation):
63 TriRefiner.__init__(self, triangulation)
65 def refine_triangulation(self, return_tri_index=False, subdiv=3):
66 """
67 Computes an uniformly refined triangulation *refi_triangulation* of
68 the encapsulated :attr:`triangulation`.
70 This function refines the encapsulated triangulation by splitting each
71 father triangle into 4 child sub-triangles built on the edges midside
72 nodes, recursively (level of recursion *subdiv*).
73 In the end, each triangle is hence divided into ``4**subdiv``
74 child triangles.
75 The default value for *subdiv* is 3 resulting in 64 refined
76 subtriangles for each triangle of the initial triangulation.
78 Parameters
79 ----------
80 return_tri_index : boolean, optional
81 Boolean indicating whether an index table indicating the father
82 triangle index of each point will be returned. Default value
83 False.
84 subdiv : integer, optional
85 Recursion level for the subdivision. Defaults value 3.
86 Each triangle will be divided into ``4**subdiv`` child triangles.
88 Returns
89 -------
90 refi_triangulation : :class:`~matplotlib.tri.Triangulation`
91 The returned refined triangulation
92 found_index : array-like of integers
93 Index of the initial triangulation containing triangle, for each
94 point of *refi_triangulation*.
95 Returned only if *return_tri_index* is set to True.
97 """
98 refi_triangulation = self._triangulation
99 ntri = refi_triangulation.triangles.shape[0]
101 # Computes the triangulation ancestors numbers in the reference
102 # triangulation.
103 ancestors = np.arange(ntri, dtype=np.int32)
104 for _ in range(subdiv):
105 refi_triangulation, ancestors = self._refine_triangulation_once(
106 refi_triangulation, ancestors)
107 refi_npts = refi_triangulation.x.shape[0]
108 refi_triangles = refi_triangulation.triangles
110 # Now we compute found_index table if needed
111 if return_tri_index:
112 # We have to initialize found_index with -1 because some nodes
113 # may very well belong to no triangle at all, e.g., in case of
114 # Delaunay Triangulation with DuplicatePointWarning.
115 found_index = np.full(refi_npts, -1, dtype=np.int32)
116 tri_mask = self._triangulation.mask
117 if tri_mask is None:
118 found_index[refi_triangles] = np.repeat(ancestors,
119 3).reshape(-1, 3)
120 else:
121 # There is a subtlety here: we want to avoid whenever possible
122 # that refined points container is a masked triangle (which
123 # would result in artifacts in plots).
124 # So we impose the numbering from masked ancestors first,
125 # then overwrite it with unmasked ancestor numbers.
126 ancestor_mask = tri_mask[ancestors]
127 found_index[refi_triangles[ancestor_mask, :]
128 ] = np.repeat(ancestors[ancestor_mask],
129 3).reshape(-1, 3)
130 found_index[refi_triangles[~ancestor_mask, :]
131 ] = np.repeat(ancestors[~ancestor_mask],
132 3).reshape(-1, 3)
133 return refi_triangulation, found_index
134 else:
135 return refi_triangulation
137 def refine_field(self, z, triinterpolator=None, subdiv=3):
138 """
139 Refines a field defined on the encapsulated triangulation.
141 Returns *refi_tri* (refined triangulation), *refi_z* (interpolated
142 values of the field at the node of the refined triangulation).
144 Parameters
145 ----------
146 z : 1d-array-like of length ``n_points``
147 Values of the field to refine, defined at the nodes of the
148 encapsulated triangulation. (``n_points`` is the number of points
149 in the initial triangulation)
150 triinterpolator : :class:`~matplotlib.tri.TriInterpolator`, optional
151 Interpolator used for field interpolation. If not specified,
152 a :class:`~matplotlib.tri.CubicTriInterpolator` will
153 be used.
154 subdiv : integer, optional
155 Recursion level for the subdivision. Defaults to 3.
156 Each triangle will be divided into ``4**subdiv`` child triangles.
158 Returns
159 -------
160 refi_tri : :class:`~matplotlib.tri.Triangulation` object
161 The returned refined triangulation
162 refi_z : 1d array of length: *refi_tri* node count.
163 The returned interpolated field (at *refi_tri* nodes)
164 """
165 if triinterpolator is None:
166 interp = matplotlib.tri.CubicTriInterpolator(
167 self._triangulation, z)
168 else:
169 cbook._check_isinstance(matplotlib.tri.TriInterpolator,
170 triinterpolator=triinterpolator)
171 interp = triinterpolator
173 refi_tri, found_index = self.refine_triangulation(
174 subdiv=subdiv, return_tri_index=True)
175 refi_z = interp._interpolate_multikeys(
176 refi_tri.x, refi_tri.y, tri_index=found_index)[0]
177 return refi_tri, refi_z
179 @staticmethod
180 def _refine_triangulation_once(triangulation, ancestors=None):
181 """
182 This function refines a matplotlib.tri *triangulation* by splitting
183 each triangle into 4 child-masked_triangles built on the edges midside
184 nodes.
185 The masked triangles, if present, are also split but their children
186 returned masked.
188 If *ancestors* is not provided, returns only a new triangulation:
189 child_triangulation.
191 If the array-like key table *ancestor* is given, it shall be of shape
192 (ntri,) where ntri is the number of *triangulation* masked_triangles.
193 In this case, the function returns
194 (child_triangulation, child_ancestors)
195 child_ancestors is defined so that the 4 child masked_triangles share
196 the same index as their father: child_ancestors.shape = (4 * ntri,).
198 """
199 x = triangulation.x
200 y = triangulation.y
202 # According to tri.triangulation doc:
203 # neighbors[i, j] is the triangle that is the neighbor
204 # to the edge from point index masked_triangles[i, j] to point
205 # index masked_triangles[i, (j+1)%3].
206 neighbors = triangulation.neighbors
207 triangles = triangulation.triangles
208 npts = np.shape(x)[0]
209 ntri = np.shape(triangles)[0]
210 if ancestors is not None:
211 ancestors = np.asarray(ancestors)
212 if np.shape(ancestors) != (ntri,):
213 raise ValueError(
214 "Incompatible shapes provide for triangulation"
215 ".masked_triangles and ancestors: {0} and {1}".format(
216 np.shape(triangles), np.shape(ancestors)))
218 # Initiating tables refi_x and refi_y of the refined triangulation
219 # points
220 # hint: each apex is shared by 2 masked_triangles except the borders.
221 borders = np.sum(neighbors == -1)
222 added_pts = (3*ntri + borders) // 2
223 refi_npts = npts + added_pts
224 refi_x = np.zeros(refi_npts)
225 refi_y = np.zeros(refi_npts)
227 # First part of refi_x, refi_y is just the initial points
228 refi_x[:npts] = x
229 refi_y[:npts] = y
231 # Second part contains the edge midside nodes.
232 # Each edge belongs to 1 triangle (if border edge) or is shared by 2
233 # masked_triangles (interior edge).
234 # We first build 2 * ntri arrays of edge starting nodes (edge_elems,
235 # edge_apexes); we then extract only the masters to avoid overlaps.
236 # The so-called 'master' is the triangle with biggest index
237 # The 'slave' is the triangle with lower index
238 # (can be -1 if border edge)
239 # For slave and master we will identify the apex pointing to the edge
240 # start
241 edge_elems = np.tile(np.arange(ntri, dtype=np.int32), 3)
242 edge_apexes = np.repeat(np.arange(3, dtype=np.int32), ntri)
243 edge_neighbors = neighbors[edge_elems, edge_apexes]
244 mask_masters = (edge_elems > edge_neighbors)
246 # Identifying the "masters" and adding to refi_x, refi_y vec
247 masters = edge_elems[mask_masters]
248 apex_masters = edge_apexes[mask_masters]
249 x_add = (x[triangles[masters, apex_masters]] +
250 x[triangles[masters, (apex_masters+1) % 3]]) * 0.5
251 y_add = (y[triangles[masters, apex_masters]] +
252 y[triangles[masters, (apex_masters+1) % 3]]) * 0.5
253 refi_x[npts:] = x_add
254 refi_y[npts:] = y_add
256 # Building the new masked_triangles; each old masked_triangles hosts
257 # 4 new masked_triangles
258 # there are 6 pts to identify per 'old' triangle, 3 new_pt_corner and
259 # 3 new_pt_midside
260 new_pt_corner = triangles
262 # What is the index in refi_x, refi_y of point at middle of apex iapex
263 # of elem ielem ?
264 # If ielem is the apex master: simple count, given the way refi_x was
265 # built.
266 # If ielem is the apex slave: yet we do not know; but we will soon
267 # using the neighbors table.
268 new_pt_midside = np.empty([ntri, 3], dtype=np.int32)
269 cum_sum = npts
270 for imid in range(3):
271 mask_st_loc = (imid == apex_masters)
272 n_masters_loc = np.sum(mask_st_loc)
273 elem_masters_loc = masters[mask_st_loc]
274 new_pt_midside[:, imid][elem_masters_loc] = np.arange(
275 n_masters_loc, dtype=np.int32) + cum_sum
276 cum_sum += n_masters_loc
278 # Now dealing with slave elems.
279 # for each slave element we identify the master and then the inode
280 # once slave_masters is identified, slave_masters_apex is such that:
281 # neighbors[slaves_masters, slave_masters_apex] == slaves
282 mask_slaves = np.logical_not(mask_masters)
283 slaves = edge_elems[mask_slaves]
284 slaves_masters = edge_neighbors[mask_slaves]
285 diff_table = np.abs(neighbors[slaves_masters, :] -
286 np.outer(slaves, np.ones(3, dtype=np.int32)))
287 slave_masters_apex = np.argmin(diff_table, axis=1)
288 slaves_apex = edge_apexes[mask_slaves]
289 new_pt_midside[slaves, slaves_apex] = new_pt_midside[
290 slaves_masters, slave_masters_apex]
292 # Builds the 4 child masked_triangles
293 child_triangles = np.empty([ntri*4, 3], dtype=np.int32)
294 child_triangles[0::4, :] = np.vstack([
295 new_pt_corner[:, 0], new_pt_midside[:, 0],
296 new_pt_midside[:, 2]]).T
297 child_triangles[1::4, :] = np.vstack([
298 new_pt_corner[:, 1], new_pt_midside[:, 1],
299 new_pt_midside[:, 0]]).T
300 child_triangles[2::4, :] = np.vstack([
301 new_pt_corner[:, 2], new_pt_midside[:, 2],
302 new_pt_midside[:, 1]]).T
303 child_triangles[3::4, :] = np.vstack([
304 new_pt_midside[:, 0], new_pt_midside[:, 1],
305 new_pt_midside[:, 2]]).T
306 child_triangulation = Triangulation(refi_x, refi_y, child_triangles)
308 # Builds the child mask
309 if triangulation.mask is not None:
310 child_triangulation.set_mask(np.repeat(triangulation.mask, 4))
312 if ancestors is None:
313 return child_triangulation
314 else:
315 return child_triangulation, np.repeat(ancestors, 4)