Ejemplo n.º 1
0
   def _get_stream_func_bdy(self):

      # calculate stream function on boundary
      # - continuous on boundary (also should be periodic)

      import numpy            as np
      import geometry_planar  as GP
      
      # coordinates of polygon
      x,y   = np.array(self.coords).transpose()
      Nx    = len(x)
      nvec  = np.arange(Nx)
      sfun  = 0*x
      
      # for each singularity (just outside polygon)
      perimeter,resolution,\
         spacings,tangent_dirn	= GP.curve_info(self.singularities)

      for i,sing in enumerate(self.singularities):
         an	         = self.sing_coeffs[i]
         branch_dir  = np.pi/2.+tangent_dirn[i]
         atan2	   = GP.arctan2_branch(y,x=x,branch_point=sing,branch_dir=branch_dir)
         atan2	   = GP.make_arctan2_cts(atan2)
         sfun        = sfun+an*atan2
      
      self.stream_func_bdy = sfun
      return
Ejemplo n.º 2
0
   def get_contour_lengths(self,bmap=None,pobj=None,show=True,test_function=None,\
                              func_vals_orig=None):

      import numpy as np
      ###########################################################################################
      class area_info:

         ########################################################################################
         def __init__(self,xy_bdy_coords,xy_conts,\
                        area,perimeter,lengths,\
                        spherical_geometry=False,\
                        ll_bdy_coords=None,ll_contours=None,\
                        func_vals=None,stream_func=None):
         
            import MIZchar as mizc
            # NB use "1*" to remove pointers to the arrays outside the function
            # - like copy, but works for lists also
            self.xy_bdy_coords   = 1*xy_bdy_coords # (x,y) coordinates of boundary (ie in projected space)
            self.xy_contours     = 1*xy_conts      # (x,y) coordinates of each contour
            self.lengths         = 1*lengths	   # lengths of each contour
            self.area	         = area		   # area of polygon
            self.perimeter       = perimeter	   # perimeter of polygon
            self.FDI             = mizc.frac_dim_index([self.area,self.perimeter])
            
            # lon-lat info if present
            self.spherical_geometry = spherical_geometry
            if spherical_geometry:
               if ll_contours is None:
                  raise ValueError('"ll_contours" not given')
               if ll_bdy_coords is None:
                  raise ValueError('"ll_bdy_coords" not given')
               self.lonlat_contours = 1*ll_contours   # (lon,lat) coordinates of each contour
               self.ll_bdy_coords   = 1*ll_bdy_coords # (lon,lat) coordinates of boundary

            if func_vals is not None:
               self.func_vals	 = 1*func_vals	   # value of function used by Laplace's equation
            if stream_func is not None:
               self.stream_func	 = 1*stream_func   # value of stream function produced by Laplace's equation

            # some summarising info about "lengths"
            lens                       = np.array(lengths)
            self.length_mean           = np.mean(lens)
            self.length_median         = np.median(lens)
            self.length_percentile05   = np.percentile(lens,5)
            self.length_percentile95   = np.percentile(lens,95)

            return
      ###########################################################################################

      if bmap is not None:
         # boundary/area information
         # - use routines for sphere
         import geometry_sphere as GS

         print('getting contour lengths on sphere...\n')

         x,y            = np.array(self.coords).transpose()
         lons,lats      = bmap(x,y,inverse=True)
         lst            = list(np.array([lons,lats]).transpose())
         ll_bdy_coords  = [(lo,la) for lo,la in lst]
         area           = GS.area_polygon_ellipsoid(lons,lats,radians=False)
         arclen         = GS.arc_length(lons,lats,radians=False,closed=True)
         perimeter      = arclen[-1]

         # get isolines of function (plot if pobj is not None)
         contours = self.get_isolines(pobj=pobj,show=show,test_function=test_function,\
                                       func_vals_orig=func_vals_orig)

         ll_conts = []
         lengths  = []

         for cont in contours:
            # list of coords (tuples)
            x,y         = np.array(cont).transpose()
            lons,lats	= bmap(x,y,inverse=True)
            arclen      = GS.arc_length(lons,lats,radians=False,closed=False)
            #
            lengths.append(arclen[-1])	#perimeter
            lst	  = list(np.array([lons,lats]).transpose())
            tups  = [(lo,la) for lo,la in lst]
            ll_conts.append(tups)

         # output object with all the info
         AI = area_info(self.coords,contours,\
                        area,perimeter,lengths,\
                        func_vals=self.func_vals,stream_func=self.stream_func_bdy,\
                        ll_bdy_coords=ll_bdy_coords,ll_contours=ll_conts,\
                        spherical_geometry=True)
      else:
         # boundary/area information
         # - just Euclidean routines
         import geometry_planar as GP

         print('getting contour lengths in the plane...\n')

         x,y         = np.array(self.coords).transpose()
         area        = self.shapely_polygon.area
         perimeter   = self.shapely_polygon.length

         # get isolines of function (plot if pobj is not None)
         contours = self.get_isolines(pobj=pobj,show=show,\
                                       test_function=test_function,\
                                       func_vals_orig=func_vals_orig)
         lengths  = []

         for cont in contours:
            # list of coords (tuples)
            P  = GP.curve_info(cont,closed=False)[0]	# perimeter
            lengths.append(P)

         # output object with all the info
         # area_info(xy_bdy_coords,xy_conts,\
         #			area,perimeter,lengths,\
         #			ll_bdy_coords=None,ll_conts=None,\
         #			func_vals=None,stream_func=None,):
         AI = area_info(self.coords,contours,\
                        area,perimeter,lengths,\
                        func_vals=self.func_vals,stream_func=self.stream_func_bdy,\
                        spherical_geometry=False)

      return AI
