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

Last change on this file since 8856 was 8856, checked in by steve, 12 years ago

Got an error when distributing a small mesh over 17 processors

File size: 51.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
26import numpy.lib.arraysetops as numset
27
28from anuga.abstract_2d_finite_volumes.neighbour_mesh import Mesh
29from anuga import indent
30
31try:
32    import local_config as config
33except:
34    import anuga_parallel.config as config
35
36
37#########################################################
38#
39# If the triangles list is reordered, the quantities
40# assigned to the triangles must also be reorded.
41#
42# *) quantities contain the quantites in the old ordering
43# *) proc_sum[i] contains the number of triangles in
44# processor i
45# *) tri_index is a map from the old triangle ordering to
46# the new ordering, where the new number for triangle
47# i is proc_sum[tri_index[i][0]]+tri_index[i][1]
48#
49# -------------------------------------------------------
50#
51# *) The quantaties are returned in the new ordering
52#
53#########################################################
54
55def reorder(quantities, tri_index, proc_sum):
56
57    # Find the number triangles
58
59    N = len(tri_index)
60
61    # Temporary storage area
62
63    index = num.zeros(N, num.int)
64    q_reord = {}
65
66    # Find the new ordering of the triangles
67
68    for i in xrange(N):
69        bin = tri_index[i][0]
70        bin_off_set = tri_index[i][1]
71        index[i] = proc_sum[bin]+bin_off_set
72
73    # Reorder each quantity according to the new ordering
74
75    for k in quantities:
76        q_reord[k] = num.zeros((N, 3), num.float)
77        for i in range(N):
78            q_reord[k][index[i]]=quantities[k].vertex_values[i]
79    del index
80
81    return q_reord
82
83
84def reorder_new(quantities, epart_order, proc_sum):
85
86    # Find the number triangles
87
88    N = len(epart_order)
89
90    # Temporary storage area
91
92
93    q_reord = {}
94
95    # Reorder each quantity according to the new ordering
96
97    for k in quantities:
98        q_reord[k] = num.zeros((N, 3), num.float)
99        q_reord[k][:] = quantities[k].vertex_values[epart_order]
100       
101
102    return q_reord
103
104
105#########################################################
106#
107# Divide the mesh using a call to metis, through pymetis.
108#
109# -------------------------------------------------------
110#
111# *)  The nodes, triangles, boundary, and quantities are
112# returned. triangles_per_proc defines the subdivision.
113# The first triangles_per_proc[0] triangles are assigned
114# to processor 0, the next triangles_per_proc[1] are
115# assigned to processor 1 etc. The boundary and quantites
116# are ordered the same way as the triangles
117#
118#########################################################
119
120#path.append('..' + sep + 'pymetis')
121
122try:
123    from pymetis.metis import partMeshNodal
124except ImportError:
125    print "***************************************************"
126    print "         Metis is probably not compiled."
127    print "         Read \anuga_core\source\pymetis\README"
128    print "***************************************************"
129    raise ImportError
130
131def pmesh_divide_metis(domain, n_procs):
132    # Wrapper for old pmesh_divide_metis which does not return tri_index or r_tri_index
133    nodes, ttriangles, boundary, triangles_per_proc, quantities, tri_index, r_tri_index = pmesh_divide_metis_helper(domain, n_procs)
134
135    print triangles_per_proc
136    assert num.all(triangles_per_proc>0), 'Metis created a partition where one mesh has 0 triangles'
137
138    return nodes, ttriangles, boundary, triangles_per_proc, quantities
139
140def pmesh_divide_metis_with_map(domain, n_procs):
141
142    return pmesh_divide_metis_helper(domain, n_procs)
143
144def pmesh_divide_metis_helper(domain, n_procs):
145   
146    # Initialise the lists
147    # List, indexed by processor of # triangles.
148   
149    triangles_per_proc = []
150   
151    # List of lists, indexed by processor of vertex numbers
152   
153    tri_list = []
154
155    # Serial to Parallel and Parallel to Serial Triangle index maps
156    tri_index = {}
157    r_tri_index = {} # reverse tri index, parallel to serial triangle index mapping
158   
159    # List indexed by processor of cumulative total of triangles allocated.
160   
161    proc_sum = []
162    for i in xrange(n_procs):
163        tri_list.append([])
164        triangles_per_proc.append(0)
165        proc_sum.append([])
166
167    # Prepare variables for the metis call
168   
169    n_tri = len(domain.triangles)
170    if n_procs != 1: #Because metis chokes on it...
171        n_vert = domain.get_number_of_nodes()
172        t_list = domain.triangles.copy()
173        t_list = num.reshape(t_list, (-1,))
174   
175        # The 1 here is for triangular mesh elements.
176        edgecut, epart, npart = partMeshNodal(n_tri, n_vert, t_list, 1, n_procs)
177        # print edgecut
178        # print npart
179        #print epart
180        del edgecut
181        del npart
182
183        # Sometimes (usu. on x86_64), partMeshNodal returns an array of zero
184        # dimensional arrays. Correct this.
185        if type(epart[0]) == num.ndarray:
186            epart_new = num.zeros(len(epart), num.int)
187            epart_new[:] = epart[:][0]
188#            for i in xrange(len(epart)):
189#                epart_new[i] = epart[i][0]
190            epart = epart_new
191            del epart_new
192        # Assign triangles to processes, according to what metis told us.
193       
194        # tri_index maps triangle number -> processor, new triangle number
195        # (local to the processor)
196
197
198
199        #triangles = []       
200        #for i in xrange(n_tri):
201        #    #triangles_per_proc[epart[i]] = triangles_per_proc[epart[i]] + 1
202        #    tri_list[epart[i]].append(domain.triangles[i])
203        #    tri_index[i] = [epart[i], len(tri_list[epart[i]]) - 1]
204        #    r_tri_index[epart[i], len(tri_list[epart[i]]) - 1] = i
205
206
207        #for i in xrange(n_procs):
208        #    tri_list[i] = num.array(tri_list[i])
209
210        #for i in xrange(n_procs):
211        #    for t in tri_list[i]:
212        #        triangles.append(t)
213
214        triangles_per_proc = num.bincount(epart)
215
216        msg = """Metis created a partition where at least one submesh has no triangles.
217        Try using a smaller number of mpi processors"""
218        assert num.all(triangles_per_proc>0), msg
219
220        proc_sum = num.zeros(n_procs+1,num.int)
221        proc_sum[1:] = num.cumsum(triangles_per_proc)
222
223        #boundary = {}
224        #for b in domain.boundary:
225        #    t =  tri_index[b[0]]
226        #    #print t
227        #    boundary[proc_sum[t[0]]+t[1], b[1]] = domain.boundary[b]
228
229
230
231        #print epart
232        #print len(tri_list[epart[0]])
233
234        #print tri_list
235        #print
236        #print tri_index
237
238
239        epart_order = num.argsort(epart, kind='mergesort')
240        new_triangles = domain.triangles[epart_order]
241
242        #new_r_tri_index_flat = num.zeros((n_tri,3), num.int)
243        new_tri_index = num.zeros((n_tri,2), num.int)
244        for i in xrange(n_procs):
245            ids = num.arange(proc_sum[i],proc_sum[i+1])
246            eids = epart_order[ids]
247            nrange = num.reshape(num.arange(triangles_per_proc[i]), (-1,1))
248            nones = num.ones_like(nrange)
249            #print ids.shape
250            #print nrange.shape
251            new_tri_index[eids] = num.concatenate((i*nones, nrange), axis = 1)
252            #new_r_tri_index_flat[ids] = num.concatenate((i*nones, nrange, num.reshape(eids, (-1,1))), axis = 1)
253
254
255        #from pprint import pprint
256        #pprint(new_r_tri_index_flat[:,0:2])
257
258        #print 50*'='
259        new_boundary = {}
260        for b in domain.boundary:
261            t =  new_tri_index[b[0]]
262            #print t
263            new_boundary[proc_sum[t[0]]+t[1], b[1]] = domain.boundary[b]
264
265
266
267#        for i in range(n_tri):
268#            assert num.allclose(new_tri_index[i], tri_index[i])
269
270
271        #x = new_r_tri_index_flat
272        #print len(x)
273
274        #new_r_tri_index = {}
275        #for i in xrange(n_tri):
276        #    new_r_tri_index[x[i,0], x[i,1]] = x[i,2]
277
278
279        #assert new_r_tri_index == r_tri_index
280
281        #from pprint import pprint
282        #pprint(new_r_tri_index)
283        #pprint(r_tri_index)
284
285        #print len(new_tri_list[0])
286        #print new_tri_list
287        #print tri_list
288
289        #for i in xrange(n_procs):
290        #    assert num.allclose(tri_list[i], new_tri_list[i]), 'i %d\n'%i
291#        tri_list = new_tri_list
292#        triangles = new_triangles
293
294
295#        for i in xrange(n_tri):
296#            tri_index[i] = ([epart[i], len(tri_list[epart[i]]) - 1])
297#            r_tri_index[epart[i], len(tri_list[epart[i]]) - 1] = i
298
299        #assert num.allclose(triangles_per_proc, new_triangles_per_proc)
300       
301        # Order the triangle list so that all of the triangles belonging
302        # to processor i are listed before those belonging to processor
303        # i+1
304
305
306
307
308
309
310        #print new_triangles
311        #print num.array(triangles)
312
313        #assert num.allclose(new_triangles,num.array(triangles))
314           
315        # The boundary labels have to changed in accoradance with the
316        # new triangle ordering, proc_sum and tri_index help with this
317
318#        proc_sum[0] = 0
319#        for i in xrange(n_procs - 1):
320#            proc_sum[i+1]=proc_sum[i]+triangles_per_proc[i]
321
322
323
324
325
326        #assert num.allclose(new_proc_sum,proc_sum)
327
328        # Relabel the boundary elements to fit in with the new triangle
329        # ordering
330
331
332
333        #quantities = reorder(domain.quantities, tri_index, proc_sum)
334        new_quantities = reorder_new(domain.quantities, epart_order, proc_sum)
335
336    else:
337        new_boundary = domain.boundary.copy()
338        triangles_per_proc[0] = n_tri
339        new_triangles = domain.triangles.copy()
340        new_tri_index = []
341        epart_order = []
342       
343        # This is essentially the same as a chunk of code from reorder.
344       
345        new_quantities = {}
346        for k in domain.quantities:
347            new_quantities[k] = num.zeros((n_tri, 3), num.float)
348            for i in range(n_tri):
349                new_quantities[k][i] = domain.quantities[k].vertex_values[i]
350       
351    # Extract the node list
352   
353    new_nodes = domain.get_nodes().copy()
354   
355    # Convert the triangle datastructure to be an array type,
356    # this helps with the communication
357
358    ttriangles = num.zeros((len(new_triangles), 3), num.int)
359    for i in xrange(len(new_triangles)):
360        ttriangles[i] = new_triangles[i]
361
362    #return nodes, ttriangles, boundary, triangles_per_proc, quantities
363   
364    #return nodes, ttriangles, boundary, triangles_per_proc, quantities, tri_index, r_tri_index
365
366    return new_nodes, new_triangles, new_boundary, triangles_per_proc, new_quantities, new_tri_index, epart_order
367
368#########################################################
369#
370# Subdivide the domain. This module is primarily
371# responsible for building the ghost layer and
372# communication pattern
373#
374#
375#  Author: Linda Stals, June 2005
376#  Modified: Linda Stals, Nov 2005 (optimise python code)
377#            Steve Roberts, Aug 2009 (convert to numpy)
378#
379#
380#########################################################
381
382
383
384#########################################################
385#
386# Subdivide the triangles into non-overlapping domains.
387#
388#  *)  The subdivision is controlled by triangles_per_proc.
389# The first triangles_per_proc[0] triangles are assigned
390# to the first processor, the second triangles_per_proc[1]
391# are assigned to the second processor etc.
392#
393#  *) nodes, triangles and boundary contains all of the
394# nodes, triangles and boundary tag information for the
395# whole domain. The triangles should be orientated in the
396# correct way and the nodes number consecutively from 0.
397#
398# -------------------------------------------------------
399#
400#  *) A dictionary containing the full_nodes, full_triangles
401# and full_boundary information for each processor is
402# returned. The node information consists of
403# [global_id, x_coord, y_coord].
404#
405#########################################################
406
407def submesh_full(mesh, triangles_per_proc):
408
409    # Initialise
410
411
412    print triangles_per_proc
413   
414    nodes = mesh.nodes
415    triangles = mesh.triangles
416    boundary = mesh.boundary
417
418    tlower = 0
419    nproc = len(triangles_per_proc)
420    nnodes = len(nodes)
421    node_list = []
422    triangle_list = []
423    boundary_list = []
424    submesh = {}
425
426#    node_range = num.reshape(num.arange(nnodes),(nnodes,1))
427#
428#    #print node_range
429#    tsubnodes = num.concatenate((node_range, nodes), 1)
430
431    #print node_range
432    #print nodes
433    # Loop over processors
434
435    for p in xrange(nproc):
436
437        # Find triangles on processor p
438
439        tupper = triangles_per_proc[p]+tlower
440        subtriangles = triangles[tlower:tupper]
441        triangle_list.append(subtriangles)
442
443        # Find the boundary edges on processor p
444
445        subboundary = {}
446        for k in boundary:
447            if (k[0] >=tlower and k[0] < tupper):
448                subboundary[k]=boundary[k]
449        boundary_list.append(subboundary)
450
451        # Find nodes in processor p
452
453#        nodemap = num.zeros(nnodes, 'i')
454#        for t in subtriangles:
455#            nodemap[t[0]]=1
456#            nodemap[t[1]]=1
457#            nodemap[t[2]]=1
458#
459#        y = tsubnodes.take(num.flatnonzero(nodemap),axis=0)
460
461        #node_list.append(y)
462
463        ids = num.unique(subtriangles.flat)
464
465           
466        lnodes = nodes[ids]
467#       print nodes.shape
468#       print ids.shape
469#       print lnodes.shape
470        x = num.concatenate((num.reshape(ids, (-1,1)),lnodes ), 1)
471#       print x
472#       print y
473        node_list.append(x)
474
475           
476        # Move to the next processor
477
478        tlower = tupper
479
480    # Put the results in a dictionary
481
482    submesh["full_nodes"] = node_list
483    submesh["full_triangles"] = triangle_list
484    submesh["full_boundary"] = boundary_list
485
486    # Clean up before exiting
487
488    #del (nodemap)
489
490    return submesh
491
492
493#########################################################
494#
495# Build the ghost layer of triangles
496#
497#  *) Given the triangle subpartion for the processor
498# build a ghost layer of triangles. The ghost layer
499# consists of two layers of neighbouring triangles.
500#
501#  *) The vertices in the ghost triangles must also
502# be added to the node list for the current processor
503#
504#
505# -------------------------------------------------------
506#
507#  *) The extra triangles and nodes are returned.
508#
509#  *)  The node information consists of
510# [global_id, x_coord, y_coord].
511#
512#  *) The triangle information consists of
513# [triangle number, t], where t = [v1, v2, v3].
514#
515#########################################################
516
517def ghost_layer_old(submesh, mesh, p, tupper, tlower, parameters = None):
518
519    ncoord = mesh.number_of_nodes
520    ntriangles = mesh.number_of_triangles
521
522    if parameters is None:
523        layer_width  = 2
524    else:
525        layer_width = parameters['ghost_layer_width']
526
527
528    trianglemap = num.zeros(ntriangles, num.int)
529
530    # Find the first layer of boundary triangles
531    for t in xrange(tlower, tupper):
532       
533        n = mesh.neighbours[t, 0]
534        if n >= 0:
535            if n < tlower or n >= tupper:
536                trianglemap[n] = 1
537
538        n = mesh.neighbours[t, 1]
539        if n >= 0:
540            if n < tlower or n >= tupper:
541                trianglemap[n] = 1
542
543        n = mesh.neighbours[t, 2]
544        if n >= 0:
545            if n < tlower or n >= tupper:
546                trianglemap[n] = 1
547
548
549    # Find the subsequent layers of ghost triangles
550    for i in range(layer_width-1):
551
552        for t in xrange(ntriangles):
553            if trianglemap[t]==i+1:
554
555                n = mesh.neighbours[t, 0]
556                if n >= 0:
557                    if (n < tlower or n >= tupper) and trianglemap[n] == 0:
558                        trianglemap[n] = i+2
559
560                n = mesh.neighbours[t, 1]
561                if n >= 0:
562                    if (n < tlower or n >= tupper) and trianglemap[n] == 0:
563                        trianglemap[n] = i+2
564
565                n = mesh.neighbours[t, 2]
566                if n >= 0:
567                    if (n < tlower or n >= tupper) and trianglemap[n] == 0:
568                        trianglemap[n] = i+2
569
570    # Build the triangle list and make note of the vertices
571
572
573    nodemap = num.zeros(ncoord, num.int)
574    fullnodes = submesh["full_nodes"][p]
575
576    subtriangles = []
577    for i in xrange(ntriangles):
578        if trianglemap[i] != 0:
579            t = list(mesh.triangles[i])
580            nodemap[t[0]] = 1
581            nodemap[t[1]] = 1
582            nodemap[t[2]] = 1
583
584    trilist = num.reshape(num.arange(ntriangles),(ntriangles,1))
585    tsubtriangles = num.concatenate((trilist, mesh.triangles), 1)
586    subtriangles = tsubtriangles.take(num.flatnonzero(trianglemap),axis=0)
587
588   
589    # Keep a record of the triangle vertices, if they are not already there
590
591    for n in fullnodes:
592        nodemap[int(n[0])] = 0
593
594    nodelist = num.reshape(num.arange(ncoord),(ncoord,1))
595    tsubnodes = num.concatenate((nodelist, mesh.get_nodes()), 1)
596    subnodes = tsubnodes.take(num.flatnonzero(nodemap),axis=0)
597
598    # Clean up before exiting
599
600    del (nodelist)
601    del (trilist)
602    del (tsubnodes)
603    del (nodemap)
604    del (trianglemap)
605
606    # Return the triangles and vertices sitting on the boundary layer
607
608    return subnodes, subtriangles, layer_width
609
610
611
612def ghost_layer(submesh, mesh, p, tupper, tlower, parameters = None):
613
614    ncoord = mesh.number_of_nodes
615    ntriangles = mesh.number_of_triangles
616
617    if parameters is None:
618        layer_width  = 2
619    else:
620        layer_width = parameters['ghost_layer_width']
621
622
623    full_ids = num.arange(tlower, tupper)
624
625    n0 = mesh.neighbours[full_ids, :]
626    n0 = num.unique(n0.flat)
627    n0 = num.extract(n0>=0,n0)
628    n0 = num.extract(num.logical_or(n0<tlower, tupper<= n0), n0)
629
630    layer_cells = {}
631    layer_cells[0] = n0
632
633
634    # Find the subsequent layers of ghost triangles
635    for i in range(layer_width-1):
636
637        # use previous layer as a start
638        n0 = mesh.neighbours[n0, :]
639        n0 = num.unique(n0.flat)
640        n0 = num.extract(n0>=0,n0)
641        n0 = num.extract(num.logical_or(n0<tlower, tupper<= n0), n0)
642
643        for j in xrange(i+1):
644            n0 = numset.setdiff1d(n0,layer_cells[j])
645
646        layer_cells[i+1] = n0
647
648
649    # Build the triangle list and make note of the vertices
650    new_trianglemap = layer_cells[0]
651    for i in range(layer_width-1):
652        new_trianglemap = numset.union1d(new_trianglemap,layer_cells[i+1])
653
654    new_subtriangles = num.concatenate((num.reshape(new_trianglemap, (-1,1)), mesh.triangles[new_trianglemap]), 1)
655
656
657
658
659    fullnodes = submesh["full_nodes"][p]
660    full_nodes_ids = num.array(fullnodes[:,0],num.int)
661
662    new_nodes = num.unique(mesh.triangles[new_trianglemap].flat)
663    new_nodes = numset.setdiff1d(new_nodes,full_nodes_ids)
664
665    new_subnodes = num.concatenate((num.reshape(new_nodes, (-1,1)), mesh.nodes[new_nodes]), 1)
666
667    # Clean up before exiting
668
669    del (new_nodes)
670    del (layer_cells)
671    del (n0)
672    del (new_trianglemap)
673
674    # Return the triangles and vertices sitting on the boundary layer
675
676    return new_subnodes, new_subtriangles, layer_width
677
678#########################################################
679#
680# Find the edges of the ghost trianlges that do not
681# have a neighbour in the current cell. These are
682# treated as a special type of boundary edge.
683#
684#  *) Given the ghost triangles in a particular
685# triangle, use the mesh to find its neigbours. If
686# the neighbour is not in the processor set it to
687# be a boundary edge
688#
689#  *) The vertices in the ghost triangles must also
690# be added to the node list for the current processor
691#
692#  *) The boundary edges for the ghost triangles are
693# ignored.
694#
695# -------------------------------------------------------
696#
697#  *) The type assigned to the ghost boundary edges is 'ghost'
698#
699#  *)  The boundary information is returned as a directorier
700# with the key = (triangle id, edge no) and the values
701# assigned to the key is 'ghost'
702#
703#
704#########################################################
705def is_in_processor(ghost_list, tlower, tupper, n):
706
707    return num.equal(ghost_list,n).any() or (tlower <= n and tupper > n)
708
709
710def ghost_bnd_layer_old(ghosttri, tlower, tupper, mesh, p):
711
712
713    boundary = mesh.boundary
714
715    ghost_list = []
716    subboundary = {}
717
718
719    # FIXME SR: For larger layers need to pass through the correct
720    # boundary tag!
721
722    for t in ghosttri:
723        ghost_list.append(t[0])
724   
725    for t in ghosttri:
726
727        n = mesh.neighbours[t[0], 0]
728        if not is_in_processor(ghost_list, tlower, tupper, n):
729            if boundary.has_key( (t[0], 0) ):
730                subboundary[t[0], 0] = boundary[t[0],0]
731            else:
732                subboundary[t[0], 0] = 'ghost'
733
734
735        n = mesh.neighbours[t[0], 1]
736        if not is_in_processor(ghost_list, tlower, tupper, n):
737            if boundary.has_key( (t[0], 1) ):
738                subboundary[t[0], 1] = boundary[t[0],1]
739            else:
740                subboundary[t[0], 1] = 'ghost'
741
742
743        n = mesh.neighbours[t[0], 2]
744        if not is_in_processor(ghost_list, tlower, tupper, n):
745            if boundary.has_key( (t[0], 2) ):
746                subboundary[t[0], 2] = boundary[t[0],2]
747            else:
748                subboundary[t[0], 2] = 'ghost'
749           
750    return subboundary
751
752
753def ghost_bnd_layer(ghosttri, tlower, tupper, mesh, p):
754
755
756    boundary = mesh.boundary
757
758    ghost_list = []
759    subboundary = {}
760
761
762    #print ghosttri
763
764    # FIXME SR: For larger layers need to pass through the correct
765    # boundary tag!
766
767#    for t in ghosttri:
768#        ghost_list.append(t[0])
769#
770#
771#
772#    for t in ghosttri:
773#
774#        n = mesh.neighbours[t[0], 0]
775#        if not is_in_processor(ghost_list, tlower, tupper, n):
776#            if boundary.has_key( (t[0], 0) ):
777#                subboundary[t[0], 0] = boundary[t[0],0]
778#            else:
779#                subboundary[t[0], 0] = 'ghost'
780#
781#
782#        n = mesh.neighbours[t[0], 1]
783#        if not is_in_processor(ghost_list, tlower, tupper, n):
784#            if boundary.has_key( (t[0], 1) ):
785#                subboundary[t[0], 1] = boundary[t[0],1]
786#            else:
787#                subboundary[t[0], 1] = 'ghost'
788#
789#
790#        n = mesh.neighbours[t[0], 2]
791#        if not is_in_processor(ghost_list, tlower, tupper, n):
792#            if boundary.has_key( (t[0], 2) ):
793#                subboundary[t[0], 2] = boundary[t[0],2]
794#            else:
795#                subboundary[t[0], 2] = 'ghost'
796#
797
798
799
800    new_ghost_list = ghosttri[:,0]
801
802    #print new_ghost_list
803
804    # 0 edge boundaries
805    nghb0 = mesh.neighbours[new_ghost_list,0]
806    gl0 = num.extract(num.logical_or(nghb0 < tlower, nghb0 >= tupper), new_ghost_list)
807    nghb0 = mesh.neighbours[gl0,0]
808    flag = numset.in1d(nghb0,new_ghost_list)
809    gl0 = num.extract(num.logical_not(flag),gl0)
810    edge0 = 0*num.ones_like(gl0)
811    n0 = len(edge0)
812    values0 = ['ghost']*n0
813
814    # 1 edge boundary
815    nghb1 = mesh.neighbours[new_ghost_list,1]
816    gl1 = num.extract(num.logical_or(nghb1 < tlower, nghb1 >= tupper), new_ghost_list)
817    nghb1 = mesh.neighbours[gl1,1]
818    flag = numset.in1d(nghb1,new_ghost_list)
819    gl1 = num.extract(num.logical_not(flag),gl1)
820    edge1 = 1*num.ones_like(gl1)
821    n1 = len(edge1)
822    values1 = ['ghost']*n1
823
824    # 1 edge boundary
825    nghb2 = mesh.neighbours[new_ghost_list,2]
826    gl2 = num.extract(num.logical_or(nghb2 < tlower, nghb2 >= tupper), new_ghost_list)
827    nghb2 = mesh.neighbours[gl2,2]
828    flag = numset.in1d(nghb2,new_ghost_list)
829    gl2 = num.extract(num.logical_not(flag),gl2)
830    edge2 = 2*num.ones_like(gl2)
831    n2 = len(edge2)
832    values2 = ['ghost']*n2
833
834
835    gl = num.concatenate((gl0,gl1,gl2))
836    edge = num.concatenate((edge0,edge1,edge2))
837    values = values0 + values1 + values2
838#    print gl
839#    print edge
840#    print values
841
842    subboundary = dict(zip(zip(gl,edge),values))
843    #intersect with boundary
844
845    # FIXME SR: these keys should be viewkeys but need python 2.7
846    subboundary.update( (k,boundary[k]) for k in set(subboundary.keys()) & set(boundary.keys()) )
847
848    #print subboundary
849
850
851    return subboundary
852
853#########################################################
854#
855# The ghost triangles on the current processor will need
856# to get updated information from the neighbouring
857# processor containing the corresponding full triangles.
858#
859#  *) The tri_per_proc is used to determine which
860# processor contains the full node copy.
861#
862# -------------------------------------------------------
863#
864#  *) The ghost communication pattern consists of
865# [global node number, neighbour processor number].
866#
867#########################################################
868
869def ghost_commun_pattern_old(subtri, p, tri_per_proc):
870
871    # Loop over the ghost triangles
872
873    ghost_commun = num.zeros((len(subtri), 2), num.int)
874
875    for i in xrange(len(subtri)):
876        global_no = subtri[i][0]
877
878        # Find which processor contains the full triangle
879
880        nproc = len(tri_per_proc)
881        neigh = nproc-1
882        sum = 0
883        for q in xrange(nproc-1):
884            if (global_no < sum+tri_per_proc[q]):
885                neigh = q
886                break
887            sum = sum+tri_per_proc[q]
888
889        # Keep a copy of the neighbour processor number
890
891        ghost_commun[i] = [global_no, neigh]
892
893    return ghost_commun
894
895
896
897def ghost_commun_pattern_old_2(subtri, p, tri_per_proc_range):
898
899    # Loop over the ghost triangles
900
901    ghost_commun = num.zeros((len(subtri), 2), num.int)
902
903    #print tri_per_proc_range
904    #print tri_per_proc
905   
906    for i in xrange(len(subtri)):
907        global_no = subtri[i][0]
908
909        # Find which processor contains the full triangle
910        new_neigh = num.searchsorted(tri_per_proc_range, global_no)
911
912        ghost_commun[i] = [global_no, new_neigh]
913
914    return ghost_commun
915
916
917def ghost_commun_pattern(subtri, p, tri_per_proc_range):
918
919
920    global_no = num.reshape(subtri[:,0],(-1,1))
921    neigh = num.reshape(num.searchsorted(tri_per_proc_range, global_no), (-1,1))
922
923    ghost_commun = num.concatenate((global_no, neigh), axis=1)
924
925
926    return ghost_commun
927
928#########################################################
929#
930# The full triangles in this processor must communicate
931# updated information to neighbouring processor that
932# contain ghost triangles
933#
934#  *) The ghost communication pattern for all of the
935# processor must be built before calling this processor.
936#
937#  *) The full communication pattern is found by looping
938# through the ghost communication pattern for all of the
939# processors. Recall that this information is stored in
940# the form [global node number, neighbour processor number].
941# The full communication for the neighbour processor is
942# then updated.
943#
944# -------------------------------------------------------
945#
946#  *) The full communication pattern consists of
947# [global id, [p1, p2, ...]], where p1, p2 etc contain
948# a ghost node copy of the triangle global id.
949#
950#########################################################
951
952def full_commun_pattern(submesh, tri_per_proc):
953    tlower = 0
954    nproc = len(tri_per_proc)
955    full_commun = []
956
957    # Loop over the processor
958
959    for p in xrange(nproc):
960
961        # Loop over the full triangles in the current processor
962        # and build an empty dictionary
963
964        fcommun = {}
965        tupper = tri_per_proc[p]+tlower
966        for i in xrange(tlower, tupper):
967            fcommun[i] = []
968        full_commun.append(fcommun)
969        tlower = tupper
970
971    # Loop over the processor again
972
973    for p in xrange(nproc):
974
975        # Loop over the ghost triangles in the current processor,
976        # find which processor contains the corresponding full copy
977        # and note that the processor must send updates to this
978        # processor
979
980        for g in submesh["ghost_commun"][p]:
981            neigh = g[1]
982            full_commun[neigh][g[0]].append(p)
983
984    return full_commun
985
986
987#########################################################
988#
989# Given the non-overlapping grid partition, an extra layer
990# of triangles are included to help with the computations.
991# The triangles in this extra layer are not updated by
992# the processor, their updated values must be sent by the
993# processor containing the original, full, copy of the
994# triangle. The communication pattern that controls these
995# updates must also be built.
996#
997#  *) Assumes that full triangles, nodes etc have already
998# been found and stored in submesh
999#
1000#  *) See the documentation for ghost_layer,
1001# ghost_commun_pattern and full_commun_pattern
1002#
1003# -------------------------------------------------------
1004#
1005#  *) The additional information is added to the submesh
1006# dictionary. See the documentation for ghost_layer,
1007# ghost_commun_pattern and full_commun_pattern
1008#
1009#  *) The ghost_triangles, ghost_nodes, ghost_boundary,
1010# ghost_commun and full_commun is added to submesh
1011#########################################################
1012
1013def submesh_ghost(submesh, mesh, triangles_per_proc, parameters = None):
1014
1015    nproc = len(triangles_per_proc)
1016    tlower = 0
1017    ghost_triangles = []
1018    ghost_nodes = []
1019    ghost_commun = []
1020    ghost_bnd = []
1021    ghost_layer_width = []
1022
1023    # Loop over the processors
1024    triangles_per_proc_ranges = num.cumsum(triangles_per_proc) - 1
1025   
1026    for p in xrange(nproc):
1027
1028        # Find the full triangles in this processor
1029
1030        tupper = triangles_per_proc[p]+tlower
1031
1032        # Build the ghost boundary layer
1033
1034        [subnodes, subtri, layer_width] = \
1035                   ghost_layer(submesh, mesh, p, tupper, tlower, parameters)
1036        ghost_layer_width.append(layer_width)
1037        ghost_triangles.append(subtri)
1038        ghost_nodes.append(subnodes)
1039
1040
1041        # Find the new boundary formed by the ghost triangles
1042       
1043        subbnd = ghost_bnd_layer(subtri, tlower, tupper, mesh, p)
1044        ghost_bnd.append(subbnd)
1045       
1046        # Build the communication pattern for the ghost nodes
1047        gcommun = \
1048                ghost_commun_pattern(subtri, p, triangles_per_proc_ranges)
1049        ghost_commun.append(gcommun)
1050
1051        # Move to the next processor
1052
1053        tlower = tupper
1054
1055
1056    # Record the ghost layer and communication pattern
1057    submesh["ghost_layer_width"] = ghost_layer_width
1058    submesh["ghost_nodes"] = ghost_nodes
1059    submesh["ghost_triangles"] = ghost_triangles
1060    submesh["ghost_commun"] = ghost_commun
1061    submesh["ghost_boundary"] = ghost_bnd
1062   
1063    # Build the communication pattern for the full triangles
1064
1065    full_commun = full_commun_pattern(submesh, triangles_per_proc)
1066    submesh["full_commun"] = full_commun
1067
1068    # Return the submesh
1069
1070    return submesh
1071
1072
1073#########################################################
1074#
1075# Certain quantities may be assigned to the triangles,
1076# these quantities must be subdivided in the same way
1077# as the triangles
1078#
1079#  *) The quantities are ordered in the same way as the
1080# triangles
1081#
1082# -------------------------------------------------------
1083#
1084#  *) The quantites attached to the full triangles are
1085# stored in full_quan
1086#
1087#  *) The quantities attached to the ghost triangles are
1088# stored in ghost_quan
1089#########################################################
1090
1091def submesh_quantities(submesh, quantities, triangles_per_proc):
1092
1093    nproc = len(triangles_per_proc)
1094
1095    lower = 0
1096
1097    # Build an empty dictionary to hold the quantites
1098
1099    submesh["full_quan"] = {}
1100    submesh["ghost_quan"] = {}
1101    for k in quantities:
1102        submesh["full_quan"][k] = []
1103        submesh["ghost_quan"][k] = []
1104
1105    # Loop trough the subdomains
1106
1107    for p in xrange(nproc):
1108        upper =   lower+triangles_per_proc[p]
1109
1110        # Find the global ID of the ghost triangles
1111
1112        global_id = []
1113        M = len(submesh["ghost_triangles"][p])
1114        for j in xrange(M):
1115            global_id.append(submesh["ghost_triangles"][p][j][0])
1116
1117        # Use the global ID to extract the quantites information from
1118        # the full domain
1119
1120        for k in quantities:
1121            submesh["full_quan"][k].append(quantities[k][lower:upper])
1122            submesh["ghost_quan"][k].append(num.zeros( (M,3) , num.float))
1123            for j in range(M):
1124                submesh["ghost_quan"][k][p][j] = \
1125                                               quantities[k][global_id[j]]
1126
1127        lower = upper
1128
1129    return submesh
1130
1131#########################################################
1132#
1133# Build the grid partition on the host.
1134#
1135#  *) See the documentation for submesh_ghost and
1136# submesh_full
1137#
1138# -------------------------------------------------------
1139#
1140#  *) A dictionary containing the full_triangles,
1141# full_nodes, full_boundary, ghost_triangles, ghost_nodes,
1142# ghost_boundary, ghost_commun and full_commun and true boundary polygon is returned.
1143#
1144#########################################################
1145
1146def build_submesh(nodes, triangles, boundary, quantities,
1147                  triangles_per_proc, parameters = None):
1148
1149    # Temporarily build the mesh to find the neighbouring
1150    # triangles and true boundary polygon\
1151
1152    mesh = Mesh(nodes, triangles, boundary)
1153    boundary_polygon = mesh.get_boundary_polygon()
1154   
1155
1156    # Subdivide into non-overlapping partitions
1157
1158    submeshf = submesh_full(mesh, triangles_per_proc)
1159   
1160    # Add any extra ghost boundary layer information
1161
1162    submeshg = submesh_ghost(submeshf, mesh, triangles_per_proc, parameters)
1163
1164    # Order the quantities information to be the same as the triangle
1165    # information
1166
1167    submesh = submesh_quantities(submeshg, quantities, \
1168                                 triangles_per_proc)
1169
1170    submesh["boundary_polygon"] = boundary_polygon
1171    return submesh
1172
1173#########################################################
1174#
1175#  Given the subdivision of the grid assigned to the
1176# current processor convert it into a form that is
1177# appropriate for the GA datastructure.
1178#
1179#  The main function of these modules is to change the
1180# node numbering. The GA datastructure assumes they
1181# are numbered consecutively from 0.
1182#
1183#  The module also changes the communication pattern
1184# datastructure into a form needed by parallel_advection
1185#
1186#  Authors: Linda Stals and Matthew Hardy, June 2005
1187#  Modified: Linda Stals, Nov 2005 (optimise python code)
1188#            Steve Roberts, Aug 2009 (updating to numpy)
1189#
1190#
1191#########################################################
1192
1193 
1194#########################################################
1195# Convert the format of the data to that used by ANUGA
1196#
1197#
1198# *) Change the nodes global ID's to an integer value,
1199#starting from 0.
1200#
1201# *) The triangles and boundary edges must also be
1202# updated accordingly.
1203#
1204# -------------------------------------------------------
1205#
1206# *) The nodes, triangles and boundary edges defined by
1207# the new numbering scheme are returned
1208#
1209#########################################################
1210
1211def build_local_GA(nodes, triangles, boundaries, tri_map):
1212
1213    Nnodes =len(nodes)
1214    Ntriangles = len(triangles)
1215   
1216    # Extract the nodes (using the local ID)
1217   
1218    GAnodes = num.take(nodes, (1, 2), 1)
1219
1220    # Build a global ID to local ID mapping
1221
1222    NGlobal = 0
1223    for i in xrange(Nnodes):
1224        if nodes[i][0] > NGlobal:
1225            NGlobal = nodes[i][0]
1226
1227    node_map = -1*num.ones(int(NGlobal)+1, num.int)
1228
1229    num.put(node_map, num.take(nodes, (0,), 1).astype(num.int), \
1230        num.arange(Nnodes))
1231       
1232    # Change the global IDs in the triangles to the local IDs
1233
1234    GAtriangles = num.zeros((Ntriangles, 3), num.int)
1235    GAtriangles[:,0] = num.take(node_map, triangles[:,0])
1236    GAtriangles[:,1] = num.take(node_map, triangles[:,1])
1237    GAtriangles[:,2] = num.take(node_map, triangles[:,2])
1238
1239    # Change the triangle numbering in the boundaries
1240
1241    GAboundaries = {}
1242    for b in boundaries:
1243        GAboundaries[tri_map[b[0]], b[1]] = boundaries[b]
1244       
1245   
1246    return GAnodes, GAtriangles, GAboundaries, node_map
1247
1248
1249#########################################################
1250# Change the communication format to that needed by the
1251# parallel advection file.
1252#
1253# *) The index contains [global triangle no,
1254# local triangle no.]
1255#
1256# -------------------------------------------------------
1257#
1258# *) The ghost_recv and full_send dictionaries are
1259# returned.
1260#
1261# *) ghost_recv dictionary is local id, global id, value
1262#
1263# *) full_recv dictionary is local id, global id, value
1264#
1265# *) The information is ordered by the global id. This
1266# means that the communication order is predetermined and
1267# local and global id do not need to be
1268# compared when the information is sent/received.
1269#
1270#########################################################
1271
1272def build_local_commun(tri_map, ghostc, fullc, nproc):
1273
1274    # Initialise
1275
1276    full_send = {}
1277    ghost_recv = {}
1278
1279    # Build the ghost_recv dictionary (sort the
1280    # information by the global numbering)
1281   
1282    ghostc = num.sort(ghostc, 0)
1283   
1284    for c in xrange(nproc):
1285        s = ghostc[:,0]
1286        d = num.compress(num.equal(ghostc[:,1],c), s)
1287        if len(d) > 0:
1288            ghost_recv[c] = [0, 0]
1289            ghost_recv[c][1] = d
1290            ghost_recv[c][0] = num.take(tri_map, d)
1291           
1292    # Build a temporary copy of the full_send dictionary
1293    # (this version allows the information to be stored
1294    # by the global numbering)
1295
1296    tmp_send = {}
1297    for global_id in fullc:
1298        for i in xrange(len(fullc[global_id])):
1299            neigh = fullc[global_id][i]
1300            if not tmp_send.has_key(neigh):
1301                tmp_send[neigh] = []
1302            tmp_send[neigh].append([global_id, \
1303                                    tri_map[global_id]])
1304
1305    # Extract the full send information and put it in the form
1306    # required for the full_send dictionary
1307
1308    for neigh in tmp_send:
1309        neigh_commun = num.sort(tmp_send[neigh], 0)
1310        full_send[neigh] = [0, 0]
1311        full_send[neigh][0] = neigh_commun[:,1]
1312        full_send[neigh][1] = neigh_commun[:,0]
1313
1314    return ghost_recv, full_send
1315
1316
1317#########################################################
1318# Convert the format of the data to that used by ANUGA
1319#
1320#
1321# *) Change the nodes global ID's to an integer value,
1322# starting from 0. The node numbering in the triangles
1323# must also be updated to take this into account.
1324#
1325# *) The triangle number will also change, which affects
1326# the boundary tag information and the communication
1327# pattern.
1328#
1329# -------------------------------------------------------
1330#
1331# *) The nodes, triangles, boundary edges and communication
1332# pattern defined by the new numbering scheme are returned
1333#
1334#########################################################
1335
1336def build_local_mesh(submesh, lower_t, upper_t, nproc):
1337
1338    # Combine the full nodes and ghost nodes
1339
1340    nodes = num.concatenate((submesh["full_nodes"], \
1341                         submesh["ghost_nodes"]))
1342
1343    ghost_layer_width = submesh["ghost_layer_width"]
1344   
1345    # Combine the full triangles and ghost triangles
1346
1347    gtri =  num.take(submesh["ghost_triangles"],(1, 2, 3),1)
1348    triangles = num.concatenate((submesh["full_triangles"], gtri))
1349
1350    # Combine the full boundaries and ghost boundaries
1351
1352    boundaries = submesh["full_boundary"]
1353    for b in submesh["ghost_boundary"]:
1354        boundaries[b]=submesh["ghost_boundary"][b]
1355
1356    # Make note of the new triangle numbers, including the ghost
1357    # triangles
1358
1359    NGlobal = upper_t
1360    for i in xrange(len(submesh["ghost_triangles"])):
1361        id = submesh["ghost_triangles"][i][0]
1362        if id > NGlobal:
1363            NGlobal = id
1364    #index = num.zeros(int(NGlobal)+1, num.int)
1365    tri_map = -1*num.ones(int(NGlobal)+1, num.int)
1366    tri_map[lower_t:upper_t]=num.arange(upper_t-lower_t)
1367    for i in xrange(len(submesh["ghost_triangles"])):
1368        tri_map[submesh["ghost_triangles"][i][0]] = i+upper_t-lower_t
1369   
1370    # Change the node numbering (and update the numbering in the
1371    # triangles)
1372
1373    [GAnodes, GAtriangles, GAboundary, node_map] = \
1374    build_local_GA(nodes, triangles, boundaries, tri_map)
1375
1376    # Extract the local quantities
1377   
1378    quantities ={}
1379    for k in submesh["full_quan"]:
1380        Nf = len(submesh["full_quan"][k])
1381        Ng = len(submesh["ghost_quan"][k])
1382        quantities[k] = num.zeros((Nf+Ng, 3), num.float)
1383        quantities[k][0:Nf] = submesh["full_quan"][k] 
1384        quantities[k][Nf:Nf+Ng] = submesh["ghost_quan"][k]
1385                             
1386    # Change the communication pattern into a form needed by
1387    # the parallel_adv
1388
1389    gcommun = submesh["ghost_commun"]
1390    fcommun = submesh["full_commun"]
1391    [ghost_rec, full_send] = \
1392                build_local_commun(tri_map, gcommun, fcommun, nproc)
1393
1394
1395    return GAnodes, GAtriangles, GAboundary, quantities, ghost_rec, \
1396           full_send, tri_map, node_map, ghost_layer_width
1397
1398
1399#########################################################
1400#
1401# Handle the communication between the host machine
1402# (processor 0) and the processors. The host machine is
1403# responsible for the doing the initial grid partitioning.
1404#
1405# The routines given below should be moved to the
1406# build_submesh.py and build_local.py file to allow
1407# overlapping of  communication and computation.
1408# This should be done after more debugging.
1409#
1410#
1411#  Author: Linda Stals, June 2005
1412#  Modified: Linda Stals, Nov 2005 (optimise python code)
1413#            Steve Roberts, Aug 2009 (update to numpy)
1414#
1415#
1416#########################################################
1417
1418
1419#########################################################
1420#
1421# Send the submesh to processor p.
1422#
1423# *) The order and form is strongly coupled with
1424# rec_submesh.
1425#
1426# -------------------------------------------------------
1427#
1428# *) All of the information has been sent to processor p.
1429#
1430#########################################################
1431
1432def send_submesh(submesh, triangles_per_proc, p, verbose=True):
1433
1434    import pypar
1435   
1436    myid = pypar.rank()
1437    nprocs = pypar.size()
1438   
1439    if verbose: print 'P%d: Sending submesh to P%d' %(myid, p)
1440   
1441    # build and send the tagmap for the boundary conditions
1442   
1443    tagmap = {}
1444    counter = 1
1445    for b in submesh["full_boundary"][p]:
1446         bkey = submesh["full_boundary"][p][b]
1447         if not tagmap.has_key(bkey):
1448             tagmap[bkey] = counter
1449             counter = counter+1
1450    for b in submesh["ghost_boundary"][p]:
1451         bkey = submesh["ghost_boundary"][p][b]
1452         if not tagmap.has_key(bkey):
1453             tagmap[bkey] = counter
1454             counter = counter+1
1455
1456
1457    # send boundary tags
1458    pypar.send(tagmap, p)
1459
1460    # send the quantities key information
1461    pypar.send(submesh["full_quan"].keys(), p)
1462
1463    # compress full_commun
1464    flat_full_commun = []
1465
1466    for c in submesh["full_commun"][p]:
1467        for i in range(len(submesh["full_commun"][p][c])):
1468            flat_full_commun.append([c,submesh["full_commun"][p][c][i]])
1469
1470    # send the array sizes so memory can be allocated
1471
1472    setup_array = num.zeros((9,),num.int)
1473    setup_array[0] = len(submesh["full_nodes"][p])
1474    setup_array[1] = len(submesh["ghost_nodes"][p])
1475    setup_array[2] = len(submesh["full_triangles"][p])
1476    setup_array[3] = len(submesh["ghost_triangles"][p])
1477    setup_array[4] = len(submesh["full_boundary"][p])
1478    setup_array[5] = len(submesh["ghost_boundary"][p])
1479    setup_array[6] = len(submesh["ghost_commun"][p])
1480    setup_array[7] = len(flat_full_commun)
1481    setup_array[8] = len(submesh["full_quan"])
1482
1483    x = num.array(setup_array, num.int)
1484    pypar.send(x, p, bypass=True)
1485
1486
1487    # ghost layer width
1488    x = num.array(submesh["ghost_layer_width"][p], num.int)
1489    pypar.send(x, p, bypass=True)
1490
1491
1492    # send the number of triangles per processor
1493    x = num.array(triangles_per_proc)
1494    pypar.send(x, p, bypass=True)
1495
1496    # send the nodes
1497    x = num.array(submesh["full_nodes"][p], num.float)
1498    pypar.send(x, p, bypass=True)
1499
1500    x = num.array(submesh["ghost_nodes"][p], num.float)
1501    pypar.send(x, p, bypass=True)
1502
1503    # send the triangles
1504    x = num.array(submesh["full_triangles"][p], num.int)
1505    pypar.send(x, p, bypass=True)
1506
1507    # send ghost triangles
1508    x = num.array(submesh["ghost_triangles"][p], num.int)
1509    pypar.send(x, p, bypass=True)
1510
1511
1512    # send the boundary
1513    bc = []
1514    for b in submesh["full_boundary"][p]:
1515        bc.append([b[0], b[1], tagmap[submesh["full_boundary"][p][b]]])
1516
1517    x = num.array(bc, num.int)
1518    pypar.send(x, p, bypass=True)
1519
1520
1521    bc = []
1522    for b in submesh["ghost_boundary"][p]:
1523        bc.append([b[0], b[1], tagmap[submesh["ghost_boundary"][p][b]]])
1524
1525    x = num.array(bc, num.int)
1526    pypar.send(x, p, bypass=True)
1527
1528
1529
1530    # send the communication pattern
1531    x = submesh["ghost_commun"][p]
1532    pypar.send(x, p, bypass=True)
1533
1534
1535    x = num.array(flat_full_commun, num.int)
1536    pypar.send(x, p, bypass=True)
1537
1538
1539    # send the quantities
1540    for k in submesh["full_quan"]:
1541        x = num.array(submesh["full_quan"][k][p], num.float)
1542        pypar.send(x, p, bypass=True)
1543       
1544    for k in submesh["ghost_quan"]:
1545        x = num.array(submesh["ghost_quan"][k][p], num.float)
1546        pypar.send(x,p, bypass=True)
1547
1548
1549
1550#########################################################
1551#
1552# Receive the submesh from processor p.
1553#
1554# *) The order and form is strongly coupled with
1555# send_submesh.
1556#
1557# -------------------------------------------------------
1558#
1559# *) All of the information has been received by the
1560# processor p and passed into build_local.
1561#
1562# *) The information is returned in a form needed by the
1563# GA datastructure.
1564#
1565#########################################################
1566
1567def rec_submesh_flat(p, verbose=True):
1568
1569    import pypar
1570   
1571    numprocs = pypar.size()
1572    myid = pypar.rank()
1573
1574    submesh_cell = {}
1575   
1576    if verbose: print indent+'P%d: Receiving submesh from P%d' %(myid, p)
1577
1578    # receive the tagmap for the boundary conditions
1579   
1580    tagmap = pypar.receive(p)
1581
1582    itagmap = {}
1583    for t in tagmap:
1584        itagmap[tagmap[t]]=t
1585
1586    # receive the quantities key information
1587    qkeys = pypar.receive(p)
1588
1589    # recieve information about the array sizes
1590    x = num.zeros((9,),num.int)
1591    pypar.receive(p, buffer=x,  bypass=True)
1592    setup_array = x
1593
1594    no_full_nodes      = setup_array[0]
1595    no_ghost_nodes     = setup_array[1]
1596    no_full_triangles  = setup_array[2]
1597    no_ghost_triangles = setup_array[3]
1598    no_full_boundary   = setup_array[4]
1599    no_ghost_boundary  = setup_array[5]
1600    no_ghost_commun    = setup_array[6]
1601    no_full_commun     = setup_array[7]
1602    no_quantities      = setup_array[8]
1603
1604   
1605    # ghost layer width
1606    x = num.zeros((1,),num.int)
1607    pypar.receive(p, buffer=x,  bypass=True)
1608    submesh_cell["ghost_layer_width"] = x[0]
1609
1610
1611    # receive the number of triangles per processor
1612    x = num.zeros((numprocs,),num.int)
1613    pypar.receive(p, buffer=x,  bypass=True)
1614    triangles_per_proc = x
1615
1616    # receive the full nodes
1617    x = num.zeros((no_full_nodes,3),num.float)
1618    pypar.receive(p, buffer=x,  bypass=True)
1619    submesh_cell["full_nodes"] = x
1620
1621
1622    # receive the ghost nodes
1623    x = num.zeros((no_ghost_nodes,3),num.float)
1624    pypar.receive(p, buffer=x,  bypass=True)
1625    submesh_cell["ghost_nodes"] = x
1626   
1627    # receive the full triangles
1628    x = num.zeros((no_full_triangles,3),num.int)
1629    pypar.receive(p, buffer=x,  bypass=True)
1630    submesh_cell["full_triangles"] = x
1631
1632   
1633    # receive the ghost triangles
1634    x = num.zeros((no_ghost_triangles,4),num.int)
1635    pypar.receive(p, buffer=x,  bypass=True)
1636    submesh_cell["ghost_triangles"] = x
1637
1638
1639
1640    # receive the full boundary
1641    x = num.zeros((no_full_boundary,3),num.int)
1642    pypar.receive(p, buffer=x,  bypass=True)
1643    bnd_c = x
1644
1645    submesh_cell["full_boundary"] = {}
1646    for b in bnd_c:
1647        submesh_cell["full_boundary"][b[0],b[1]]=itagmap[b[2]]
1648
1649
1650    # receive the ghost boundary
1651    x = num.zeros((no_ghost_boundary,3),num.int)
1652    pypar.receive(p, buffer=x,  bypass=True)
1653    bnd_c = x
1654
1655    submesh_cell["ghost_boundary"] = {}
1656    for b in bnd_c:
1657        submesh_cell["ghost_boundary"][b[0],b[1]]=itagmap[b[2]]
1658
1659    # receive the ghost communication pattern
1660    x = num.zeros((no_ghost_commun,2),num.int)
1661    pypar.receive(p, buffer=x,  bypass=True)
1662    submesh_cell["ghost_commun"] = x
1663   
1664    # receive the full communication pattern
1665    x = num.zeros((no_full_commun,2),num.int)
1666    pypar.receive(p, buffer=x,  bypass=True)
1667    full_commun = x
1668
1669    submesh_cell["full_commun"] = {}
1670    for c in full_commun:
1671        submesh_cell["full_commun"][c[0]] = []
1672    for c in full_commun:
1673        submesh_cell["full_commun"][c[0]].append(c[1])
1674
1675    # receive the quantities
1676
1677    submesh_cell["full_quan"]={}
1678    for i in range(no_quantities):
1679        x = num.zeros((no_full_triangles,3), num.float)
1680        pypar.receive(p, buffer=x, bypass=True)
1681        submesh_cell["full_quan"][qkeys[i]]= x
1682
1683    submesh_cell["ghost_quan"]={}
1684    for i in range(no_quantities):
1685        x = num.zeros((no_ghost_triangles,3), num.float)
1686        pypar.receive(p, buffer=x, bypass=True)
1687        submesh_cell["ghost_quan"][qkeys[i]]= x
1688   
1689    return submesh_cell, triangles_per_proc,\
1690           no_full_nodes, no_full_triangles
1691
1692
1693
1694#########################################################
1695#
1696# Receive the submesh from processor p.
1697#
1698# *) The order and form is strongly coupled with
1699# send_submesh.
1700#
1701# -------------------------------------------------------
1702#
1703# *) All of the information has been received by the
1704# processor p and passed into build_local.
1705#
1706# *) The information is returned in a form needed by the
1707# GA datastructure.
1708#
1709#########################################################
1710
1711def rec_submesh(p, verbose=True):
1712
1713    import pypar
1714   
1715    numproc = pypar.size()
1716    myid = pypar.rank()
1717
1718    [submesh_cell, triangles_per_proc,\
1719     number_of_full_nodes, number_of_full_triangles] = rec_submesh_flat(p,verbose)
1720   
1721    # find the full triangles assigned to this processor
1722
1723    lower_t = 0
1724    for i in range(myid):
1725        lower_t = lower_t+triangles_per_proc[i]
1726    upper_t = lower_t+triangles_per_proc[myid]
1727
1728    # convert the information into a form needed by the GA
1729    # datastructure
1730
1731    [GAnodes, GAtriangles, boundary, quantities, \
1732     ghost_rec, full_send, tri_map, node_map, ghost_layer_width] = \
1733              build_local_mesh(submesh_cell, lower_t, upper_t, \
1734                               numproc)
1735   
1736    return GAnodes, GAtriangles, boundary, quantities,\
1737           ghost_rec, full_send,\
1738           number_of_full_nodes, number_of_full_triangles, tri_map, node_map,\
1739           ghost_layer_width
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753#########################################################
1754#
1755# Extract the submesh that will belong to the
1756# processor 0 (i.e. processor zero)
1757#
1758#  *) See the documentation for build_submesh
1759#
1760# -------------------------------------------------------
1761#
1762#  *) A dictionary containing the full_triangles,
1763# full_nodes, full_boundary, ghost_triangles, ghost_nodes,
1764# ghost_boundary, ghost_commun and full_commun belonging
1765# to processor zero are returned.
1766#
1767#########################################################
1768def extract_submesh(submesh, triangles_per_proc, p=0):
1769
1770   
1771    submesh_cell = {}
1772    submesh_cell["ghost_layer_width"] = submesh["ghost_layer_width"][p]
1773    submesh_cell["full_nodes"] = submesh["full_nodes"][p]
1774    submesh_cell["ghost_nodes"] = submesh["ghost_nodes"][p]
1775    submesh_cell["full_triangles"] = submesh["full_triangles"][p]
1776    submesh_cell["ghost_triangles"] = submesh["ghost_triangles"][p]
1777    submesh_cell["full_boundary"] = submesh["full_boundary"][p]
1778    submesh_cell["ghost_boundary"] = submesh["ghost_boundary"][p]
1779    submesh_cell["ghost_commun"] = submesh["ghost_commun"][p]
1780    submesh_cell["full_commun"] = submesh["full_commun"][p]
1781    submesh_cell["full_quan"] ={}
1782    submesh_cell["ghost_quan"]={}
1783    for k in submesh["full_quan"]:
1784        submesh_cell["full_quan"][k] = submesh["full_quan"][k][p]
1785        submesh_cell["ghost_quan"][k] = submesh["ghost_quan"][k][p]
1786
1787
1788    # FIXME SR: I think there is already a structure with this info in the mesh
1789    lower_t = 0
1790    for i in range(p):
1791        lower_t = lower_t+triangles_per_proc[i]
1792    upper_t = lower_t+triangles_per_proc[p]
1793
1794    numprocs = len(triangles_per_proc)
1795    points, vertices, boundary, quantities, ghost_recv_dict, \
1796            full_send_dict, tri_map, node_map, ghost_layer_width = \
1797            build_local_mesh(submesh_cell, lower_t, upper_t, numprocs)
1798
1799
1800    return  points, vertices, boundary, quantities, ghost_recv_dict, \
1801           full_send_dict, tri_map, node_map, ghost_layer_width
1802           
1803
1804
1805
1806
Note: See TracBrowser for help on using the repository browser.