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

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

Getting rid of a few print statements

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