source: trunk/anuga_core/source/anuga_parallel/parallel_api.py @ 8546

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

Pulling out s2p_map and p2s_map

File size: 11.7 KB
Line 
1"""Trying to lump parallel stuff into simpler interface
2
3
4"""
5
6import numpy as num
7
8# The abstract Python-MPI interface
9from anuga_parallel.parallel_abstraction import size, rank, get_processor_name
10from anuga_parallel.parallel_abstraction import finalize, send, receive
11from anuga_parallel.parallel_abstraction import pypar_available, barrier
12
13
14# ANUGA parallel engine (only load if pypar can)
15if pypar_available:
16    from anuga_parallel.distribute_mesh  import send_submesh
17    from anuga_parallel.distribute_mesh  import rec_submesh
18    from anuga_parallel.distribute_mesh  import extract_hostmesh
19
20    # Mesh partitioning using Metis
21    from anuga_parallel.distribute_mesh import build_submesh
22    from anuga_parallel.distribute_mesh import pmesh_divide_metis_with_map
23
24    from anuga_parallel.parallel_shallow_water import Parallel_domain
25
26#------------------------------------------------------------------------------
27# Read in processor information
28#------------------------------------------------------------------------------
29
30numprocs = size()
31myid = rank()
32processor_name = get_processor_name()
33#print 'I am processor %d of %d on node %s' %(myid, numprocs, processor_name)
34
35
36
37
38def distribute(domain, verbose=False, debug=False, parameters = None):
39    """ Distribute the domain to all processes
40    """
41
42
43    if debug:
44        verbose = True
45       
46    barrier()
47
48    # FIXME: Dummy assignment (until boundaries are refactored to
49    # be independent of domains until they are applied)
50    if myid == 0:
51        bdmap = {}
52        for tag in domain.get_boundary_tags():
53            bdmap[tag] = None
54   
55   
56        domain.set_boundary(bdmap)
57
58
59    if not pypar_available or numprocs == 1 : return domain # Bypass
60
61    # For some obscure reason this communication must happen prior to
62    # the more complex mesh distribution - Oh Well!
63    if myid == 0:
64        domain_name = domain.get_name()
65        domain_dir = domain.get_datadir()
66        domain_store = domain.get_store()
67        domain_minimum_storable_height = domain.minimum_storable_height
68        georef = domain.geo_reference
69        number_of_global_triangles = domain.number_of_triangles
70        number_of_global_nodes = domain.number_of_nodes
71       
72        # FIXME - what other attributes need to be transferred?
73
74        for p in xrange(1, numprocs):
75            # FIXME SR: Creates cPickle dump
76            send((domain_name, domain_dir, domain_store, \
77                  domain_minimum_storable_height, georef, \
78                  number_of_global_triangles, number_of_global_nodes), p)
79    else:
80        if verbose: print 'P%d: Receiving domain attributes' %(myid)
81
82        domain_name, domain_dir, domain_store, \
83                  domain_minimum_storable_height, \
84                  georef, number_of_global_triangles, \
85                  number_of_global_nodes = receive(0)
86
87
88
89    # Distribute boundary conditions
90    # FIXME: This cannot handle e.g. Time_boundaries due to
91    # difficulties pickling functions
92    if myid == 0:
93        boundary_map = domain.boundary_map
94        for p in xrange(1, numprocs):
95            # FIXME SR: Creates cPickle dump
96            send(boundary_map, p)
97    else:
98        if verbose: print 'P%d: Receiving boundary map' %(myid)       
99
100        boundary_map = receive(0)
101       
102
103
104
105    if myid == 0:
106        # Partition and distribute mesh.
107        # Structures returned is in the
108        # correct form for the ANUGA data structure
109
110
111        points, vertices, boundary, quantities,\
112                ghost_recv_dict, full_send_dict,\
113                number_of_full_nodes, number_of_full_triangles,\
114                s2p_map, p2s_map, tri_map, node_map, ghost_layer_width =\
115                distribute_mesh(domain, verbose=verbose, debug=debug, parameters=parameters)
116           
117        # Extract l2g maps
118        tri_l2g  = extract_l2g_map(tri_map)
119        node_l2g = extract_l2g_map(node_map)
120
121        if debug:
122            print 'P%d' %myid
123            print 'tri_map ',tri_map
124            print 'node_map',node_map
125            print 'tri_l2g', tri_l2g
126            print 'node_l2g', node_l2g
127            print 's2p_map', s2p_map
128            print 'p2s_map', p2s_map
129
130
131        def protocol(x):
132            vanilla=False
133            import pypar
134            control_info, x = pypar.create_control_info(x, vanilla, return_object=True)
135            print 'protocol', control_info[0]
136           
137        # Send serial to parallel (s2p) and parallel to serial (p2s) triangle mapping to proc 1 .. numprocs
138
139
140
141#        n = len(s2p_map)
142#        s2p_map_keys_flat = num.reshape(num.array(s2p_map.keys(),num.int), (n,1) )
143#        s2p_map_values_flat = num.array(s2p_map.values(),num.int)
144#        s2p_map_flat = num.concatenate( (s2p_map_keys_flat, s2p_map_values_flat), axis=1 )
145#
146#        n = len(p2s_map)
147#        p2s_map_keys_flat = num.reshape(num.array(p2s_map.keys(),num.int), (n,2) )
148#        p2s_map_values_flat = num.reshape(num.array(p2s_map.values(),num.int) , (n,1))
149#        p2s_map_flat = num.concatenate( (p2s_map_keys_flat, p2s_map_values_flat), axis=1 )
150
151        s2p_map = None
152        p2s_map = None
153
154#        for p in range(1, numprocs):
155#
156#            # FIXME SR: Creates cPickle dump
157#            send(s2p_map_flat, p)
158#            # FIXME SR: Creates cPickle dump
159#            #print p2s_map
160#            send(p2s_map_flat, p)
161
162        if verbose: print 'Communication done'
163       
164    else:
165        # Read in the mesh partition that belongs to this
166        # processor
167        if verbose: print 'P%d: Receiving submeshes' %(myid)               
168        points, vertices, boundary, quantities,\
169                ghost_recv_dict, full_send_dict,\
170                number_of_full_nodes, number_of_full_triangles, \
171                tri_map, node_map, ghost_layer_width =\
172                rec_submesh(0, verbose)
173
174
175
176        # Extract l2g maps
177        tri_l2g  = extract_l2g_map(tri_map)
178        node_l2g = extract_l2g_map(node_map)
179       
180        # Recieve serial to parallel (s2p) and parallel to serial (p2s) triangle mapping
181        s2p_map = None
182        p2s_map = None
183
184#        s2p_map_flat = receive(0)
185#        s2p_map = dict.fromkeys(s2p_map_flat[:,0], s2p_map_flat[:,1:2])
186#
187#        p2s_map_flat = receive(0)
188#        p2s_map_keys = [tuple(x) for x in p2s_map_flat[:,0:1]]
189#
190#        p2s_map = dict.fromkeys(p2s_map_keys, p2s_map_flat[:,2])
191
192    #------------------------------------------------------------------------
193    # Build the domain for this processor using partion structures
194    #------------------------------------------------------------------------
195
196    if verbose: print 'myid = %g, no_full_nodes = %g, no_full_triangles = %g' % (myid, number_of_full_nodes, number_of_full_triangles)
197
198   
199    domain = Parallel_domain(points, vertices, boundary,
200                             full_send_dict=full_send_dict,
201                             ghost_recv_dict=ghost_recv_dict,
202                             number_of_full_nodes=number_of_full_nodes,
203                             number_of_full_triangles=number_of_full_triangles,
204                             geo_reference=georef,
205                             number_of_global_triangles = number_of_global_triangles,
206                             number_of_global_nodes = number_of_global_nodes,
207                             s2p_map = s2p_map,
208                             p2s_map = p2s_map, ## jj added this
209                             tri_l2g = tri_l2g, ## SR added this
210                             node_l2g = node_l2g,
211                             ghost_layer_width = ghost_layer_width)
212
213    #------------------------------------------------------------------------
214    # Transfer initial conditions to each subdomain
215    #------------------------------------------------------------------------
216    for q in quantities:
217        domain.set_quantity(q, quantities[q]) 
218
219
220    #------------------------------------------------------------------------
221    # Transfer boundary conditions to each subdomain
222    #------------------------------------------------------------------------
223    boundary_map['ghost'] = None  # Add binding to ghost boundary
224    domain.set_boundary(boundary_map)
225
226
227    #------------------------------------------------------------------------
228    # Transfer other attributes to each subdomain
229    #------------------------------------------------------------------------
230    domain.set_name(domain_name)
231    domain.set_datadir(domain_dir)
232    domain.set_store(domain_store)
233    domain.set_minimum_storable_height(domain_minimum_storable_height)
234    domain.geo_reference = georef   
235
236    #------------------------------------------------------------------------
237    # Return parallel domain to all nodes
238    #------------------------------------------------------------------------
239    return domain   
240
241
242
243
244
245
246def distribute_mesh(domain, verbose=False, debug=False, parameters=None):
247
248
249    if debug:
250        verbose = True
251
252    numprocs = size()
253
254   
255    # Subdivide the mesh
256    if verbose: print 'Subdivide mesh'
257    nodes, triangles, boundary, triangles_per_proc, quantities, \
258           s2p_map, p2s_map = \
259           pmesh_divide_metis_with_map(domain, numprocs)
260
261    #PETE: s2p_map (maps serial domain triangles to parallel domain triangles)
262    #      sp2_map (maps parallel domain triangles to domain triangles)
263
264
265    # Build the mesh that should be assigned to each processor,
266    # this includes ghost nodes and the communication pattern
267    if verbose: print 'Build submeshes'   
268    submesh = build_submesh(nodes, triangles, boundary,\
269                            quantities, triangles_per_proc, parameters)
270
271    if verbose:
272        for p in range(numprocs):
273            N = len(submesh['ghost_nodes'][p])               
274            M = len(submesh['ghost_triangles'][p])
275            print 'There are %d ghost nodes and %d ghost triangles on proc %d'\
276                  %(N, M, p)
277
278    #if debug:
279    #    from pprint import pprint
280    #    pprint(submesh)
281
282
283    # Send the mesh partition to the appropriate processor
284    if verbose: print 'Distribute submeshes'       
285    for p in range(1, numprocs):
286        send_submesh(submesh, triangles_per_proc, p, verbose)
287
288    # Build the local mesh for processor 0
289    points, vertices, boundary, quantities, \
290            ghost_recv_dict, full_send_dict, tri_map, node_map, ghost_layer_width =\
291              extract_hostmesh(submesh, triangles_per_proc)
292
293    # Keep track of the number full nodes and triangles.
294    # This is useful later if one needs access to a ghost-free domain
295    # Here, we do it for process 0. The others are done in rec_submesh.
296    number_of_full_nodes = len(submesh['full_nodes'][0])
297    number_of_full_triangles = len(submesh['full_triangles'][0])
298       
299    #print
300    #for p in range(numprocs):
301    #    print 'Process %d:' %(p)
302    #
303    #    print 'full_triangles:'
304    #    print submesh['full_triangles'][p]
305    #
306    #    print 'full_nodes:'
307    #    print submesh['full_nodes'][p]
308    #
309    #    print 'ghost_triangles:'
310    #    print submesh['ghost_triangles'][p]#
311    #
312    #    print 'ghost_nodes:'
313    #   print submesh['ghost_nodes'][p]                               
314    #    print
315    #
316    #print 'Receive dict'
317    #print ghost_recv_dict
318    #
319    #print 'Send dict'
320    #print full_send_dict       
321
322
323    # Return structures necessary for building the parallel domain
324    return points, vertices, boundary, quantities,\
325           ghost_recv_dict, full_send_dict,\
326           number_of_full_nodes, number_of_full_triangles, \
327           s2p_map, p2s_map, tri_map, node_map, ghost_layer_width
328   
329
330
331def extract_l2g_map(map):
332    # Extract l2g data  from corresponding map
333    # Maps
334
335    import numpy as num
336   
337    b = num.arange(len(map))
338
339    l_ids = num.extract(map>-1,map)
340    g_ids = num.extract(map>-1,b)
341
342#    print len(g_ids)
343#    print len(l_ids)
344#    print l_ids
345
346    l2g = num.zeros_like(g_ids)
347    l2g[l_ids] = g_ids
348
349    return l2g
350
Note: See TracBrowser for help on using the repository browser.