source: trunk/anuga_core/source/anuga/parallel/distribute_mesh.py @ 9516

Last change on this file since 9516 was 9500, checked in by steve, 10 years ago

setup up to move anuga_parllel to anuga.parallel

File size: 47.7 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 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_parallel\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    new_ghost_list = ghosttri[:,0]
648
649    #print new_ghost_list
650
651    # 0 edge boundaries
652    nghb0 = mesh.neighbours[new_ghost_list,0]
653    gl0 = num.extract(num.logical_or(nghb0 < tlower, nghb0 >= tupper), new_ghost_list)
654    nghb0 = mesh.neighbours[gl0,0]
655    flag = numset.in1d(nghb0,new_ghost_list)
656    gl0 = num.extract(num.logical_not(flag),gl0)
657    edge0 = 0*num.ones_like(gl0)
658    n0 = len(edge0)
659    values0 = ['ghost']*n0
660
661    # 1 edge boundary
662    nghb1 = mesh.neighbours[new_ghost_list,1]
663    gl1 = num.extract(num.logical_or(nghb1 < tlower, nghb1 >= tupper), new_ghost_list)
664    nghb1 = mesh.neighbours[gl1,1]
665    flag = numset.in1d(nghb1,new_ghost_list)
666    gl1 = num.extract(num.logical_not(flag),gl1)
667    edge1 = 1*num.ones_like(gl1)
668    n1 = len(edge1)
669    values1 = ['ghost']*n1
670
671    # 2 edge boundary
672    nghb2 = mesh.neighbours[new_ghost_list,2]
673    gl2 = num.extract(num.logical_or(nghb2 < tlower, nghb2 >= tupper), new_ghost_list)
674    nghb2 = mesh.neighbours[gl2,2]
675    flag = numset.in1d(nghb2,new_ghost_list)
676    gl2 = num.extract(num.logical_not(flag),gl2)
677    edge2 = 2*num.ones_like(gl2)
678    n2 = len(edge2)
679    values2 = ['ghost']*n2
680
681
682    gl = num.concatenate((gl0,gl1,gl2))
683    edge = num.concatenate((edge0,edge1,edge2))
684    values = values0 + values1 + values2
685#    print gl
686#    print edge
687#    print values
688
689    subboundary = dict(zip(zip(gl,edge),values))
690    #intersect with boundary
691
692    # FIXME SR: these keys should be viewkeys but need python 2.7
693    subboundary.update( (k,boundary[k]) for k in set(subboundary.keys()) & set(boundary.keys()) )
694
695    #print subboundary
696
697
698    return subboundary
699
700#########################################################
701#
702# The ghost triangles on the current processor will need
703# to get updated information from the neighbouring
704# processor containing the corresponding full triangles.
705#
706#  *) The tri_per_proc is used to determine which
707# processor contains the full node copy.
708#
709# -------------------------------------------------------
710#
711#  *) The ghost communication pattern consists of
712# [global node number, neighbour processor number].
713#
714#########################################################
715
716def ghost_commun_pattern_old(subtri, p, tri_per_proc):
717
718    # Loop over the ghost triangles
719
720    ghost_commun = num.zeros((len(subtri), 2), num.int)
721
722    for i in xrange(len(subtri)):
723        global_no = subtri[i][0]
724
725        # Find which processor contains the full triangle
726
727        nproc = len(tri_per_proc)
728        neigh = nproc-1
729        sum = 0
730        for q in xrange(nproc-1):
731            if (global_no < sum+tri_per_proc[q]):
732                neigh = q
733                break
734            sum = sum+tri_per_proc[q]
735
736        # Keep a copy of the neighbour processor number
737
738        ghost_commun[i] = [global_no, neigh]
739
740    return ghost_commun
741
742
743
744def ghost_commun_pattern_old_2(subtri, p, tri_per_proc_range):
745
746    # Loop over the ghost triangles
747
748    ghost_commun = num.zeros((len(subtri), 2), num.int)
749
750    #print tri_per_proc_range
751    #print tri_per_proc
752   
753    for i in xrange(len(subtri)):
754        global_no = subtri[i][0]
755
756        # Find which processor contains the full triangle
757        new_neigh = num.searchsorted(tri_per_proc_range, global_no)
758
759        ghost_commun[i] = [global_no, new_neigh]
760
761    return ghost_commun
762
763
764def ghost_commun_pattern(subtri, p, tri_per_proc_range):
765
766
767    global_no = num.reshape(subtri[:,0],(-1,1))
768    neigh = num.reshape(num.searchsorted(tri_per_proc_range, global_no), (-1,1))
769
770    ghost_commun = num.concatenate((global_no, neigh), axis=1)
771
772
773    return ghost_commun
774
775#########################################################
776#
777# The full triangles in this processor must communicate
778# updated information to neighbouring processor that
779# contain ghost triangles
780#
781#  *) The ghost communication pattern for all of the
782# processor must be built before calling this processor.
783#
784#  *) The full communication pattern is found by looping
785# through the ghost communication pattern for all of the
786# processors. Recall that this information is stored in
787# the form [global node number, neighbour processor number].
788# The full communication for the neighbour processor is
789# then updated.
790#
791# -------------------------------------------------------
792#
793#  *) The full communication pattern consists of
794# [global id, [p1, p2, ...]], where p1, p2 etc contain
795# a ghost node copy of the triangle global id.
796#
797#########################################################
798
799def full_commun_pattern(submesh, tri_per_proc):
800    tlower = 0
801    nproc = len(tri_per_proc)
802    full_commun = []
803
804    # Loop over the processor
805
806    for p in xrange(nproc):
807
808        # Loop over the full triangles in the current processor
809        # and build an empty dictionary
810
811        fcommun = {}
812        tupper = tri_per_proc[p]+tlower
813        for i in xrange(tlower, tupper):
814            fcommun[i] = []
815        full_commun.append(fcommun)
816        tlower = tupper
817
818    # Loop over the processor again
819
820    for p in xrange(nproc):
821
822        # Loop over the ghost triangles in the current processor,
823        # find which processor contains the corresponding full copy
824        # and note that the processor must send updates to this
825        # processor
826
827        for g in submesh["ghost_commun"][p]:
828            neigh = g[1]
829            full_commun[neigh][g[0]].append(p)
830
831    return full_commun
832
833
834#########################################################
835#
836# Given the non-overlapping grid partition, an extra layer
837# of triangles are included to help with the computations.
838# The triangles in this extra layer are not updated by
839# the processor, their updated values must be sent by the
840# processor containing the original, full, copy of the
841# triangle. The communication pattern that controls these
842# updates must also be built.
843#
844#  *) Assumes that full triangles, nodes etc have already
845# been found and stored in submesh
846#
847#  *) See the documentation for ghost_layer,
848# ghost_commun_pattern and full_commun_pattern
849#
850# -------------------------------------------------------
851#
852#  *) The additional information is added to the submesh
853# dictionary. See the documentation for ghost_layer,
854# ghost_commun_pattern and full_commun_pattern
855#
856#  *) The ghost_triangles, ghost_nodes, ghost_boundary,
857# ghost_commun and full_commun is added to submesh
858#########################################################
859
860def submesh_ghost(submesh, mesh, triangles_per_proc, parameters = None):
861
862    nproc = len(triangles_per_proc)
863    tlower = 0
864    ghost_triangles = []
865    ghost_nodes = []
866    ghost_commun = []
867    ghost_bnd = []
868    ghost_layer_width = []
869
870    # Loop over the processors
871    triangles_per_proc_ranges = num.cumsum(triangles_per_proc) - 1
872   
873    for p in xrange(nproc):
874
875        # Find the full triangles in this processor
876
877        tupper = triangles_per_proc[p]+tlower
878
879        # Build the ghost boundary layer
880
881        [subnodes, subtri, layer_width] = \
882                   ghost_layer(submesh, mesh, p, tupper, tlower, parameters)
883        ghost_layer_width.append(layer_width)
884        ghost_triangles.append(subtri)
885        ghost_nodes.append(subnodes)
886
887
888        # Find the new boundary formed by the ghost triangles
889       
890        subbnd = ghost_bnd_layer(subtri, tlower, tupper, mesh, p)
891        ghost_bnd.append(subbnd)
892       
893        # Build the communication pattern for the ghost nodes
894        gcommun = \
895                ghost_commun_pattern(subtri, p, triangles_per_proc_ranges)
896        ghost_commun.append(gcommun)
897
898        # Move to the next processor
899
900        tlower = tupper
901
902
903    # Record the ghost layer and communication pattern
904    submesh["ghost_layer_width"] = ghost_layer_width
905    submesh["ghost_nodes"] = ghost_nodes
906    submesh["ghost_triangles"] = ghost_triangles
907    submesh["ghost_commun"] = ghost_commun
908    submesh["ghost_boundary"] = ghost_bnd
909   
910    # Build the communication pattern for the full triangles
911
912    full_commun = full_commun_pattern(submesh, triangles_per_proc)
913    submesh["full_commun"] = full_commun
914
915    # Return the submesh
916
917    return submesh
918
919
920#########################################################
921#
922# Certain quantities may be assigned to the triangles,
923# these quantities must be subdivided in the same way
924# as the triangles
925#
926#  *) The quantities are ordered in the same way as the
927# triangles
928#
929# -------------------------------------------------------
930#
931#  *) The quantites attached to the full triangles are
932# stored in full_quan
933#
934#  *) The quantities attached to the ghost triangles are
935# stored in ghost_quan
936#########################################################
937
938def submesh_quantities(submesh, quantities, triangles_per_proc):
939
940    nproc = len(triangles_per_proc)
941
942    lower = 0
943
944    # Build an empty dictionary to hold the quantites
945
946    submesh["full_quan"] = {}
947    submesh["ghost_quan"] = {}
948    for k in quantities:
949        submesh["full_quan"][k] = []
950        submesh["ghost_quan"][k] = []
951
952    # Loop trough the subdomains
953
954    for p in xrange(nproc):
955        upper =   lower+triangles_per_proc[p]
956
957        # Find the global ID of the ghost triangles
958
959        global_id = []
960        M = len(submesh["ghost_triangles"][p])
961        for j in xrange(M):
962            global_id.append(submesh["ghost_triangles"][p][j][0])
963
964        # Use the global ID to extract the quantites information from
965        # the full domain
966
967        for k in quantities:
968            submesh["full_quan"][k].append(quantities[k][lower:upper])
969            submesh["ghost_quan"][k].append(num.zeros( (M,3) , num.float))
970            for j in range(M):
971                submesh["ghost_quan"][k][p][j] = \
972                                               quantities[k][global_id[j]]
973
974        lower = upper
975
976    return submesh
977
978#########################################################
979#
980# Build the grid partition on the host.
981#
982#  *) See the documentation for submesh_ghost and
983# submesh_full
984#
985# -------------------------------------------------------
986#
987#  *) A dictionary containing the full_triangles,
988# full_nodes, full_boundary, ghost_triangles, ghost_nodes,
989# ghost_boundary, ghost_commun and full_commun and true boundary polygon is returned.
990#
991#########################################################
992
993def build_submesh(nodes, triangles, boundary, quantities,
994                  triangles_per_proc, parameters = None):
995
996    # Temporarily build the mesh to find the neighbouring
997    # triangles and true boundary polygon\
998
999    mesh = Mesh(nodes, triangles, boundary)
1000    boundary_polygon = mesh.get_boundary_polygon()
1001   
1002
1003    # Subdivide into non-overlapping partitions
1004
1005    submeshf = submesh_full(mesh, triangles_per_proc)
1006   
1007    # Add any extra ghost boundary layer information
1008
1009    submeshg = submesh_ghost(submeshf, mesh, triangles_per_proc, parameters)
1010
1011    # Order the quantities information to be the same as the triangle
1012    # information
1013
1014    submesh = submesh_quantities(submeshg, quantities, \
1015                                 triangles_per_proc)
1016
1017    submesh["boundary_polygon"] = boundary_polygon
1018    return submesh
1019
1020#########################################################
1021#
1022#  Given the subdivision of the grid assigned to the
1023# current processor convert it into a form that is
1024# appropriate for the GA datastructure.
1025#
1026#  The main function of these modules is to change the
1027# node numbering. The GA datastructure assumes they
1028# are numbered consecutively from 0.
1029#
1030#  The module also changes the communication pattern
1031# datastructure into a form needed by parallel_advection
1032#
1033#  Authors: Linda Stals and Matthew Hardy, June 2005
1034#  Modified: Linda Stals, Nov 2005 (optimise python code)
1035#            Steve Roberts, Aug 2009 (updating to numpy)
1036#
1037#
1038#########################################################
1039
1040 
1041#########################################################
1042# Convert the format of the data to that used by ANUGA
1043#
1044#
1045# *) Change the nodes global ID's to an integer value,
1046#starting from 0.
1047#
1048# *) The triangles and boundary edges must also be
1049# updated accordingly.
1050#
1051# -------------------------------------------------------
1052#
1053# *) The nodes, triangles and boundary edges defined by
1054# the new numbering scheme are returned
1055#
1056#########################################################
1057
1058def build_local_GA(nodes, triangles, boundaries, tri_map):
1059
1060    Nnodes =len(nodes)
1061    Ntriangles = len(triangles)
1062   
1063    # Extract the nodes (using the local ID)
1064   
1065    GAnodes = num.take(nodes, (1, 2), 1)
1066
1067    # Build a global ID to local ID mapping
1068
1069    NGlobal = 0
1070    for i in xrange(Nnodes):
1071        if nodes[i][0] > NGlobal:
1072            NGlobal = nodes[i][0]
1073
1074    node_map = -1*num.ones(int(NGlobal)+1, num.int)
1075
1076    num.put(node_map, num.take(nodes, (0,), 1).astype(num.int), \
1077        num.arange(Nnodes))
1078       
1079    # Change the global IDs in the triangles to the local IDs
1080
1081    GAtriangles = num.zeros((Ntriangles, 3), num.int)
1082    GAtriangles[:,0] = num.take(node_map, triangles[:,0])
1083    GAtriangles[:,1] = num.take(node_map, triangles[:,1])
1084    GAtriangles[:,2] = num.take(node_map, triangles[:,2])
1085
1086    # Change the triangle numbering in the boundaries
1087
1088    GAboundaries = {}
1089    for b in boundaries:
1090        GAboundaries[tri_map[b[0]], b[1]] = boundaries[b]
1091       
1092   
1093    return GAnodes, GAtriangles, GAboundaries, node_map
1094
1095
1096#########################################################
1097# Change the communication format to that needed by the
1098# parallel advection file.
1099#
1100# *) The index contains [global triangle no,
1101# local triangle no.]
1102#
1103# -------------------------------------------------------
1104#
1105# *) The ghost_recv and full_send dictionaries are
1106# returned.
1107#
1108# *) ghost_recv dictionary is local id, global id, value
1109#
1110# *) full_recv dictionary is local id, global id, value
1111#
1112# *) The information is ordered by the global id. This
1113# means that the communication order is predetermined and
1114# local and global id do not need to be
1115# compared when the information is sent/received.
1116#
1117#########################################################
1118
1119def build_local_commun(tri_map, ghostc, fullc, nproc):
1120
1121    # Initialise
1122
1123    full_send = {}
1124    ghost_recv = {}
1125
1126    # Build the ghost_recv dictionary (sort the
1127    # information by the global numbering)
1128   
1129    ghostc = num.sort(ghostc, 0)
1130   
1131    for c in xrange(nproc):
1132        s = ghostc[:,0]
1133        d = num.compress(num.equal(ghostc[:,1],c), s)
1134        if len(d) > 0:
1135            ghost_recv[c] = [0, 0]
1136            ghost_recv[c][1] = d
1137            ghost_recv[c][0] = num.take(tri_map, d)
1138           
1139    # Build a temporary copy of the full_send dictionary
1140    # (this version allows the information to be stored
1141    # by the global numbering)
1142
1143    tmp_send = {}
1144    for global_id in fullc:
1145        for i in xrange(len(fullc[global_id])):
1146            neigh = fullc[global_id][i]
1147            if not tmp_send.has_key(neigh):
1148                tmp_send[neigh] = []
1149            tmp_send[neigh].append([global_id, \
1150                                    tri_map[global_id]])
1151
1152    # Extract the full send information and put it in the form
1153    # required for the full_send dictionary
1154
1155    for neigh in tmp_send:
1156        neigh_commun = num.sort(tmp_send[neigh], 0)
1157        full_send[neigh] = [0, 0]
1158        full_send[neigh][0] = neigh_commun[:,1]
1159        full_send[neigh][1] = neigh_commun[:,0]
1160
1161    return ghost_recv, full_send
1162
1163
1164#########################################################
1165# Convert the format of the data to that used by ANUGA
1166#
1167#
1168# *) Change the nodes global ID's to an integer value,
1169# starting from 0. The node numbering in the triangles
1170# must also be updated to take this into account.
1171#
1172# *) The triangle number will also change, which affects
1173# the boundary tag information and the communication
1174# pattern.
1175#
1176# -------------------------------------------------------
1177#
1178# *) The nodes, triangles, boundary edges and communication
1179# pattern defined by the new numbering scheme are returned
1180#
1181#########################################################
1182
1183def build_local_mesh(submesh, lower_t, upper_t, nproc):
1184
1185    # Combine the full nodes and ghost nodes
1186
1187    nodes = num.concatenate((submesh["full_nodes"], \
1188                         submesh["ghost_nodes"]))
1189
1190    ghost_layer_width = submesh["ghost_layer_width"]
1191   
1192    # Combine the full triangles and ghost triangles
1193
1194    gtri =  num.take(submesh["ghost_triangles"],(1, 2, 3),1)
1195    triangles = num.concatenate((submesh["full_triangles"], gtri))
1196
1197    # Combine the full boundaries and ghost boundaries
1198
1199    boundaries = submesh["full_boundary"]
1200    for b in submesh["ghost_boundary"]:
1201        boundaries[b]=submesh["ghost_boundary"][b]
1202
1203    # Make note of the new triangle numbers, including the ghost
1204    # triangles
1205
1206    NGlobal = upper_t
1207    for i in xrange(len(submesh["ghost_triangles"])):
1208        id = submesh["ghost_triangles"][i][0]
1209        if id > NGlobal:
1210            NGlobal = id
1211    #index = num.zeros(int(NGlobal)+1, num.int)
1212    tri_map = -1*num.ones(int(NGlobal)+1, num.int)
1213    tri_map[lower_t:upper_t]=num.arange(upper_t-lower_t)
1214    for i in xrange(len(submesh["ghost_triangles"])):
1215        tri_map[submesh["ghost_triangles"][i][0]] = i+upper_t-lower_t
1216   
1217    # Change the node numbering (and update the numbering in the
1218    # triangles)
1219
1220    [GAnodes, GAtriangles, GAboundary, node_map] = \
1221    build_local_GA(nodes, triangles, boundaries, tri_map)
1222
1223    # Extract the local quantities
1224   
1225    quantities ={}
1226    for k in submesh["full_quan"]:
1227        Nf = len(submesh["full_quan"][k])
1228        Ng = len(submesh["ghost_quan"][k])
1229        quantities[k] = num.zeros((Nf+Ng, 3), num.float)
1230        quantities[k][0:Nf] = submesh["full_quan"][k] 
1231        quantities[k][Nf:Nf+Ng] = submesh["ghost_quan"][k]
1232                             
1233    # Change the communication pattern into a form needed by
1234    # the parallel_adv
1235
1236    gcommun = submesh["ghost_commun"]
1237    fcommun = submesh["full_commun"]
1238    [ghost_rec, full_send] = \
1239                build_local_commun(tri_map, gcommun, fcommun, nproc)
1240
1241
1242
1243    tri_l2g  = extract_l2g_map(tri_map)
1244    node_l2g = extract_l2g_map(node_map)
1245     
1246    return GAnodes, GAtriangles, GAboundary, quantities, ghost_rec, \
1247           full_send, tri_map, node_map, tri_l2g, node_l2g, ghost_layer_width
1248
1249
1250#########################################################
1251#
1252# Handle the communication between the host machine
1253# (processor 0) and the processors. The host machine is
1254# responsible for the doing the initial grid partitioning.
1255#
1256# The routines given below should be moved to the
1257# build_submesh.py and build_local.py file to allow
1258# overlapping of  communication and computation.
1259# This should be done after more debugging.
1260#
1261#
1262#  Author: Linda Stals, June 2005
1263#  Modified: Linda Stals, Nov 2005 (optimise python code)
1264#            Steve Roberts, Aug 2009 (update to numpy)
1265#
1266#
1267#########################################################
1268
1269
1270#########################################################
1271#
1272# Send the submesh to processor p.
1273#
1274# *) The order and form is strongly coupled with
1275# rec_submesh.
1276#
1277# -------------------------------------------------------
1278#
1279# *) All of the information has been sent to processor p.
1280#
1281#########################################################
1282
1283def send_submesh(submesh, triangles_per_proc, p, verbose=True):
1284
1285    import pypar
1286   
1287    myid = pypar.rank()
1288    nprocs = pypar.size()
1289   
1290    if verbose: print 'P%d: Sending submesh to P%d' %(myid, p)
1291   
1292    # build and send the tagmap for the boundary conditions
1293   
1294    tagmap = {}
1295    counter = 1
1296    for b in submesh["full_boundary"][p]:
1297        bkey = submesh["full_boundary"][p][b]
1298        if not tagmap.has_key(bkey):
1299            tagmap[bkey] = counter
1300            counter = counter+1
1301    for b in submesh["ghost_boundary"][p]:
1302        bkey = submesh["ghost_boundary"][p][b]
1303        if not tagmap.has_key(bkey):
1304            tagmap[bkey] = counter
1305            counter = counter+1
1306
1307
1308    # send boundary tags
1309    pypar.send(tagmap, p)
1310
1311    # send the quantities key information
1312    pypar.send(submesh["full_quan"].keys(), p)
1313
1314    # compress full_commun
1315    flat_full_commun = []
1316
1317    for c in submesh["full_commun"][p]:
1318        for i in range(len(submesh["full_commun"][p][c])):
1319            flat_full_commun.append([c,submesh["full_commun"][p][c][i]])
1320
1321    # send the array sizes so memory can be allocated
1322
1323    setup_array = num.zeros((9,),num.int)
1324    setup_array[0] = len(submesh["full_nodes"][p])
1325    setup_array[1] = len(submesh["ghost_nodes"][p])
1326    setup_array[2] = len(submesh["full_triangles"][p])
1327    setup_array[3] = len(submesh["ghost_triangles"][p])
1328    setup_array[4] = len(submesh["full_boundary"][p])
1329    setup_array[5] = len(submesh["ghost_boundary"][p])
1330    setup_array[6] = len(submesh["ghost_commun"][p])
1331    setup_array[7] = len(flat_full_commun)
1332    setup_array[8] = len(submesh["full_quan"])
1333
1334    x = num.array(setup_array, num.int)
1335    pypar.send(x, p, bypass=True)
1336
1337
1338    # ghost layer width
1339    x = num.array(submesh["ghost_layer_width"][p], num.int)
1340    pypar.send(x, p, bypass=True)
1341
1342
1343    # send the number of triangles per processor
1344    x = num.array(triangles_per_proc)
1345    pypar.send(x, p, bypass=True)
1346
1347    # send the nodes
1348    x = num.array(submesh["full_nodes"][p], num.float)
1349    pypar.send(x, p, bypass=True)
1350
1351    x = num.array(submesh["ghost_nodes"][p], num.float)
1352    pypar.send(x, p, bypass=True)
1353
1354    # send the triangles
1355    x = num.array(submesh["full_triangles"][p], num.int)
1356    pypar.send(x, p, bypass=True)
1357
1358    # send ghost triangles
1359    x = num.array(submesh["ghost_triangles"][p], num.int)
1360    pypar.send(x, p, bypass=True)
1361
1362
1363    # send the boundary
1364    bc = []
1365    for b in submesh["full_boundary"][p]:
1366        bc.append([b[0], b[1], tagmap[submesh["full_boundary"][p][b]]])
1367
1368    x = num.array(bc, num.int)
1369    pypar.send(x, p, bypass=True)
1370
1371
1372    bc = []
1373    for b in submesh["ghost_boundary"][p]:
1374        bc.append([b[0], b[1], tagmap[submesh["ghost_boundary"][p][b]]])
1375
1376    x = num.array(bc, num.int)
1377    pypar.send(x, p, bypass=True)
1378
1379
1380
1381    # send the communication pattern
1382    x = submesh["ghost_commun"][p]
1383    pypar.send(x, p, bypass=True)
1384
1385
1386    x = num.array(flat_full_commun, num.int)
1387    pypar.send(x, p, bypass=True)
1388
1389
1390    # send the quantities
1391    for k in submesh["full_quan"]:
1392        x = num.array(submesh["full_quan"][k][p], num.float)
1393        pypar.send(x, p, bypass=True)
1394       
1395    for k in submesh["ghost_quan"]:
1396        x = num.array(submesh["ghost_quan"][k][p], num.float)
1397        pypar.send(x,p, bypass=True)
1398
1399
1400
1401#########################################################
1402#
1403# Receive the submesh from processor p.
1404#
1405# *) The order and form is strongly coupled with
1406# send_submesh.
1407#
1408# -------------------------------------------------------
1409#
1410# *) All of the information has been received by the
1411# processor p and passed into build_local.
1412#
1413# *) The information is returned in a form needed by the
1414# GA datastructure.
1415#
1416#########################################################
1417
1418def rec_submesh_flat(p, verbose=True):
1419
1420    import pypar
1421   
1422    numprocs = pypar.size()
1423    myid = pypar.rank()
1424
1425    submesh_cell = {}
1426   
1427    if verbose: print indent+'P%d: Receiving submesh from P%d' %(myid, p)
1428
1429    # receive the tagmap for the boundary conditions
1430   
1431    tagmap = pypar.receive(p)
1432
1433    itagmap = {}
1434    for t in tagmap:
1435        itagmap[tagmap[t]]=t
1436
1437    # receive the quantities key information
1438    qkeys = pypar.receive(p)
1439
1440    # recieve information about the array sizes
1441    x = num.zeros((9,),num.int)
1442    pypar.receive(p, buffer=x,  bypass=True)
1443    setup_array = x
1444
1445    no_full_nodes      = setup_array[0]
1446    no_ghost_nodes     = setup_array[1]
1447    no_full_triangles  = setup_array[2]
1448    no_ghost_triangles = setup_array[3]
1449    no_full_boundary   = setup_array[4]
1450    no_ghost_boundary  = setup_array[5]
1451    no_ghost_commun    = setup_array[6]
1452    no_full_commun     = setup_array[7]
1453    no_quantities      = setup_array[8]
1454
1455   
1456    # ghost layer width
1457    x = num.zeros((1,),num.int)
1458    pypar.receive(p, buffer=x,  bypass=True)
1459    submesh_cell["ghost_layer_width"] = x[0]
1460
1461
1462    # receive the number of triangles per processor
1463    x = num.zeros((numprocs,),num.int)
1464    pypar.receive(p, buffer=x,  bypass=True)
1465    triangles_per_proc = x
1466
1467    # receive the full nodes
1468    x = num.zeros((no_full_nodes,3),num.float)
1469    pypar.receive(p, buffer=x,  bypass=True)
1470    submesh_cell["full_nodes"] = x
1471
1472
1473    # receive the ghost nodes
1474    x = num.zeros((no_ghost_nodes,3),num.float)
1475    pypar.receive(p, buffer=x,  bypass=True)
1476    submesh_cell["ghost_nodes"] = x
1477   
1478    # receive the full triangles
1479    x = num.zeros((no_full_triangles,3),num.int)
1480    pypar.receive(p, buffer=x,  bypass=True)
1481    submesh_cell["full_triangles"] = x
1482
1483   
1484    # receive the ghost triangles
1485    x = num.zeros((no_ghost_triangles,4),num.int)
1486    pypar.receive(p, buffer=x,  bypass=True)
1487    submesh_cell["ghost_triangles"] = x
1488
1489
1490
1491    # receive the full boundary
1492    x = num.zeros((no_full_boundary,3),num.int)
1493    pypar.receive(p, buffer=x,  bypass=True)
1494    bnd_c = x
1495
1496    submesh_cell["full_boundary"] = {}
1497    for b in bnd_c:
1498        submesh_cell["full_boundary"][b[0],b[1]]=itagmap[b[2]]
1499
1500
1501    # receive the ghost boundary
1502    x = num.zeros((no_ghost_boundary,3),num.int)
1503    pypar.receive(p, buffer=x,  bypass=True)
1504    bnd_c = x
1505
1506    submesh_cell["ghost_boundary"] = {}
1507    for b in bnd_c:
1508        submesh_cell["ghost_boundary"][b[0],b[1]]=itagmap[b[2]]
1509
1510    # receive the ghost communication pattern
1511    x = num.zeros((no_ghost_commun,2),num.int)
1512    pypar.receive(p, buffer=x,  bypass=True)
1513    submesh_cell["ghost_commun"] = x
1514   
1515    # receive the full communication pattern
1516    x = num.zeros((no_full_commun,2),num.int)
1517    pypar.receive(p, buffer=x,  bypass=True)
1518    full_commun = x
1519
1520    submesh_cell["full_commun"] = {}
1521    for c in full_commun:
1522        submesh_cell["full_commun"][c[0]] = []
1523    for c in full_commun:
1524        submesh_cell["full_commun"][c[0]].append(c[1])
1525
1526    # receive the quantities
1527
1528    submesh_cell["full_quan"]={}
1529    for i in range(no_quantities):
1530        x = num.zeros((no_full_triangles,3), num.float)
1531        pypar.receive(p, buffer=x, bypass=True)
1532        submesh_cell["full_quan"][qkeys[i]]= x
1533
1534    submesh_cell["ghost_quan"]={}
1535    for i in range(no_quantities):
1536        x = num.zeros((no_ghost_triangles,3), num.float)
1537        pypar.receive(p, buffer=x, bypass=True)
1538        submesh_cell["ghost_quan"][qkeys[i]]= x
1539   
1540    return submesh_cell, triangles_per_proc,\
1541           no_full_nodes, no_full_triangles
1542
1543
1544
1545#########################################################
1546#
1547# Receive the submesh from processor p.
1548#
1549# *) The order and form is strongly coupled with
1550# send_submesh.
1551#
1552# -------------------------------------------------------
1553#
1554# *) All of the information has been received by the
1555# processor p and passed into build_local.
1556#
1557# *) The information is returned in a form needed by the
1558# GA datastructure.
1559#
1560#########################################################
1561
1562def rec_submesh(p, verbose=True):
1563
1564    import pypar
1565   
1566    numproc = pypar.size()
1567    myid = pypar.rank()
1568
1569    [submesh_cell, triangles_per_proc,\
1570     number_of_full_nodes, number_of_full_triangles] = rec_submesh_flat(p,verbose)
1571   
1572    # find the full triangles assigned to this processor
1573
1574    lower_t = 0
1575    for i in range(myid):
1576        lower_t = lower_t+triangles_per_proc[i]
1577    upper_t = lower_t+triangles_per_proc[myid]
1578
1579    # convert the information into a form needed by the GA
1580    # datastructure
1581
1582    [GAnodes, GAtriangles, boundary, quantities, \
1583     ghost_rec, full_send, \
1584     tri_map, node_map, tri_l2g, node_l2g, \
1585     ghost_layer_width] = \
1586     build_local_mesh(submesh_cell, lower_t, upper_t, numproc)
1587   
1588    return GAnodes, GAtriangles, boundary, quantities,\
1589           ghost_rec, full_send,\
1590           number_of_full_nodes, number_of_full_triangles, tri_map, node_map,\
1591           tri_l2g, node_l2g, ghost_layer_width
1592
1593
1594
1595
1596#########################################################
1597#
1598# Extract the submesh that will belong to the
1599# processor 0 (i.e. processor zero)
1600#
1601#  *) See the documentation for build_submesh
1602#
1603# -------------------------------------------------------
1604#
1605#  *) A dictionary containing the full_triangles,
1606# full_nodes, full_boundary, ghost_triangles, ghost_nodes,
1607# ghost_boundary, ghost_commun and full_commun belonging
1608# to processor zero are returned.
1609#
1610#########################################################
1611def extract_submesh(submesh, triangles_per_proc, p2s_map=None, p=0):
1612
1613   
1614    submesh_cell = {}
1615    submesh_cell["ghost_layer_width"] = submesh["ghost_layer_width"][p]
1616    submesh_cell["full_nodes"] = submesh["full_nodes"][p]
1617    submesh_cell["ghost_nodes"] = submesh["ghost_nodes"][p]
1618    submesh_cell["full_triangles"] = submesh["full_triangles"][p]
1619    submesh_cell["ghost_triangles"] = submesh["ghost_triangles"][p]
1620    submesh_cell["full_boundary"] = submesh["full_boundary"][p]
1621    submesh_cell["ghost_boundary"] = submesh["ghost_boundary"][p]
1622    submesh_cell["ghost_commun"] = submesh["ghost_commun"][p]
1623    submesh_cell["full_commun"] = submesh["full_commun"][p]
1624    submesh_cell["full_quan"] ={}
1625    submesh_cell["ghost_quan"]={}
1626    for k in submesh["full_quan"]:
1627        submesh_cell["full_quan"][k] = submesh["full_quan"][k][p]
1628        submesh_cell["ghost_quan"][k] = submesh["ghost_quan"][k][p]
1629
1630
1631    # FIXME SR: I think there is already a structure with this info in the mesh
1632    lower_t = 0
1633    for i in range(p):
1634        lower_t = lower_t+triangles_per_proc[i]
1635    upper_t = lower_t+triangles_per_proc[p]
1636
1637    numprocs = len(triangles_per_proc)
1638    points, vertices, boundary, quantities, ghost_recv_dict, \
1639            full_send_dict, tri_map, node_map, tri_l2g, node_l2g, \
1640            ghost_layer_width = \
1641            build_local_mesh(submesh_cell, lower_t, upper_t, numprocs)
1642
1643
1644    if p2s_map is None:
1645        pass
1646    else:
1647        try:
1648            tri_l2g = p2s_map[tri_l2g]
1649        except:
1650            tri_l2g = p2s_map
1651
1652    return  points, vertices, boundary, quantities, ghost_recv_dict, \
1653           full_send_dict, tri_map, node_map, tri_l2g, node_l2g, ghost_layer_width
1654           
1655
1656
1657def extract_l2g_map(map):
1658    # Extract l2g data  from corresponding map
1659    # Maps
1660
1661    import numpy as num
1662
1663    b = num.arange(len(map))
1664
1665    l_ids = num.extract(map>-1,map)
1666    g_ids = num.extract(map>-1,b)
1667
1668
1669#    print len(g_ids)
1670#    print len(l_ids)
1671#    print l_ids
1672#    print g_ids
1673
1674    l2g = num.zeros_like(g_ids)
1675    l2g[l_ids] = g_ids
1676
1677    return l2g
1678
1679
1680
1681
1682
Note: See TracBrowser for help on using the repository browser.