Ejemplo n.º 3
0
   def _check_coords(self,coords,fvals,do_check=True):

      import rtree.index	as Rindex
      import numpy            as np
      import MIZchar          as mizc
      import geometry_planar  as GP
      import shapely.geometry as shgeom # http://toblerity.org/shapely/manual.html

      #############################################################
      def do_fill(pcoords,res=None,f=None):

         #############################################################
         if res is None: 
            print('Too few points - adding more')
         else:
            print('Checking boundary points are close enough together')

         xc,yc    = np.array(pcoords).transpose()
         xc2,yc2  = mizc.fill_poly(xc,yc,res=res)
         pc       = np.array([xc2,yc2]).transpose()   # arrays of arrays: coords = rows
         pc       = [tuple(xy) for xy in pc]          # list of tuples
         #############################################################

         #############################################################
         if f is not None:
            fv = []
            i0 = -1
            for i,cc in enumerate(pc):
               if cc in pcoords:
                  # NB this includes 1st and last points
                  i0 = i0+1
                  fv.append(f[i0])
               else:
                  x,y   = cc
                  x0,y0 = pcoords[i0]
                  x1,y1 = pcoords[i0+1]
                  wt    = np.sqrt(pow(x-x0,2)+pow(y-y0,2))/np.sqrt(pow(x1-x0,2)+pow(y1-y0,2))
                  #
                  f0 = f[i0]
                  f1 = f[i0+1]
                  fv.append(f0+wt*(f1-f0))
         #############################################################

         return pc,fv
      #############################################################

      pcoords  = list(coords)
      fvals    = list(fvals)
      pcoords  = [tuple(cc) for cc in pcoords]

      #######################################################
      # check for periodicity
      if (pcoords[0]!=pcoords[-1]):
         # not periodic
         # - BUT need last and first coordinate the same for shapely polygon
         print('not periodic')
         pcoords.append(pcoords[0])
         fvals.append(fvals[0])
      #######################################################

      if do_check:

         #######################################################
         # want to go round curve anti-clockwise
         x,y         = np.array(pcoords).transpose()
         area        = GP.area_polygon_euclidean(x,y)
         self.area   = abs(area)
         if area<0:
            print("Curve traversed in clockwise direction - reversing arrays' order")
            pcoords.reverse()
            fvals.reverse()
         #######################################################

         #############################################################
         #check that there are enough points 
         Nmin  = 40
         while len(pcoords)<Nmin:
            #double no of points
            pcoords,fvals  = do_fill(pcoords,f=fvals)
         #############################################################

         #############################################################
         #check the points are evenly spaced
         spc            = GP.curve_info(pcoords)[2]
         res            = np.mean(spc)
         pcoords,fvals  = do_fill(pcoords,res=res,f=fvals)
         #######################################################
         

      #######################################################
      # make shapely polygon
      # (coords now has end-point repeated,
      #	self.coords doesn't)
      self.shapely_polygon = shgeom.Polygon(pcoords)
      self.coords          = pcoords[:-1]
      self.func_vals       = np.array(fvals[:-1])
      #######################################################


      #######################################################
      # gets spacings,directions between points, and perimeter
      self.number_of_points   = len(self.coords)
      self.perimeter,self.resolution,\
      	 self.spacings,self.tangent_dirn  = GP.curve_info(self.coords)
      #######################################################

      #######################################################
      # make rtree index
      idx   = Rindex.Index()
      for i,(xp,yp) in enumerate(self.coords):
         idx.insert(i,(xp,yp,xp,yp)) # a point is a rectangle of zero side-length
      self.coord_index	= idx
      #######################################################

      return
