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

Last change on this file since 9126 was 9126, checked in by steve, 11 years ago

Small change to run_sequential codes

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