source: trunk/anuga_core/anuga/pmesh/mesh_interface.py @ 9679

Last change on this file since 9679 was 9105, checked in by davies, 11 years ago

Fixed conflicts

File size: 14.2 KB
Line 
1
2from anuga.coordinate_transforms.geo_reference import Geo_reference,DEFAULT_ZONE
3from anuga.geometry.polygon import  point_in_polygon ,populate_polygon
4from anuga.utilities.numerical_tools import ensure_numeric
5import numpy as num
6from anuga.geometry.polygon import inside_polygon
7from anuga.geometry.polygon import polylist2points_verts
8import anuga.utilities.log as log
9import datetime
10
11# This is due to pmesh being a package and a module and
12# the current dir being unknown
13try:
14    from anuga.pmesh.mesh import Mesh
15except ImportError: 
16    from mesh import Mesh
17
18import exceptions
19class PolygonError(exceptions.Exception): pass
20class SegmentError(exceptions.Exception): pass
21
22def create_mesh_from_regions(bounding_polygon,
23                             boundary_tags,
24                             maximum_triangle_area=None,
25                             filename=None,
26                             interior_regions=None,
27                             interior_holes=None,
28                             hole_tags=None,
29                             poly_geo_reference=None,
30                             mesh_geo_reference=None,
31                             minimum_triangle_angle=28.0,
32                             fail_if_polygons_outside=True,
33                             breaklines=None,
34                             use_cache=False,
35                             verbose=True,
36                             regionPtArea=None):
37    """Create mesh from bounding polygons, and resolutions.
38
39    bounding_polygon is a list of points in Eastings and Northings,
40    relative to the poly_geo_reference.
41
42    Boundary tags is a dictionary of symbolic tags. For every tag there
43    is a list of indices referring to segments associated with that tag.
44    If a segment is omitted an Exception will be raised.
45
46    maximum_triangle_area is the maximal area per triangle
47    for the bounding polygon, excluding the  interior regions.
48
49    Interior_regions is a list of tuples consisting of (polygon,
50    resolution) for each region to be separately refined. Do not have
51    polygon lines cross or be on-top of each other.  Also do not have
52    polygon close to each other.
53   
54    NOTE: If a interior_region is outside the bounding_polygon it should
55    throw an error
56   
57    Interior_holes is a list of polygons for each hole.
58    hole_tags is an optional list of boundary tags for the holes, see
59                boundary_tags parameter.
60
61    This function does not allow segments to share points - use underlying
62    pmesh functionality for that
63
64    poly_geo_reference is the geo_reference of the bounding polygon and
65    the interior polygons.
66    If none, assume absolute.  Please pass one though, since absolute
67    references have a zone.
68   
69    mesh_geo_reference is the geo_reference of the mesh to be created.
70    If none is given one will be automatically generated.  It was use
71    the lower left hand corner of  bounding_polygon (absolute)
72    as the x and y values for the geo_ref.
73
74    breaklines is a list of polygons. These lines will be preserved by the
75               triangulation algorithm - useful for coastlines, walls, etc.
76               The polygons are not closed.                       
77   
78    Returns the mesh instance if no filename is given
79
80    Note, interior regions should be fully nested, as overlaps may cause
81    unintended resolutions.
82
83    fail_if_polygons_outside: If True (the default) Exception in thrown
84    where interior polygons fall outside bounding polygon. If False, these
85    will be ignored and execution continued.
86       
87   
88    """
89   
90   
91    if verbose: log.resource_usage_timing(log.logging.INFO, "start_")
92    if verbose: log.timingInfo("maximum_triangle_area, " + str(maximum_triangle_area))
93    if verbose: log.timingInfo("minimum_triangle_angle, " + str(minimum_triangle_angle))
94    if verbose: log.timingInfo("startMesh, '%s'" % log.CurrentDateTime())
95   
96    # Build arguments and keyword arguments for use with caching or apply.
97    args = (bounding_polygon,
98            boundary_tags)
99   
100    kwargs = {'maximum_triangle_area': maximum_triangle_area,
101              'filename': filename,
102              'interior_regions': interior_regions,
103              'interior_holes': interior_holes,
104              'hole_tags': hole_tags,
105              'poly_geo_reference': poly_geo_reference,
106              'mesh_geo_reference': mesh_geo_reference,
107              'minimum_triangle_angle': minimum_triangle_angle,
108              'fail_if_polygons_outside': fail_if_polygons_outside,
109              'breaklines': breaklines,
110              'verbose': verbose,
111              'regionPtArea': regionPtArea}   # FIXME (Ole): Should be bypassed one day. See ticket:14
112
113    # Call underlying engine with or without caching
114    if use_cache is True:
115        try:
116            from anuga.caching import cache
117        except:
118            msg = 'Caching was requested, but caching module'+\
119                  'could not be imported'
120            raise Exception(msg)
121
122
123        m = cache(_create_mesh_from_regions,
124                  args, kwargs,
125                  verbose=verbose,
126                  compression=False)
127    else:
128        m = apply(_create_mesh_from_regions,
129                  args, kwargs)
130
131
132   
133    return m   
134       
135
136
137def _create_mesh_from_regions(bounding_polygon,
138                              boundary_tags,
139                              maximum_triangle_area=None,
140                              filename=None,                             
141                              interior_regions=None,
142                              interior_holes=None,
143                              hole_tags=None,
144                              poly_geo_reference=None,
145                              mesh_geo_reference=None,
146                              minimum_triangle_angle=28.0,
147                              fail_if_polygons_outside=True,
148                              breaklines=None,
149                              verbose=True,
150                              regionPtArea=None):
151    """_create_mesh_from_regions - internal function.
152
153    See create_mesh_from_regions for documentation.
154    """
155
156    # check the segment indexes - throw an error if they are out of bounds
157    if boundary_tags is not None:
158        max_points = len(bounding_polygon)
159        for key in boundary_tags.keys():
160            if len([x for x in boundary_tags[key] if x > max_points-1]) >= 1:
161                msg = 'Boundary tag %s has segment out of bounds. '\
162                      %(str(key))
163                msg += 'Number of points in bounding polygon = %d' % max_points
164                raise SegmentError(msg)
165
166        for i in range(max_points):
167            found = False
168            for tag in boundary_tags:
169                if i in boundary_tags[tag]:
170                    found = True
171            if found is False:
172                msg = 'Segment %d was not assigned a boundary_tag.' % i
173                msg +=  'Default tag "exterior" will be assigned to missing segment'
174                #raise Exception(msg)
175                # Fixme: Use proper Python warning
176                if verbose: log.critical('WARNING: %s' % msg)
177               
178
179   
180    #In addition I reckon the polygons could be of class Geospatial_data
181    #(DSG) If polygons were classes caching would break in places.
182
183    # Simple check
184    bounding_polygon = ensure_numeric(bounding_polygon, num.float)
185    msg = 'Bounding polygon must be a list of points or an Nx2 array'
186    assert len(bounding_polygon.shape) == 2, msg
187    assert bounding_polygon.shape[1] == 2, msg   
188
189    #
190    if interior_regions is not None:
191       
192        # Test that all the interior polygons are inside the
193        # bounding_poly and throw out those that aren't fully
194        # included.  #Note, Both poly's have the same geo_ref,
195        # therefore don't take into account # geo_ref
196
197
198        polygons_inside_boundary = []
199        for interior_polygon, res in interior_regions:
200            indices = inside_polygon(interior_polygon, bounding_polygon,
201                                     closed = True, verbose = False)
202   
203            if len(indices) <> len(interior_polygon): 
204                msg = 'Interior polygon %s is not fully inside'\
205                      %(str(interior_polygon))
206                msg += ' bounding polygon: %s.' %(str(bounding_polygon))
207
208                if fail_if_polygons_outside is True:
209                    raise PolygonError(msg)
210                else:
211                    msg += ' I will ignore it.'
212                    log.critical(msg)
213
214            else:
215                polygons_inside_boundary.append([interior_polygon, res])
216               
217        # Record only those that were fully contained       
218        interior_regions = polygons_inside_boundary
219
220           
221   
222# the following segment of code could be used to Test that all the
223# interior polygons are inside the bounding_poly... however it might need
224# to be change a bit   
225#
226#count = 0
227#for i in range(len(interior_regions)):
228#    region = interior_regions[i]
229#    interior_polygon = region[0]
230#    if len(inside_polygon(interior_polygon, bounding_polygon,
231#                   closed = True, verbose = False)) <> len(interior_polygon):
232#        print 'WARNING: interior polygon %d is outside bounding polygon' %(i)
233#        count += 1
234
235#if count == 0:
236#    print 'interior regions OK'
237#else:
238#    print 'check out your interior polygons'
239#    print 'check %s in production directory' %figname
240#    import sys; sys.exit()
241
242    if interior_holes is not None:       
243        # Test that all the interior polygons are inside the bounding_poly
244        for interior_polygon in interior_holes:
245
246            # Test that we have a polygon
247            if len(num.array(interior_polygon).flat) < 6:
248                msg = 'Interior hole polygon %s has too few (<3) points.\n' \
249                    %(str(interior_polygon))
250                msg = msg + '(Insure that you have specified a LIST of interior hole polygons)'
251                raise PolygonError(msg)
252                   
253            indices = inside_polygon(interior_polygon, bounding_polygon,
254                                     closed = True, verbose = False)
255
256   
257            if len(indices) <> len(interior_polygon): 
258                msg = 'Interior polygon %s is outside bounding polygon: %s'\
259                      %(str(interior_polygon), str(bounding_polygon))
260                raise PolygonError(msg)
261
262    # Resolve geo referencing       
263    if mesh_geo_reference is None:
264        xllcorner = min(bounding_polygon[:,0])
265        yllcorner = min(bounding_polygon[:,1])   
266        #
267        if poly_geo_reference is None:
268            zone = DEFAULT_ZONE
269        else:
270            zone = poly_geo_reference.get_zone()
271            [(xllcorner,yllcorner)] = poly_geo_reference.get_absolute( \
272            [(xllcorner,yllcorner)])
273        # create a geo_ref, based on the llc of the bounding_polygon
274        mesh_geo_reference = Geo_reference(xllcorner = xllcorner,
275                                           yllcorner = yllcorner,
276                                           zone = zone)
277
278    m = Mesh(geo_reference=mesh_geo_reference)
279
280    # build a list of discrete segments from the breakline polygons
281    if breaklines is not None:
282        points, verts = polylist2points_verts(breaklines)
283        m.add_points_and_segments(points, verts)
284
285    # Do bounding polygon
286    m.add_region_from_polygon(bounding_polygon,
287                              segment_tags=boundary_tags,
288                              geo_reference=poly_geo_reference)
289
290    # Find one point inside region automatically
291    if interior_regions is not None:
292        excluded_polygons = []       
293        for polygon, res in interior_regions:
294            excluded_polygons.append( polygon )
295    else:
296        excluded_polygons = None
297
298    # Convert bounding poly to absolute values
299    # this sort of thing can be fixed with the geo_points class
300    if poly_geo_reference is not None:
301        bounding_polygon_absolute = \
302            poly_geo_reference.get_absolute(bounding_polygon)
303    else:
304        bounding_polygon_absolute = bounding_polygon
305   
306    inner_point = point_in_polygon(bounding_polygon_absolute)
307    inner = m.add_region(inner_point[0], inner_point[1])
308    inner.setMaxArea(maximum_triangle_area)
309
310    # Do interior regions
311#    if interior_regions is not None:   
312#        for polygon, res in interior_regions:
313#            m.add_region_from_polygon(polygon,
314#                                      geo_reference=poly_geo_reference)
315#            # convert bounding poly to absolute values
316#            if poly_geo_reference is not None:
317#                polygon_absolute = \
318#                    poly_geo_reference.get_absolute(polygon)
319#            else:
320#                polygon_absolute = polygon
321#            inner_point = point_in_polygon(polygon_absolute)
322#            region = m.add_region(inner_point[0], inner_point[1])
323#            region.setMaxArea(res)
324           
325           
326    if interior_regions is not None:   
327        for polygon, res in interior_regions:
328            m.add_region_from_polygon(polygon,
329                                      max_triangle_area=res,
330                                      geo_reference=poly_geo_reference)
331   
332           
333    # Do interior holes
334    if interior_holes is not None:   
335        for n, polygon in enumerate(interior_holes):
336            try:
337                tags = hole_tags[n]
338            except:
339                tags = {}
340            m.add_hole_from_polygon(polygon,
341                                    segment_tags=tags,
342                                    geo_reference=poly_geo_reference)
343
344
345    # 22/04/2014
346    # Add user-specified point-based regions with max area
347    if(regionPtArea is not None):
348        for i in range(len(regionPtArea)):
349            inner = m.add_region(regionPtArea[i][0], regionPtArea[i][1])
350            inner.setMaxArea(regionPtArea[i][2])
351       
352               
353
354
355    # NOTE (Ole): This was moved here as it is annoying if mesh is always
356    # stored irrespective of whether the computation
357    # was cached or not. This caused Domain to
358    # recompute as it has meshfile as a dependency
359
360    # Decide whether to store this mesh or return it
361   
362   
363    if filename is None:
364        return m
365    else:
366        if verbose: log.critical("Generating mesh to file '%s'" % filename)
367     
368        m.generate_mesh(minimum_triangle_angle=minimum_triangle_angle,
369                        verbose=verbose)
370        m.export_mesh_file(filename)
371
372        return m
373       
Note: See TracBrowser for help on using the repository browser.