Ejemplo n.º 4
0
   def _check_singularities(self):
      # don't want too many singularities
      import numpy as np
      import geometry_planar	as GP
      
      N0 = self.number_of_points
      N1 = self.number_of_singularities
      
      # set limits for N1
      Nthresh  = 100
      frac     = 1.3
      # frac   = .2
      
      # if self.solve_exactly:
      #		# number of singularities and number of boundary points should be the same
      #		# - this doesn't work too well - need more sing's
      #		Ntarget	= N0
      if N0 <= Nthresh:
         # if N0<=Nthresh, try to get N1~N0
         Ntarget  = N0+30
      else:
         # try to get N1~frac*N0, if frac*N0<Nthresh
         Ntarget	= int(np.max([np.round(frac*N0),Nthresh]))
      
      print('\nChecking singularities...\n')
      print('Number of boundary points	     : '+str(N0))
      print('Number of singularities         : '+str(N1))
      print('Desired number of singularities : '+str(Ntarget))
      
      check_again = False
      # NB this applies to the case where N1==Ntarget
      # NB also if N1>=Ntarget, we can reduce N1 to Ntarget exactly in 1 go
      
      if N1>Ntarget:
      
         ##########################################################################
         # reduce the number of sing's by increasing spacing between points
         coords	= self.singularities
         perimeter,resolution,\
               spacings,tangent_dirn   = GP.curve_info(coords)
         #
         s_target    = N1/float(Ntarget)*resolution # increase mean spacing between points
         new_coords  = [coords[0]]
         #
         ss = 0
         s0 = 0
         s1 = s0+s_target
         for n,c0 in enumerate(coords[1:]):
            ss = ss+spacings[n]
            if ss>=s1:
               new_coords.append(c0)
               s0 = s1
               s1 = s0+s_target
         
         #######################################################################
         # update list of singularities:
         N2                            = len(new_coords)
         self.singularities            = new_coords
         self.number_of_singularities  = N2
         #######################################################################
         
         if N2>Ntarget:

            #######################################################################
            # delete a few points randomly from new_coords to get right number
            Ndel  = N2-Ntarget
            while Ndel>0:
               idel	= np.random.randint(N2)
               new_coords.remove(new_coords[idel])
               N2       = len(new_coords)
               Ndel     = N2-Ntarget
            
            # update list of singularities
            self.singularities            = new_coords
            self.number_of_singularities  = N2
            #######################################################################
         
         ##########################################################################
         elif N2<Ntarget:

            #######################################################################
            # get some more points from self.singularities
            # *this adds more at start preferentially
            # but shouldn't matter since it won't be too many
            Nadd	= Ntarget-N2
            iadd	= 0
            
            for coord in self.singularities:
               if coord in new_coords:
               	  # in there so now need to insert before next element of new_coords
               	  iadd	= iadd+1
               else:
               	  # adds coord before new_coords[iadd]
               	  new_coords  = list(new_coords)
               	  new_coords.insert(iadd,coord)
               	  N2    = len(new_coords)
               	  Nadd  = Ntarget-N2

               	  # new element so still need to increase point at which to place 
               	  iadd	= iadd+1

               if Nadd==0:
                  break

            # update list of singularities
            self.singularities            = new_coords
            self.number_of_singularities  = N2
            #######################################################################

         ##########################################################################
         print('New number of singularities		: '+str(Ntarget)+'\n')
         ##########################################################################

      elif N1<Ntarget:

         ##########################################################################
         # set up for another iteration
         if 0:
            # increase buffer_resolution
            # - not so good since this only adds more points round corners
            fac                     = int(np.ceil(1.2*Ntarget/float(N1)))
            self.buffer_resolution  = fac*self.buffer_resolution
         else:
            import MIZchar as mizc
            xs,ys = np.array(self.singularities).transpose()

            # double the number of sings
            # TODO this may not be the best way
            # - perhaps specify resolution
            xs,ys = mizc.fill_poly(xs,ys)
            xys   = np.array([xs,ys]).transpose()
            #
            self.singularities            = [tuple(xyi) for xyi in xys]
            self.number_of_singularities  = len(xys)
         ##########################################################################

         # need to call _get_singularities again,
         # to reset the singularities and check them
         check_again = True
         print('Trying again to get singularities (too few)...\n')
         ##########################################################################

      return check_again
	def __init__(self,coords,func_vals,singularities=None):
			# initialise object

			import numpy as np
			import shapely.geometry as shgeom # http://toblerity.org/shapely/manual.html
			import geometry_planar	as GP 	  # also in py_funs
			import rtree.index	as Rindex

			#######################################################
			self.func_vals = np.array(func_vals)
			self.coords		= 1*coords # no longer pointer to list from outside the function
			pcoords				= 1*coords

			if (pcoords[0][0]!=pcoords[-1][0]) and (pcoords[0][1]!=pcoords[-1][1]):
				# not periodic
				# - BUT need last and first coordinate the same for shapely polygon
				print('not periodic')
				pcoords.append(pcoords[0])
			#######################################################

			#######################################################
			# make shapely polygon
			# (coords now has end-point repeated,
			#	self.coords doesn't)
			self.shapely_polygon	= shgeom.Polygon(pcoords)
			#######################################################
			
			#######################################################
			# want to go round curve anti-clockwise
			x,y				= GP.coords2xy(self.coords)
			area				= GP.area_polygon_euclidean(x,y)
			self.area	= abs(area)

			if area<0:
				print("Curve traversed in clockwise direction - reversing arrays' order")
				self.func_vals = list(self.func_vals)
				self.func_vals.reverse()
				self.func_vals = np.array(self.func_vals)
				#
				self.coords		= list(self.coords)
				self.coords.reverse()
				self.coords		= self.coords

			# make rtree index
			idx	= Rindex.Index()
			for i,(xp,yp) in enumerate(self.coords):
				idx.insert(i,(xp,yp,xp,yp)) # a point is a rectangle of zero side-length
			self.coord_index	= idx
			#######################################################

			self.number_of_points	= len(self.coords)

			# gets spacings,directions between points, and perimeter
			self.perimeter,self.resolution,\
						self.spacings,self.tangent_dirn	= GP.curve_info(self.coords)
			#

			# get points around boundary of polygon, then
			# expand points by an amount related to the spacings of the coords
			self.buffer_resolution	= 16 # default buffer resolution
																	# (number of segments used to approximate a quarter circle around a point.)

			# self.solve_exactly	= solve_exactly
			# # if True: number of singularities and number of boundary points should be the same

			if singularities is None:
				get_sings = True
			else:
				get_sings		       = False
				self.singularities	       = singularities 
				self.number_of_singularities   = len(self.singularities)
				print('Number of boundary points : '+str(self.number_of_points))
				print('Number of singularities	: '+str(self.number_of_singularities)+'\n')

			while get_sings:
				# May need a couple of repetitions if too few singularities are found
				get_sings	= self._get_singularities()

			# solve Laplace's eqn (get a_n)
			self._solve_laplace_eqn()

			# # evaluate error on boundary:
			print('\nCalculating error on the boundary...')
			self._eval_solution_boundary()
			print(str(self.boundary_error)+'\n')

			if 0:
				# evaluate normal derivative -> stream function on boundary:
				print('\nCalculating normal derivative -> stream function on the boundary...\n')
				self._eval_derivs_boundary()

			elif 1:
				# use analytical definition of stream function
				# - for log, this is arctan2 (+const)
				self._get_stream_func_bdy()

			return