source: inundation/parallel/build_submesh.py @ 2625

Last change on this file since 2625 was 2625, checked in by linda, 18 years ago

Added ghost boundary layers

File size: 17.2 KB
Line 
1#########################################################
2#
3# Subdivide the GA domain. This module is primarily
4# responsible for building the ghost layer and
5# communication pattern
6#
7#
8#  Author: Linda Stals, June 2005
9#  Modified: Linda Stals, Nov 2005 (optimise python code)
10#
11#
12#########################################################
13
14import sys
15
16from Numeric import zeros, Float, Int, concatenate, \
17     reshape, arrayrange, take, nonzero
18
19from mesh import Mesh
20
21
22#########################################################
23#
24# Subdivide the triangles into non-overlapping domains.
25#
26#  *)  The subdivision is controlled by triangles_per_proc.
27# The first triangles_per_proc[0] triangles are assigned
28# to the first processor, the second triangles_per_proc[1]
29# are assigned to the second processor etc.
30#
31#  *) nodes, triangles and boundary contains all of the
32# nodes, triangles and boundary tag information for the
33# whole domain. The triangles should be orientated in the
34# correct way and the nodes number consecutively from 0.
35#
36# -------------------------------------------------------
37#
38#  *) A dictionary containing the full_nodes, full_triangles
39# and full_boundary information for each processor is
40# returned. The node information consists of
41# [global_id, x_coord, y_coord].
42#
43#########################################################
44
45def submesh_full(nodes, triangles, boundary, triangles_per_proc):
46
47    # Initialise
48
49    tlower = 0
50    nproc = len(triangles_per_proc)
51    nnodes = len(nodes)
52    node_list = []
53    triangle_list = []
54    boundary_list = []
55    submesh = {}
56    node_range = reshape(arrayrange(nnodes),(nnodes,1))
57    tsubnodes = concatenate((node_range, nodes), 1)
58
59    # Loop over processors
60
61    for p in range(nproc):
62
63        # Find triangles on processor p
64
65        tupper = triangles_per_proc[p]+tlower
66        subtriangles = triangles[tlower:tupper]
67        triangle_list.append(subtriangles)
68
69        # Find the boundary edges on processor p
70
71        subboundary = {}
72        for k in boundary:
73            if (k[0] >=tlower and k[0] < tupper):
74                subboundary[k]=boundary[k]
75        boundary_list.append(subboundary)
76
77        # Find nodes in processor p
78
79        nodemap = zeros(nnodes, 'i')
80        for t in subtriangles:
81            nodemap[t[0]]=1
82            nodemap[t[1]]=1
83            nodemap[t[2]]=1
84
85        node_list.append(take(tsubnodes,nonzero(nodemap)))
86
87        # Move to the next processor
88
89        tlower = tupper
90
91    # Put the results in a dictionary
92
93    submesh["full_nodes"] = node_list
94    submesh["full_triangles"] = triangle_list
95    submesh["full_boundary"] = boundary_list
96
97    # Clean up before exiting
98
99    del (nodemap)
100
101    return submesh
102
103
104#########################################################
105#
106# Build the ghost layer of triangles
107#
108#  *) Given the triangle subpartion for the processor
109# build a ghost layer of triangles. The ghost layer
110# consists of two layers of neighbouring triangles.
111#
112#  *) The vertices in the ghost triangles must also
113# be added to the node list for the current processor
114#
115#
116# -------------------------------------------------------
117#
118#  *) The extra triangles and nodes are returned.
119#
120#  *)  The node information consists of
121# [global_id, x_coord, y_coord].
122#
123#  *) The triangle information consists of
124# [triangle number, t], where t = [v1, v2, v3].
125#
126#########################################################
127
128def ghost_layer(submesh, mesh, p, tupper, tlower):
129
130    ncoord = len(mesh.coordinates)
131    ntriangles = len(mesh.triangles)
132
133    # Find the first layer of boundary triangles
134
135    trianglemap = zeros(ntriangles, 'i')
136    for t in range(tlower, tupper):
137        n = mesh.neighbours[t, 0]
138        if n > 0:
139            if n < tlower or n >= tupper:
140                trianglemap[n] = 1
141        n = mesh.neighbours[t, 1]
142        if n > 0:
143            if n < tlower or n >= tupper:
144                trianglemap[n] = 1
145        n = mesh.neighbours[t, 2]
146        if n > 0:
147            if n < tlower or n >= tupper:
148                trianglemap[n] = 1
149
150    # Find the second layer of boundary triangles
151
152    for t in range(len(trianglemap)):
153        if trianglemap[t]==1:
154            n = mesh.neighbours[t, 0]
155            if n > 0:
156                if (n < tlower or n >= tupper) and trianglemap[n] == 0:
157                    trianglemap[n] = 1
158            n = mesh.neighbours[t, 1]
159            if n > 0:
160                if (n < tlower or n >= tupper) and trianglemap[n] == 0:
161                    trianglemap[n] = 1
162            n = mesh.neighbours[t, 2]
163            if n > 0:
164                if (n < tlower or n >= tupper) and trianglemap[n] == 0:
165                    trianglemap[n] = 1
166
167    # Build the triangle list and make note of the vertices
168
169    nodemap = zeros(ncoord, 'i')
170    fullnodes = submesh["full_nodes"][p]
171
172    subtriangles = []
173    for i in range(len(trianglemap)):
174        if trianglemap[i] == 1:
175            t = list(mesh.triangles[i])
176            nodemap[t[0]] = 1
177            nodemap[t[1]] = 1
178            nodemap[t[2]] = 1
179
180    trilist = reshape(arrayrange(ntriangles),(ntriangles,1))
181    tsubtriangles = concatenate((trilist, mesh.triangles), 1)
182    subtriangles = take(tsubtriangles, nonzero(trianglemap))
183
184    # Keep a record of the triangle vertices, if they are not already there
185
186    subnodes = []
187    for n in fullnodes:
188        nodemap[int(n[0])] = 0
189
190    nodelist = reshape(arrayrange(ncoord),(ncoord,1))
191    tsubnodes = concatenate((nodelist, mesh.coordinates), 1)
192    subnodes = take(tsubnodes, nonzero(nodemap))
193
194    # Clean up before exiting
195
196    del (nodelist)
197    del (trilist)
198    del (tsubnodes)
199    del (nodemap)
200    del (trianglemap)
201
202    # Return the triangles and vertices sitting on the boundary layer
203
204    return subnodes, subtriangles
205
206#########################################################
207#
208# Find the edges of the ghost trianlges that do not
209# have a neighbour in the current cell. These are
210# treated as a special type of boundary edge.
211#
212#  *) Given the ghost triangles in a particular
213# triangle, use the mesh to find its neigbours. If
214# the neighbour is not in the processor set it to
215# be a boundary edge
216#
217#  *) The vertices in the ghost triangles must also
218# be added to the node list for the current processor
219#
220#  *) The boundary edges for the ghost triangles are
221# ignored.
222#
223# -------------------------------------------------------
224#
225#  *) The type assigned to the ghost boundary edges is 'ghost'
226#
227#  *)  The boundary information is returned as a directorier
228# with the key = (triangle id, edge no) and the values
229# assigned to the key is 'ghost'
230#
231#
232#########################################################
233def is_in_processor(ghost_list, tlower, tupper, n):
234    return (n in ghost_list) or (tlower <= n and tupper >= n)
235
236def ghost_bnd_layer(ghosttri, tlower, tupper, mesh, p):
237
238    ghost_list = []
239    subboundary = {}
240       
241    for t in ghosttri:
242        ghost_list.append(t[0])
243
244    for t in ghosttri:
245        n = mesh.neighbours[t[0], 0]
246        if not is_in_processor(ghost_list, tlower, tupper, n):
247            subboundary[t[0], 0] = 'ghost'
248
249        n = mesh.neighbours[t[0], 1]
250        if not is_in_processor(ghost_list, tlower, tupper, n):
251            subboundary[t[0], 1] = 'ghost'
252
253        n = mesh.neighbours[t[0], 2]
254        if not is_in_processor(ghost_list, tlower, tupper, n):
255            subboundary[t[0], 2] = 'ghost'
256   
257    return subboundary
258
259#########################################################
260#
261# The ghost triangles on the current processor will need
262# to get updated information from the neighbouring
263# processor containing the corresponding full triangles.
264#
265#  *) The tri_per_proc is used to determine which
266# processor contains the full node copy.
267#
268# -------------------------------------------------------
269#
270#  *) The ghost communication pattern consists of
271# [global node number, neighbour processor number].
272#
273#########################################################
274
275def ghost_commun_pattern(subtri, p, tri_per_proc):
276
277    # Loop over the ghost triangles
278
279    ghost_commun = zeros((len(subtri), 2), Int)
280
281    for i in range(len(subtri)):
282        global_no = subtri[i][0]
283
284        # Find which processor contains the full triangle
285
286        nproc = len(tri_per_proc)
287        neigh = nproc-1
288        sum = 0
289        for q in range(nproc-1):
290            if (global_no < sum+tri_per_proc[q]):
291                neigh = q
292                break
293            sum = sum+tri_per_proc[q]
294
295        # Keep a copy of the neighbour processor number
296
297        ghost_commun[i] = [global_no, neigh]
298
299    return ghost_commun
300
301#########################################################
302#
303# The full triangles in this processor must communicate
304# updated information to neighbouring processor that
305# contain ghost triangles
306#
307#  *) The ghost communication pattern for all of the
308# processor must be built before calling this processor.
309#
310#  *) The full communication pattern is found by looping
311# through the ghost communication pattern for all of the
312# processors. Recall that this information is stored in
313# the form [global node number, neighbour processor number].
314# The full communication for the neighbour processor is
315# then updated.
316#
317# -------------------------------------------------------
318#
319#  *) The full communication pattern consists of
320# [global id, [p1, p2, ...]], where p1, p2 etc contain
321# a ghost node copy of the triangle global id.
322#
323#########################################################
324
325def full_commun_pattern(submesh, tri_per_proc):
326    tlower = 0
327    nproc = len(tri_per_proc)
328    full_commun = []
329
330    # Loop over the processor
331
332    for p in range(nproc):
333
334        # Loop over the full triangles in the current processor
335        # and build an empty dictionary
336
337        fcommun = {}
338        tupper = tri_per_proc[p]+tlower
339        for i in range(tlower, tupper):
340            fcommun[i] = []
341        full_commun.append(fcommun)
342        tlower = tupper
343
344    # Loop over the processor again
345
346    for p in range(nproc):
347
348        # Loop over the ghost triangles in the current processor,
349        # find which processor contains the corresponding full copy
350        # and note that the processor must send updates to this
351        # processor
352
353        for g in submesh["ghost_commun"][p]:
354            neigh = g[1]
355            full_commun[neigh][g[0]].append(p)
356
357    return full_commun
358
359
360#########################################################
361#
362# Given the non-overlapping grid partition, an extra layer
363# of triangles are included to help with the computations.
364# The triangles in this extra layer are not updated by
365# the processor, their updated values must be sent by the
366# processor containing the original, full, copy of the
367# triangle. The communication pattern that controls these
368# updates must also be built.
369#
370#  *) Assumes that full triangles, nodes etc have already
371# been found and stored in submesh
372#
373#  *) See the documentation for ghost_layer,
374# ghost_commun_pattern and full_commun_pattern
375#
376# -------------------------------------------------------
377#
378#  *) The additional information is added to the submesh
379# dictionary. See the documentation for ghost_layer,
380# ghost_commun_pattern and full_commun_pattern
381#
382#  *) The ghost_triangles, ghost_nodes, ghost_boundary,
383# ghost_commun and full_commun is added to submesh
384#########################################################
385
386def submesh_ghost(submesh, mesh, triangles_per_proc):
387
388    nproc = len(triangles_per_proc)
389    tlower = 0
390    ghost_triangles = []
391    ghost_nodes = []
392    ghost_commun = []
393    ghost_bnd = []
394
395    # Loop over the processors
396
397    for p in range(nproc):
398
399        # Find the full triangles in this processor
400
401        tupper = triangles_per_proc[p]+tlower
402
403        # Build the ghost boundary layer
404
405        [subnodes, subtri] = \
406                   ghost_layer(submesh, mesh, p, tupper, tlower)
407        ghost_triangles.append(subtri)
408        ghost_nodes.append(subnodes)
409
410        # Find the boundary layer formed by the ghost triangles
411       
412        subbnd = ghost_bnd_layer(subtri, tupper, tlower, mesh, p)
413        ghost_bnd.append(subbnd)
414       
415        # Build the communication pattern for the ghost nodes
416
417        gcommun = \
418                ghost_commun_pattern(subtri, p, triangles_per_proc)
419        ghost_commun.append(gcommun)
420
421        # Move to the next processor
422
423        tlower = tupper
424
425    # Record the ghost layer and communication pattern
426
427    submesh["ghost_nodes"] = ghost_nodes
428    submesh["ghost_triangles"] = ghost_triangles
429    submesh["ghost_commun"] = ghost_commun
430    submesh["ghost_boundary"] = ghost_bnd
431   
432    # Build the communication pattern for the full triangles
433
434    full_commun = full_commun_pattern(submesh, triangles_per_proc)
435    submesh["full_commun"] = full_commun
436
437    # Return the submesh
438
439    return submesh
440
441
442#########################################################
443#
444# Certain quantities may be assigned to the triangles,
445# these quantities must be subdivided in the same way
446# as the triangles
447#
448#  *) The quantities are ordered in the same way as the
449# triangles
450#
451# -------------------------------------------------------
452#
453#  *) The quantites attached to the full triangles are
454# stored in full_quan
455#
456#  *) The quantities attached to the ghost triangles are
457# stored in ghost_quan
458#########################################################
459
460def submesh_quantities(submesh, quantities, triangles_per_proc):
461
462    nproc = len(triangles_per_proc)
463
464    lower = 0
465
466    # Build an empty dictionary to hold the quantites
467
468    submesh["full_quan"] = {}
469    submesh["ghost_quan"] = {}
470    for k in quantities:
471        submesh["full_quan"][k] = []
472        submesh["ghost_quan"][k] = []
473
474    # Loop trough the subdomains
475
476    for p in range(nproc):
477        upper =   lower+triangles_per_proc[p]
478
479        # Find the global ID of the ghost triangles
480
481        global_id = []
482        M = len(submesh["ghost_triangles"][p])
483        for j in range(M):
484            global_id.append(submesh["ghost_triangles"][p][j][0])
485
486        # Use the global ID to extract the quantites information from
487        # the full domain
488
489        for k in quantities:
490            submesh["full_quan"][k].append(quantities[k][lower:upper])
491            submesh["ghost_quan"][k].append(zeros( (M,3) , Float))
492            for j in range(M):
493                submesh["ghost_quan"][k][p][j] = \
494                                               quantities[k][global_id[j]]
495
496        lower = upper
497
498    return submesh
499
500#########################################################
501#
502# Build the grid partition on the host.
503#
504#  *) See the documentation for submesh_ghost and
505# submesh_full
506#
507# -------------------------------------------------------
508#
509#  *) A dictionary containing the full_triangles,
510# full_nodes, full_boundary, ghost_triangles, ghost_nodes,
511# ghost_boundary, ghost_commun and full_commun is returned.
512#
513#########################################################
514
515def build_submesh(nodes, triangles, edges, quantities,
516                  triangles_per_proc):
517
518    # Temporarily build the mesh to find the neighbouring
519    # triangles
520
521    mesh = Mesh(nodes, triangles)
522
523    # Subdivide into non-overlapping partitions
524
525    submeshf = submesh_full(nodes, triangles, edges, \
526                            triangles_per_proc)
527
528    # Add any extra ghost boundary layer information
529
530    submeshg = submesh_ghost(submeshf, mesh, triangles_per_proc)
531
532    # Order the quantities information to be the same as the triangle
533    # information
534
535    submesh = submesh_quantities(submeshg, quantities, \
536                                 triangles_per_proc)
537
538    return submesh
539
540#########################################################
541#
542# Extract the submesh that will belong to the
543# "host processor" (i.e. processor zero)
544#
545#  *) See the documentation for build_submesh
546#
547# -------------------------------------------------------
548#
549#  *) A dictionary containing the full_triangles,
550# full_nodes, full_boundary, ghost_triangles, ghost_nodes,
551# ghost_boundary, ghost_commun and full_commun belonging
552# to processor zero are returned.
553#
554#########################################################
555def extract_hostmesh(submesh):
556
557    submesh_cell = {}
558    submesh_cell["full_nodes"] = submesh["full_nodes"][0]
559    submesh_cell["ghost_nodes"] = submesh["ghost_nodes"][0]
560    submesh_cell["full_triangles"] = submesh["full_triangles"][0]
561    submesh_cell["ghost_triangles"] = submesh["ghost_triangles"][0]
562    submesh_cell["full_boundary"] = submesh["full_boundary"][0]
563    submesh_cell["ghost_boundary"] = submesh["ghost_boundary"][0]
564    submesh_cell["ghost_commun"] = submesh["ghost_commun"][0]
565    submesh_cell["full_commun"] = submesh["full_commun"][0]
566    submesh_cell["full_quan"] ={}
567    submesh_cell["ghost_quan"]={}
568    for k in submesh["full_quan"]:
569        submesh_cell["full_quan"][k] = submesh["full_quan"][k][0]
570        submesh_cell["ghost_quan"][k] = submesh["ghost_quan"][k][0]
571
572    return submesh_cell
573
Note: See TracBrowser for help on using the repository browser.