source: trunk/anuga_core/source/anuga_parallel/distribute_mesh.py @ 8497

Last change on this file since 8497 was 8496, checked in by steve, 13 years ago

Some problems with dry regioons causing wierd results for
run_parallel_sw_merimbula.

File size: 39.3 KB
Line 
1#########################################################
2#
3#
4#  Read in a data file and subdivide the triangle list
5#
6#
7#  The final routine, pmesh_divide_metis, does automatic
8# grid partitioning. Once testing has finished on this
9# routine the others should be removed.
10#
11#  Authors: Linda Stals and Matthew Hardy, June 2005
12#  Modified: Linda Stals, Nov 2005
13#            Jack Kelly, Nov 2005
14#            Steve Roberts, Aug 2009 (updating to numpy)
15#
16#
17#########################################################
18
19
20import sys
21from os import sep
22from sys import path
23from math import floor
24
25import numpy as num
26
27from anuga.abstract_2d_finite_volumes.neighbour_mesh import Mesh
28from anuga import indent
29
30
31#########################################################
32#
33# If the triangles list is reordered, the quantities
34# assigned to the triangles must also be reorded.
35#
36# *) quantities contain the quantites in the old ordering
37# *) proc_sum[i] contains the number of triangles in
38# processor i
39# *) tri_index is a map from the old triangle ordering to
40# the new ordering, where the new number for triangle
41# i is proc_sum[tri_index[i][0]]+tri_index[i][1]
42#
43# -------------------------------------------------------
44#
45# *) The quantaties are returned in the new ordering
46#
47#########################################################
48
49def reorder(quantities, tri_index, proc_sum):
50
51    # Find the number triangles
52
53    N = len(tri_index)
54
55    # Temporary storage area
56
57    index = num.zeros(N, num.int)
58    q_reord = {}
59
60    # Find the new ordering of the triangles
61
62    for i in range(N):
63        bin = tri_index[i][0]
64        bin_off_set = tri_index[i][1]
65        index[i] = proc_sum[bin]+bin_off_set
66
67    # Reorder each quantity according to the new ordering
68
69    for k in quantities:
70        q_reord[k] = num.zeros((N, 3), num.float)
71        for i in range(N):
72            q_reord[k][index[i]]=quantities[k].vertex_values[i]
73    del index
74
75    return q_reord
76
77
78#########################################################
79#
80# Divide the mesh using a call to metis, through pymetis.
81#
82# -------------------------------------------------------
83#
84# *)  The nodes, triangles, boundary, and quantities are
85# returned. triangles_per_proc defines the subdivision.
86# The first triangles_per_proc[0] triangles are assigned
87# to processor 0, the next triangles_per_proc[1] are
88# assigned to processor 1 etc. The boundary and quantites
89# are ordered the same way as the triangles
90#
91#########################################################
92
93#path.append('..' + sep + 'pymetis')
94
95try:
96    from pymetis.metis import partMeshNodal
97except ImportError:
98    print "***************************************************"
99    print "         Metis is probably not compiled."
100    print "         Read \anuga_core\source\pymetis\README"
101    print "***************************************************"
102    raise ImportError
103
104def pmesh_divide_metis(domain, n_procs):
105    # Wrapper for old pmesh_divide_metis which does not return tri_index or r_tri_index
106    nodes, ttriangles, boundary, triangles_per_proc, quantities, tri_index, r_tri_index = pmesh_divide_metis_helper(domain, n_procs)
107    return nodes, ttriangles, boundary, triangles_per_proc, quantities
108
109def pmesh_divide_metis_with_map(domain, n_procs):
110    return pmesh_divide_metis_helper(domain, n_procs)
111
112def pmesh_divide_metis_helper(domain, n_procs):
113   
114    # Initialise the lists
115    # List, indexed by processor of # triangles.
116   
117    triangles_per_proc = []
118   
119    # List of lists, indexed by processor of vertex numbers
120   
121    tri_list = []
122
123    # Serial to Parallel and Parallel to Serial Triangle index maps
124    tri_index = {}
125    r_tri_index = {} # reverse tri index, parallel to serial triangle index mapping
126   
127    # List indexed by processor of cumulative total of triangles allocated.
128   
129    proc_sum = []
130    for i in range(n_procs):
131        tri_list.append([])
132        triangles_per_proc.append(0)
133        proc_sum.append([])
134
135    # Prepare variables for the metis call
136   
137    n_tri = len(domain.triangles)
138    if n_procs != 1: #Because metis chokes on it...
139        n_vert = domain.get_number_of_nodes()
140        t_list = domain.triangles.copy()
141        t_list = num.reshape(t_list, (-1,))
142   
143        # The 1 here is for triangular mesh elements.
144        edgecut, epart, npart = partMeshNodal(n_tri, n_vert, t_list, 1, n_procs)
145        # print edgecut
146        # print npart
147        # print epart
148        del edgecut
149        del npart
150
151        # Sometimes (usu. on x86_64), partMeshNodal returns an array of zero
152        # dimensional arrays. Correct this.
153        if type(epart[0]) == num.ndarray:
154            epart_new = num.zeros(len(epart), num.int)
155            for i in range(len(epart)):
156                epart_new[i] = epart[i][0]
157            epart = epart_new
158            del epart_new
159        # Assign triangles to processes, according to what metis told us.
160       
161        # tri_index maps triangle number -> processor, new triangle number
162        # (local to the processor)
163       
164        triangles = []       
165        for i in range(n_tri):
166            triangles_per_proc[epart[i]] = triangles_per_proc[epart[i]] + 1
167            tri_list[epart[i]].append(domain.triangles[i])
168            tri_index[i] = ([epart[i], len(tri_list[epart[i]]) - 1])
169            r_tri_index[epart[i], len(tri_list[epart[i]]) - 1] = i
170       
171        # Order the triangle list so that all of the triangles belonging
172        # to processor i are listed before those belonging to processor
173        # i+1
174
175        for i in range(n_procs):
176            for t in tri_list[i]:
177                triangles.append(t)
178           
179        # The boundary labels have to changed in accoradance with the
180        # new triangle ordering, proc_sum and tri_index help with this
181
182        proc_sum[0] = 0
183        for i in range(n_procs - 1):
184            proc_sum[i+1]=proc_sum[i]+triangles_per_proc[i]
185
186        # Relabel the boundary elements to fit in with the new triangle
187        # ordering
188
189        boundary = {}
190        for b in domain.boundary:
191            t =  tri_index[b[0]]
192            boundary[proc_sum[t[0]]+t[1], b[1]] = domain.boundary[b]
193
194        quantities = reorder(domain.quantities, tri_index, proc_sum)
195    else:
196        boundary = domain.boundary.copy()
197        triangles_per_proc[0] = n_tri
198        triangles = domain.triangles.copy()
199       
200        # This is essentially the same as a chunk of code from reorder.
201       
202        quantities = {}
203        for k in domain.quantities:
204            quantities[k] = num.zeros((n_tri, 3), num.float)
205            for i in range(n_tri):
206                quantities[k][i] = domain.quantities[k].vertex_values[i]
207       
208    # Extract the node list
209   
210    nodes = domain.get_nodes().copy()
211   
212    # Convert the triangle datastructure to be an array type,
213    # this helps with the communication
214
215    ttriangles = num.zeros((len(triangles), 3), num.int)
216    for i in range(len(triangles)):
217        ttriangles[i] = triangles[i]
218
219    #return nodes, ttriangles, boundary, triangles_per_proc, quantities
220   
221    return nodes, ttriangles, boundary, triangles_per_proc, quantities, tri_index, r_tri_index
222
223
224#########################################################
225#
226# Subdivide the domain. This module is primarily
227# responsible for building the ghost layer and
228# communication pattern
229#
230#
231#  Author: Linda Stals, June 2005
232#  Modified: Linda Stals, Nov 2005 (optimise python code)
233#            Steve Roberts, Aug 2009 (convert to numpy)
234#
235#
236#########################################################
237
238
239
240#########################################################
241#
242# Subdivide the triangles into non-overlapping domains.
243#
244#  *)  The subdivision is controlled by triangles_per_proc.
245# The first triangles_per_proc[0] triangles are assigned
246# to the first processor, the second triangles_per_proc[1]
247# are assigned to the second processor etc.
248#
249#  *) nodes, triangles and boundary contains all of the
250# nodes, triangles and boundary tag information for the
251# whole domain. The triangles should be orientated in the
252# correct way and the nodes number consecutively from 0.
253#
254# -------------------------------------------------------
255#
256#  *) A dictionary containing the full_nodes, full_triangles
257# and full_boundary information for each processor is
258# returned. The node information consists of
259# [global_id, x_coord, y_coord].
260#
261#########################################################
262
263def submesh_full(nodes, triangles, boundary, triangles_per_proc):
264
265    # Initialise
266
267    tlower = 0
268    nproc = len(triangles_per_proc)
269    nnodes = len(nodes)
270    node_list = []
271    triangle_list = []
272    boundary_list = []
273    submesh = {}
274    node_range = num.reshape(num.arange(nnodes),(nnodes,1))
275
276    #print node_range
277    tsubnodes = num.concatenate((node_range, nodes), 1)
278
279
280    # Loop over processors
281
282    for p in range(nproc):
283
284        # Find triangles on processor p
285
286        tupper = triangles_per_proc[p]+tlower
287        subtriangles = triangles[tlower:tupper]
288        triangle_list.append(subtriangles)
289
290        # Find the boundary edges on processor p
291
292        subboundary = {}
293        for k in boundary:
294            if (k[0] >=tlower and k[0] < tupper):
295                subboundary[k]=boundary[k]
296        boundary_list.append(subboundary)
297
298        # Find nodes in processor p
299
300        nodemap = num.zeros(nnodes, 'i')
301        for t in subtriangles:
302            nodemap[t[0]]=1
303            nodemap[t[1]]=1
304            nodemap[t[2]]=1
305
306       
307        node_list.append(tsubnodes.take(num.flatnonzero(nodemap),axis=0))
308
309        # Move to the next processor
310
311        tlower = tupper
312
313    # Put the results in a dictionary
314
315    submesh["full_nodes"] = node_list
316    submesh["full_triangles"] = triangle_list
317    submesh["full_boundary"] = boundary_list
318
319    # Clean up before exiting
320
321    del (nodemap)
322
323    return submesh
324
325
326#########################################################
327#
328# Build the ghost layer of triangles
329#
330#  *) Given the triangle subpartion for the processor
331# build a ghost layer of triangles. The ghost layer
332# consists of two layers of neighbouring triangles.
333#
334#  *) The vertices in the ghost triangles must also
335# be added to the node list for the current processor
336#
337#
338# -------------------------------------------------------
339#
340#  *) The extra triangles and nodes are returned.
341#
342#  *)  The node information consists of
343# [global_id, x_coord, y_coord].
344#
345#  *) The triangle information consists of
346# [triangle number, t], where t = [v1, v2, v3].
347#
348#########################################################
349
350def ghost_layer(submesh, mesh, p, tupper, tlower):
351
352    ncoord = mesh.number_of_nodes
353    ntriangles = mesh.number_of_triangles
354
355    # Find the first layer of boundary triangles
356
357    trianglemap = num.zeros(ntriangles, 'i')
358    for t in range(tlower, tupper):
359       
360        n = mesh.neighbours[t, 0]
361
362        if n >= 0:
363            if n < tlower or n >= tupper:
364                trianglemap[n] = 1
365        n = mesh.neighbours[t, 1]
366        if n >= 0:
367            if n < tlower or n >= tupper:
368                trianglemap[n] = 1
369        n = mesh.neighbours[t, 2]
370        if n >= 0:
371            if n < tlower or n >= tupper:
372                trianglemap[n] = 1
373
374    # Find the second layer of boundary triangles
375
376    for t in range(len(trianglemap)):
377        if trianglemap[t]==1:
378            n = mesh.neighbours[t, 0]
379            if n >= 0:
380                if (n < tlower or n >= tupper) and trianglemap[n] == 0:
381                    trianglemap[n] = 2
382            n = mesh.neighbours[t, 1]
383            if n >= 0:
384                if (n < tlower or n >= tupper) and trianglemap[n] == 0:
385                    trianglemap[n] = 2
386            n = mesh.neighbours[t, 2]
387            if n >= 0:
388                if (n < tlower or n >= tupper) and trianglemap[n] == 0:
389                    trianglemap[n] = 2
390
391    # Build the triangle list and make note of the vertices
392
393    nodemap = num.zeros(ncoord, 'i')
394    fullnodes = submesh["full_nodes"][p]
395
396    subtriangles = []
397    for i in range(len(trianglemap)):
398        if trianglemap[i] != 0:
399            t = list(mesh.triangles[i])
400            nodemap[t[0]] = 1
401            nodemap[t[1]] = 1
402            nodemap[t[2]] = 1
403
404    trilist = num.reshape(num.arange(ntriangles),(ntriangles,1))
405    tsubtriangles = num.concatenate((trilist, mesh.triangles), 1)
406    subtriangles = tsubtriangles.take(num.flatnonzero(trianglemap),axis=0)
407
408   
409    # Keep a record of the triangle vertices, if they are not already there
410
411    subnodes = []
412    for n in fullnodes:
413        nodemap[int(n[0])] = 0
414
415    nodelist = num.reshape(num.arange(ncoord),(ncoord,1))
416    tsubnodes = num.concatenate((nodelist, mesh.get_nodes()), 1)
417    subnodes = tsubnodes.take(num.flatnonzero(nodemap),axis=0)
418
419    # Clean up before exiting
420
421    del (nodelist)
422    del (trilist)
423    del (tsubnodes)
424    del (nodemap)
425    del (trianglemap)
426
427    # Return the triangles and vertices sitting on the boundary layer
428
429    return subnodes, subtriangles
430
431#########################################################
432#
433# Find the edges of the ghost trianlges that do not
434# have a neighbour in the current cell. These are
435# treated as a special type of boundary edge.
436#
437#  *) Given the ghost triangles in a particular
438# triangle, use the mesh to find its neigbours. If
439# the neighbour is not in the processor set it to
440# be a boundary edge
441#
442#  *) The vertices in the ghost triangles must also
443# be added to the node list for the current processor
444#
445#  *) The boundary edges for the ghost triangles are
446# ignored.
447#
448# -------------------------------------------------------
449#
450#  *) The type assigned to the ghost boundary edges is 'ghost'
451#
452#  *)  The boundary information is returned as a directorier
453# with the key = (triangle id, edge no) and the values
454# assigned to the key is 'ghost'
455#
456#
457#########################################################
458def is_in_processor(ghost_list, tlower, tupper, n):
459
460    return num.equal(ghost_list,n).any() or (tlower <= n and tupper > n)
461
462
463def ghost_bnd_layer(ghosttri, tlower, tupper, mesh, p):
464
465    ghost_list = []
466    subboundary = {}
467
468
469    for t in ghosttri:
470        ghost_list.append(t[0])
471   
472    for t in ghosttri:
473
474        n = mesh.neighbours[t[0], 0]
475        if not is_in_processor(ghost_list, tlower, tupper, n):
476            subboundary[t[0], 0] = 'ghost'
477
478        n = mesh.neighbours[t[0], 1]
479        if not is_in_processor(ghost_list, tlower, tupper, n):
480            subboundary[t[0], 1] = 'ghost'
481
482        n = mesh.neighbours[t[0], 2]
483        if not is_in_processor(ghost_list, tlower, tupper, n):
484            subboundary[t[0], 2] = 'ghost'
485           
486    return subboundary
487
488#########################################################
489#
490# The ghost triangles on the current processor will need
491# to get updated information from the neighbouring
492# processor containing the corresponding full triangles.
493#
494#  *) The tri_per_proc is used to determine which
495# processor contains the full node copy.
496#
497# -------------------------------------------------------
498#
499#  *) The ghost communication pattern consists of
500# [global node number, neighbour processor number].
501#
502#########################################################
503
504def ghost_commun_pattern(subtri, p, tri_per_proc):
505
506    # Loop over the ghost triangles
507
508    ghost_commun = num.zeros((len(subtri), 2), num.int)
509
510    for i in range(len(subtri)):
511        global_no = subtri[i][0]
512
513        # Find which processor contains the full triangle
514
515        nproc = len(tri_per_proc)
516        neigh = nproc-1
517        sum = 0
518        for q in range(nproc-1):
519            if (global_no < sum+tri_per_proc[q]):
520                neigh = q
521                break
522            sum = sum+tri_per_proc[q]
523
524        # Keep a copy of the neighbour processor number
525
526        ghost_commun[i] = [global_no, neigh]
527
528    return ghost_commun
529
530#########################################################
531#
532# The full triangles in this processor must communicate
533# updated information to neighbouring processor that
534# contain ghost triangles
535#
536#  *) The ghost communication pattern for all of the
537# processor must be built before calling this processor.
538#
539#  *) The full communication pattern is found by looping
540# through the ghost communication pattern for all of the
541# processors. Recall that this information is stored in
542# the form [global node number, neighbour processor number].
543# The full communication for the neighbour processor is
544# then updated.
545#
546# -------------------------------------------------------
547#
548#  *) The full communication pattern consists of
549# [global id, [p1, p2, ...]], where p1, p2 etc contain
550# a ghost node copy of the triangle global id.
551#
552#########################################################
553
554def full_commun_pattern(submesh, tri_per_proc):
555    tlower = 0
556    nproc = len(tri_per_proc)
557    full_commun = []
558
559    # Loop over the processor
560
561    for p in range(nproc):
562
563        # Loop over the full triangles in the current processor
564        # and build an empty dictionary
565
566        fcommun = {}
567        tupper = tri_per_proc[p]+tlower
568        for i in range(tlower, tupper):
569            fcommun[i] = []
570        full_commun.append(fcommun)
571        tlower = tupper
572
573    # Loop over the processor again
574
575    for p in range(nproc):
576
577        # Loop over the ghost triangles in the current processor,
578        # find which processor contains the corresponding full copy
579        # and note that the processor must send updates to this
580        # processor
581
582        for g in submesh["ghost_commun"][p]:
583            neigh = g[1]
584            full_commun[neigh][g[0]].append(p)
585
586    return full_commun
587
588
589#########################################################
590#
591# Given the non-overlapping grid partition, an extra layer
592# of triangles are included to help with the computations.
593# The triangles in this extra layer are not updated by
594# the processor, their updated values must be sent by the
595# processor containing the original, full, copy of the
596# triangle. The communication pattern that controls these
597# updates must also be built.
598#
599#  *) Assumes that full triangles, nodes etc have already
600# been found and stored in submesh
601#
602#  *) See the documentation for ghost_layer,
603# ghost_commun_pattern and full_commun_pattern
604#
605# -------------------------------------------------------
606#
607#  *) The additional information is added to the submesh
608# dictionary. See the documentation for ghost_layer,
609# ghost_commun_pattern and full_commun_pattern
610#
611#  *) The ghost_triangles, ghost_nodes, ghost_boundary,
612# ghost_commun and full_commun is added to submesh
613#########################################################
614
615def submesh_ghost(submesh, mesh, triangles_per_proc):
616
617    nproc = len(triangles_per_proc)
618    tlower = 0
619    ghost_triangles = []
620    ghost_nodes = []
621    ghost_commun = []
622    ghost_bnd = []
623
624    # Loop over the processors
625
626    for p in range(nproc):
627
628        # Find the full triangles in this processor
629
630        tupper = triangles_per_proc[p]+tlower
631
632        # Build the ghost boundary layer
633
634        [subnodes, subtri] = \
635                   ghost_layer(submesh, mesh, p, tupper, tlower)
636        ghost_triangles.append(subtri)
637        ghost_nodes.append(subnodes)
638
639
640        # Find the boundary layer formed by the ghost triangles
641       
642        subbnd = ghost_bnd_layer(subtri, tlower, tupper, mesh, p)
643        ghost_bnd.append(subbnd)
644       
645        # Build the communication pattern for the ghost nodes
646
647        gcommun = \
648                ghost_commun_pattern(subtri, p, triangles_per_proc)
649        ghost_commun.append(gcommun)
650
651        # Move to the next processor
652
653        tlower = tupper
654
655
656    # Record the ghost layer and communication pattern
657
658    submesh["ghost_nodes"] = ghost_nodes
659    submesh["ghost_triangles"] = ghost_triangles
660    submesh["ghost_commun"] = ghost_commun
661    submesh["ghost_boundary"] = ghost_bnd
662   
663    # Build the communication pattern for the full triangles
664
665    full_commun = full_commun_pattern(submesh, triangles_per_proc)
666    submesh["full_commun"] = full_commun
667
668    # Return the submesh
669
670    return submesh
671
672
673#########################################################
674#
675# Certain quantities may be assigned to the triangles,
676# these quantities must be subdivided in the same way
677# as the triangles
678#
679#  *) The quantities are ordered in the same way as the
680# triangles
681#
682# -------------------------------------------------------
683#
684#  *) The quantites attached to the full triangles are
685# stored in full_quan
686#
687#  *) The quantities attached to the ghost triangles are
688# stored in ghost_quan
689#########################################################
690
691def submesh_quantities(submesh, quantities, triangles_per_proc):
692
693    nproc = len(triangles_per_proc)
694
695    lower = 0
696
697    # Build an empty dictionary to hold the quantites
698
699    submesh["full_quan"] = {}
700    submesh["ghost_quan"] = {}
701    for k in quantities:
702        submesh["full_quan"][k] = []
703        submesh["ghost_quan"][k] = []
704
705    # Loop trough the subdomains
706
707    for p in range(nproc):
708        upper =   lower+triangles_per_proc[p]
709
710        # Find the global ID of the ghost triangles
711
712        global_id = []
713        M = len(submesh["ghost_triangles"][p])
714        for j in range(M):
715            global_id.append(submesh["ghost_triangles"][p][j][0])
716
717        # Use the global ID to extract the quantites information from
718        # the full domain
719
720        for k in quantities:
721            submesh["full_quan"][k].append(quantities[k][lower:upper])
722            submesh["ghost_quan"][k].append(num.zeros( (M,3) , num.float))
723            for j in range(M):
724                submesh["ghost_quan"][k][p][j] = \
725                                               quantities[k][global_id[j]]
726
727        lower = upper
728
729    return submesh
730
731#########################################################
732#
733# Build the grid partition on the host.
734#
735#  *) See the documentation for submesh_ghost and
736# submesh_full
737#
738# -------------------------------------------------------
739#
740#  *) A dictionary containing the full_triangles,
741# full_nodes, full_boundary, ghost_triangles, ghost_nodes,
742# ghost_boundary, ghost_commun and full_commun and true boundary polygon is returned.
743#
744#########################################################
745
746def build_submesh(nodes, triangles, edges, quantities,
747                  triangles_per_proc):
748
749    # Temporarily build the mesh to find the neighbouring
750    # triangles and true boundary polygon
751
752    mesh = Mesh(nodes, triangles)
753    boundary_polygon = mesh.get_boundary_polygon()
754   
755
756    # Subdivide into non-overlapping partitions
757
758    submeshf = submesh_full(nodes, triangles, edges, \
759                            triangles_per_proc)
760   
761    # Add any extra ghost boundary layer information
762
763    submeshg = submesh_ghost(submeshf, mesh, triangles_per_proc)
764
765    # Order the quantities information to be the same as the triangle
766    # information
767
768    submesh = submesh_quantities(submeshg, quantities, \
769                                 triangles_per_proc)
770
771    submesh["boundary_polygon"] = boundary_polygon
772    return submesh
773
774#########################################################
775#
776#  Given the subdivision of the grid assigned to the
777# current processor convert it into a form that is
778# appropriate for the GA datastructure.
779#
780#  The main function of these modules is to change the
781# node numbering. The GA datastructure assumes they
782# are numbered consecutively from 0.
783#
784#  The module also changes the communication pattern
785# datastructure into a form needed by parallel_advection
786#
787#  Authors: Linda Stals and Matthew Hardy, June 2005
788#  Modified: Linda Stals, Nov 2005 (optimise python code)
789#            Steve Roberts, Aug 2009 (updating to numpy)
790#
791#
792#########################################################
793
794 
795#########################################################
796# Convert the format of the data to that used by ANUGA
797#
798#
799# *) Change the nodes global ID's to an integer value,
800#starting from 0.
801#
802# *) The triangles and boundary edges must also be
803# updated accordingly.
804#
805# -------------------------------------------------------
806#
807# *) The nodes, triangles and boundary edges defined by
808# the new numbering scheme are returned
809#
810#########################################################
811
812def build_local_GA(nodes, triangles, boundaries, tri_map):
813
814    Nnodes =len(nodes)
815    Ntriangles = len(triangles)
816   
817    # Extract the nodes (using the local ID)
818   
819    GAnodes = num.take(nodes, (1, 2), 1)
820
821    # Build a global ID to local ID mapping
822
823    NGlobal = 0
824    for i in range(Nnodes):
825        if nodes[i][0] > NGlobal:
826            NGlobal = nodes[i][0]
827
828    node_map = -1*num.ones(int(NGlobal)+1, num.int)
829
830    num.put(node_map, num.take(nodes, (0,), 1).astype(num.int), \
831        num.arange(Nnodes))
832       
833    # Change the global IDs in the triangles to the local IDs
834
835    GAtriangles = num.zeros((Ntriangles, 3), num.int)
836    GAtriangles[:,0] = num.take(node_map, triangles[:,0])
837    GAtriangles[:,1] = num.take(node_map, triangles[:,1])
838    GAtriangles[:,2] = num.take(node_map, triangles[:,2])
839
840    # Change the triangle numbering in the boundaries
841
842    GAboundaries = {}
843    for b in boundaries:
844        GAboundaries[tri_map[b[0]], b[1]] = boundaries[b]
845       
846   
847    return GAnodes, GAtriangles, GAboundaries, node_map
848
849
850#########################################################
851# Change the communication format to that needed by the
852# parallel advection file.
853#
854# *) The index contains [global triangle no,
855# local triangle no.]
856#
857# -------------------------------------------------------
858#
859# *) The ghost_recv and full_send dictionaries are
860# returned.
861#
862# *) ghost_recv dictionary is local id, global id, value
863#
864# *) full_recv dictionary is local id, global id, value
865#
866# *) The information is ordered by the global id. This
867# means that the communication order is predetermined and
868# local and global id do not need to be
869# compared when the information is sent/received.
870#
871#########################################################
872
873def build_local_commun(tri_map, ghostc, fullc, nproc):
874
875    # Initialise
876
877    full_send = {}
878    ghost_recv = {}
879
880    # Build the ghost_recv dictionary (sort the
881    # information by the global numbering)
882   
883    ghostc = num.sort(ghostc, 0)
884   
885    for c in range(nproc):
886        s = ghostc[:,0]
887        d = num.compress(num.equal(ghostc[:,1],c), s)
888        if len(d) > 0:
889            ghost_recv[c] = [0, 0]
890            ghost_recv[c][1] = d
891            ghost_recv[c][0] = num.take(tri_map, d)
892           
893    # Build a temporary copy of the full_send dictionary
894    # (this version allows the information to be stored
895    # by the global numbering)
896
897    tmp_send = {}
898    for global_id in fullc:
899        for i in range(len(fullc[global_id])):
900            neigh = fullc[global_id][i]
901            if not tmp_send.has_key(neigh):
902                tmp_send[neigh] = []
903            tmp_send[neigh].append([global_id, \
904                                    tri_map[global_id]])
905
906    # Extract the full send information and put it in the form
907    # required for the full_send dictionary
908
909    for neigh in tmp_send:
910        neigh_commun = num.sort(tmp_send[neigh], 0)
911        full_send[neigh] = [0, 0]
912        full_send[neigh][0] = neigh_commun[:,1]
913        full_send[neigh][1] = neigh_commun[:,0]
914
915    return ghost_recv, full_send
916
917
918#########################################################
919# Convert the format of the data to that used by ANUGA
920#
921#
922# *) Change the nodes global ID's to an integer value,
923# starting from 0. The node numbering in the triangles
924# must also be updated to take this into account.
925#
926# *) The triangle number will also change, which affects
927# the boundary tag information and the communication
928# pattern.
929#
930# -------------------------------------------------------
931#
932# *) The nodes, triangles, boundary edges and communication
933# pattern defined by the new numbering scheme are returned
934#
935#########################################################
936
937def build_local_mesh(submesh, lower_t, upper_t, nproc):
938
939    # Combine the full nodes and ghost nodes
940
941    nodes = num.concatenate((submesh["full_nodes"], \
942                         submesh["ghost_nodes"]))
943   
944    # Combine the full triangles and ghost triangles
945
946    gtri =  num.take(submesh["ghost_triangles"],(1, 2, 3),1)
947    triangles = num.concatenate((submesh["full_triangles"], gtri))
948
949    # Combine the full boundaries and ghost boundaries
950
951    boundaries = submesh["full_boundary"]
952    for b in submesh["ghost_boundary"]:
953        boundaries[b]=submesh["ghost_boundary"][b]
954
955    # Make note of the new triangle numbers, including the ghost
956    # triangles
957
958    NGlobal = upper_t
959    for i in range(len(submesh["ghost_triangles"])):
960        id = submesh["ghost_triangles"][i][0]
961        if id > NGlobal:
962            NGlobal = id
963    #index = num.zeros(int(NGlobal)+1, num.int)
964    tri_map = -1*num.ones(int(NGlobal)+1, num.int)
965    tri_map[lower_t:upper_t]=num.arange(upper_t-lower_t)
966    for i in range(len(submesh["ghost_triangles"])):
967        tri_map[submesh["ghost_triangles"][i][0]] = i+upper_t-lower_t
968   
969    # Change the node numbering (and update the numbering in the
970    # triangles)
971
972    [GAnodes, GAtriangles, GAboundary, node_map] = \
973    build_local_GA(nodes, triangles, boundaries, tri_map)
974
975    # Extract the local quantities
976   
977    quantities ={}
978    for k in submesh["full_quan"]:
979        Nf = len(submesh["full_quan"][k])
980        Ng = len(submesh["ghost_quan"][k])
981        quantities[k] = num.zeros((Nf+Ng, 3), num.float)
982        quantities[k][0:Nf] = submesh["full_quan"][k] 
983        quantities[k][Nf:Nf+Ng] = submesh["ghost_quan"][k]
984                             
985    # Change the communication pattern into a form needed by
986    # the parallel_adv
987
988    gcommun = submesh["ghost_commun"]
989    fcommun = submesh["full_commun"]
990    [ghost_rec, full_send] = \
991                build_local_commun(tri_map, gcommun, fcommun, nproc)
992
993
994    return GAnodes, GAtriangles, GAboundary, quantities, ghost_rec, \
995           full_send, tri_map, node_map
996
997
998#########################################################
999#
1000# Handle the communication between the host machine
1001# (processor 0) and the processors. The host machine is
1002# responsible for the doing the initial grid partitioning.
1003#
1004# The routines given below should be moved to the
1005# build_submesh.py and build_local.py file to allow
1006# overlapping of  communication and computation.
1007# This should be done after more debugging.
1008#
1009#
1010#  Author: Linda Stals, June 2005
1011#  Modified: Linda Stals, Nov 2005 (optimise python code)
1012#            Steve Roberts, Aug 2009 (update to numpy)
1013#
1014#
1015#########################################################
1016
1017
1018#########################################################
1019#
1020# Send the submesh to processor p.
1021#
1022# *) The order and form is strongly coupled with
1023# rec_submesh.
1024#
1025# -------------------------------------------------------
1026#
1027# *) All of the information has been sent to processor p.
1028#
1029#########################################################
1030
1031def send_submesh(submesh, triangles_per_proc, p, verbose=True):
1032
1033    import pypar
1034   
1035    myid = pypar.rank()
1036   
1037    if verbose: print 'process %d sending submesh to process %d' %(myid, p)
1038   
1039    # build and send the tagmap for the boundary conditions
1040   
1041    tagmap = {}
1042    counter = 1
1043    for b in submesh["full_boundary"][p]:
1044         bkey = submesh["full_boundary"][p][b]
1045         if not tagmap.has_key(bkey):
1046             tagmap[bkey] = counter
1047             counter = counter+1
1048    for b in submesh["ghost_boundary"][p]:
1049         bkey = submesh["ghost_boundary"][p][b]
1050         if not tagmap.has_key(bkey):
1051             tagmap[bkey] = counter
1052             counter = counter+1
1053
1054    pypar.send(tagmap, p)
1055
1056    # send the quantities key information
1057
1058    pypar.send(submesh["full_quan"].keys(), p)
1059
1060    # send the number of triangles per processor
1061
1062    pypar.send(triangles_per_proc, p)
1063
1064    # compress full_commun
1065
1066    flat_full_commun = []
1067
1068    for c in submesh["full_commun"][p]:
1069        for i in range(len(submesh["full_commun"][p][c])):
1070            flat_full_commun.append([c,submesh["full_commun"][p][c][i]])
1071
1072    # send the array sizes so memory can be allocated
1073
1074    setup_array = num.zeros((9,),num.int)
1075    setup_array[0] = len(submesh["full_nodes"][p])
1076    setup_array[1] = len(submesh["ghost_nodes"][p])
1077    setup_array[2] = len(submesh["full_triangles"][p])
1078    setup_array[3] = len(submesh["ghost_triangles"][p])
1079    setup_array[4] = len(submesh["full_boundary"][p])
1080    setup_array[5] = len(submesh["ghost_boundary"][p])
1081    setup_array[6] = len(submesh["ghost_commun"][p])
1082    setup_array[7] = len(flat_full_commun)
1083    setup_array[8] = len(submesh["full_quan"])
1084   
1085    pypar.send(num.array(setup_array, num.int), p)
1086   
1087    # send the nodes
1088
1089    pypar.send(num.array(submesh["full_nodes"][p], num.float), p)
1090    pypar.send(num.array(submesh["ghost_nodes"][p], num.float),p)
1091
1092    # send the triangles
1093
1094    pypar.send(num.array(submesh["full_triangles"][p],  num.int), p)
1095    pypar.send(num.array(submesh["ghost_triangles"][p], num.int), p)
1096
1097    # send the boundary
1098
1099    bc = []
1100    for b in submesh["full_boundary"][p]:
1101        bc.append([b[0], b[1], tagmap[submesh["full_boundary"][p][b]]])
1102
1103
1104    pypar.send(num.array(bc, num.int), p)
1105
1106    bc = []
1107    for b in submesh["ghost_boundary"][p]:
1108        bc.append([b[0], b[1], tagmap[submesh["ghost_boundary"][p][b]]])
1109
1110    pypar.send(num.array(bc, num.int), p)
1111
1112    # send the communication pattern
1113
1114    pypar.send(submesh["ghost_commun"][p], p)
1115
1116    pypar.send(num.array(flat_full_commun, num.int), p)
1117
1118    # send the quantities
1119   
1120    for k in submesh["full_quan"]:
1121        pypar.send(num.array(submesh["full_quan"][k][p], num.float), p)
1122       
1123    for k in submesh["ghost_quan"]:
1124        pypar.send(num.array(submesh["ghost_quan"][k][p], num.float),p)
1125       
1126
1127#########################################################
1128#
1129# Receive the submesh from processor p.
1130#
1131# *) The order and form is strongly coupled with
1132# send_submesh.
1133#
1134# -------------------------------------------------------
1135#
1136# *) All of the information has been received by the
1137# processor p and passed into build_local.
1138#
1139# *) The information is returned in a form needed by the
1140# GA datastructure.
1141#
1142#########################################################
1143
1144def rec_submesh_flat(p, verbose=True):
1145
1146    import pypar
1147   
1148    numproc = pypar.size()
1149    myid = pypar.rank()
1150
1151    submesh_cell = {}
1152   
1153    if verbose: print indent+'P%d: Receiving submesh from P%d' %(myid, p)
1154
1155    # receive the tagmap for the boundary conditions
1156   
1157    tagmap = pypar.receive(p)
1158
1159    itagmap = {}
1160    for t in tagmap:
1161        itagmap[tagmap[t]]=t
1162
1163    # receive the quantities key information
1164
1165    qkeys = pypar.receive(p)
1166
1167    # receive the number of triangles per processor
1168
1169    triangles_per_proc = pypar.receive(p)
1170
1171    # recieve information about the array sizes
1172
1173    setup_array = pypar.receive(p)
1174
1175    no_full_nodes      = setup_array[0]
1176    no_ghost_nodes     = setup_array[1]
1177    no_full_triangles  = setup_array[2]
1178    no_ghost_triangles = setup_array[3]
1179    no_full_boundary   = setup_array[4]
1180    no_ghost_boundary  = setup_array[5]
1181    no_ghost_commun    = setup_array[6]
1182    no_full_commun     = setup_array[7]
1183    no_quantities      = setup_array[8]
1184   
1185    # receive the full nodes
1186
1187    submesh_cell["full_nodes"] = pypar.receive(p)
1188
1189    # receive the ghost nodes
1190
1191    submesh_cell["ghost_nodes"] = pypar.receive(p)
1192   
1193    # receive the full triangles
1194
1195    submesh_cell["full_triangles"] = pypar.receive(p)
1196   
1197    # receive the ghost triangles
1198
1199    submesh_cell["ghost_triangles"] = pypar.receive(p)
1200
1201    # receive the full boundary
1202
1203    bnd_c = pypar.receive(p)
1204
1205    submesh_cell["full_boundary"] = {}
1206    for b in bnd_c:
1207        submesh_cell["full_boundary"][b[0],b[1]]=itagmap[b[2]]
1208
1209    # receive the ghost boundary
1210
1211    bnd_c = pypar.receive(p)
1212
1213    submesh_cell["ghost_boundary"] = {}
1214    for b in bnd_c:
1215        submesh_cell["ghost_boundary"][b[0],b[1]]=itagmap[b[2]]
1216
1217    # receive the ghost communication pattern
1218
1219    submesh_cell["ghost_commun"] = pypar.receive(p)
1220   
1221    # receive the full communication pattern
1222
1223    full_commun = pypar.receive(p)
1224
1225    submesh_cell["full_commun"] = {}
1226    for c in full_commun:
1227        submesh_cell["full_commun"][c[0]] = []
1228    for c in full_commun:
1229        submesh_cell["full_commun"][c[0]].append(c[1])
1230
1231    # receive the quantities
1232
1233    submesh_cell["full_quan"]={}
1234   
1235    for i in range(no_quantities):
1236        tmp = pypar.receive(p)
1237        submesh_cell["full_quan"][qkeys[i]]=num.zeros((no_full_triangles,3), num.float)
1238        submesh_cell["full_quan"][qkeys[i]][:] = tmp[:]
1239
1240    submesh_cell["ghost_quan"]={}
1241    for i in range(no_quantities):
1242        tmp = pypar.receive(p)
1243        submesh_cell["ghost_quan"][qkeys[i]]= num.zeros((no_ghost_triangles,3), num.float)
1244        submesh_cell["ghost_quan"][qkeys[i]][:] = tmp[:]
1245   
1246    return submesh_cell, triangles_per_proc,\
1247           no_full_nodes, no_full_triangles
1248
1249
1250
1251#########################################################
1252#
1253# Receive the submesh from processor p.
1254#
1255# *) The order and form is strongly coupled with
1256# send_submesh.
1257#
1258# -------------------------------------------------------
1259#
1260# *) All of the information has been received by the
1261# processor p and passed into build_local.
1262#
1263# *) The information is returned in a form needed by the
1264# GA datastructure.
1265#
1266#########################################################
1267
1268def rec_submesh(p, verbose=True):
1269
1270    import pypar
1271   
1272    numproc = pypar.size()
1273    myid = pypar.rank()
1274
1275    [submesh_cell, triangles_per_proc,\
1276     number_of_full_nodes, number_of_full_triangles] = rec_submesh_flat(p,verbose)
1277   
1278    # find the full triangles assigned to this processor
1279
1280    lower_t = 0
1281    for i in range(myid):
1282        lower_t = lower_t+triangles_per_proc[i]
1283    upper_t = lower_t+triangles_per_proc[myid]
1284
1285    # convert the information into a form needed by the GA
1286    # datastructure
1287
1288    [GAnodes, GAtriangles, boundary, quantities, \
1289     ghost_rec, full_send, tri_map, node_map] = \
1290              build_local_mesh(submesh_cell, lower_t, upper_t, \
1291                               numproc)
1292   
1293    return GAnodes, GAtriangles, boundary, quantities,\
1294           ghost_rec, full_send,\
1295           number_of_full_nodes, number_of_full_triangles, tri_map, node_map
1296         
1297
1298
1299#########################################################
1300#
1301# Extract the submesh that will belong to the
1302# "host processor" (i.e. processor zero)
1303#
1304#  *) See the documentation for build_submesh
1305#
1306# -------------------------------------------------------
1307#
1308#  *) A dictionary containing the full_triangles,
1309# full_nodes, full_boundary, ghost_triangles, ghost_nodes,
1310# ghost_boundary, ghost_commun and full_commun belonging
1311# to processor zero are returned.
1312#
1313#########################################################
1314def extract_hostmesh(submesh, triangles_per_proc):
1315
1316   
1317    submesh_cell = {}
1318    submesh_cell["full_nodes"] = submesh["full_nodes"][0]
1319    submesh_cell["ghost_nodes"] = submesh["ghost_nodes"][0]
1320    submesh_cell["full_triangles"] = submesh["full_triangles"][0]
1321    submesh_cell["ghost_triangles"] = submesh["ghost_triangles"][0]
1322    submesh_cell["full_boundary"] = submesh["full_boundary"][0]
1323    submesh_cell["ghost_boundary"] = submesh["ghost_boundary"][0]
1324    submesh_cell["ghost_commun"] = submesh["ghost_commun"][0]
1325    submesh_cell["full_commun"] = submesh["full_commun"][0]
1326    submesh_cell["full_quan"] ={}
1327    submesh_cell["ghost_quan"]={}
1328    for k in submesh["full_quan"]:
1329        submesh_cell["full_quan"][k] = submesh["full_quan"][k][0]
1330        submesh_cell["ghost_quan"][k] = submesh["ghost_quan"][k][0]
1331
1332    numprocs = len(triangles_per_proc)
1333    points, vertices, boundary, quantities, ghost_recv_dict, \
1334            full_send_dict, tri_map, node_map = \
1335            build_local_mesh(submesh_cell, 0, triangles_per_proc[0], numprocs)
1336
1337
1338    return  points, vertices, boundary, quantities, ghost_recv_dict, \
1339           full_send_dict, tri_map, node_map
1340           
1341
1342
1343
1344
Note: See TracBrowser for help on using the repository browser.