This is an archival dump of old wiki content --- see scipy.org for current material

Attachment 'laplace.py'

Download

 1 #!/usr/bin/env python
 2 
 3 """
 4 This script compares different ways of implementing an iterative
 5 procedure to solve Laplace's equation. These provide a general
 6 guideline to using Python for high-performance computing and also
 7 provide a simple means to compare the computational time taken by the
 8 different approaches. The script compares functions implemented in
 9 pure Python, Numeric, weave.blitz, weave.inline, fortran (via f2py)
 10 and Pyrex. The function main(), additionally accelerates the pure
 11 Python version using Psyco and provides some numbers on how well that
 12 works. To compare all the options you need to have Numeric, weave,
 13 f2py, Pyrex and Psyco installed. If Psyco is not installed the script
 14 will print a warning but will perform all other tests.
 15 
 16 The fortran and pyrex modules are compiled using the setup.py script
 17 that is provided with this file. You can build them like so:
 18 
 19  python setup.py build_ext --inplace
 20 
 21 
 22 Author: Prabhu Ramachandran <prabhu_r at users dot sf dot net>
 23 License: BSD
 24 Last modified: Sep. 18, 2004
 25 """
 26 
 27 import numpy
 28 from scipy import weave
 29 from scipy.weave import converters
 30 import sys, time
 31 
 32 msg = """**************************************************
 33 Please build the fortran and Pyrex modules like so:
 34 
 35  python setup.py build_ext --inplace
 36 
 37 You will require f2py and Pyrex.
 38 **************************************************
 39 """
 40 build = 0
 41 try:
 42  import flaplace
 43 except ImportError:
 44  build = 1
 45 try:
 46  import pyx_lap
 47 except ImportError:
 48  build = 1
 49 if build:
 50  print msg
 51 
 52 
 53 class Grid:
 54  
 55  """A simple grid class that stores the details and solution of the
 56  computational grid."""
 57  
 58  def __init__(self, nx=10, ny=10, xmin=0.0, xmax=1.0,
 59  ymin=0.0, ymax=1.0):
 60  self.xmin, self.xmax, self.ymin, self.ymax = xmin, xmax, ymin, ymax
 61  self.dx = float(xmax-xmin)/(nx-1)
 62  self.dy = float(ymax-ymin)/(ny-1)
 63  self.u = numpy.zeros((nx, ny), 'd')
 64  # used to compute the change in solution in some of the methods.
 65  self.old_u = self.u.copy() 
 66 
 67  def setBC(self, l, r, b, t): 
 68  """Sets the boundary condition given the left, right, bottom
 69  and top values (or arrays)""" 
 70  self.u[0, :] = l
 71  self.u[-1, :] = r
 72  self.u[:, 0] = b
 73  self.u[:,-1] = t
 74  self.old_u = self.u.copy()
 75 
 76  def setBCFunc(self, func):
 77  """Sets the BC given a function of two variables."""
 78  xmin, ymin = self.xmin, self.ymin
 79  xmax, ymax = self.xmax, self.ymax
 80  x = numpy.arange(xmin, xmax + self.dx*0.5, self.dx)
 81  y = numpy.arange(ymin, ymax + self.dy*0.5, self.dy)
 82  self.u[0 ,:] = func(xmin,y)
 83  self.u[-1,:] = func(xmax,y)
 84  self.u[:, 0] = func(x,ymin)
 85  self.u[:,-1] = func(x,ymax)
 86 
 87  def computeError(self): 
 88  """Computes absolute error using an L2 norm for the solution.
 89  This requires that self.u and self.old_u must be appropriately
 90  setup.""" 
 91  v = (self.u - self.old_u).flat
 92  return numpy.sqrt(numpy.dot(v,v))
 93  
 94 
 95 class LaplaceSolver:
 96  
 97  """A simple Laplacian solver that can use different schemes to
 98  solve the problem."""
 99  
 100  def __init__(self, grid, stepper='numeric'):
 101  self.grid = grid
 102  self.setTimeStepper(stepper)
 103 
 104  def slowTimeStep(self, dt=0.0):
 105  """Takes a time step using straight forward Python loops."""
 106  g = self.grid
 107  nx, ny = g.u.shape 
 108  dx2, dy2 = g.dx**2, g.dy**2
 109  dnr_inv = 0.5/(dx2 + dy2)
 110  u = g.u
 111 
 112  err = 0.0
 113  for i in range(1, nx-1):
 114  for j in range(1, ny-1):
 115  tmp = u[i,j]
 116  u[i,j] = ((u[i-1, j] + u[i+1, j])*dy2 +
 117  (u[i, j-1] + u[i, j+1])*dx2)*dnr_inv
 118  diff = u[i,j] - tmp
 119  err += diff*diff
 120 
 121  return numpy.sqrt(err)
 122  
 123  def numericTimeStep(self, dt=0.0):
 124  """Takes a time step using a numeric expressions."""
 125  g = self.grid
 126  dx2, dy2 = g.dx**2, g.dy**2
 127  dnr_inv = 0.5/(dx2 + dy2)
 128  u = g.u
 129  g.old_u = u.copy()
 130 
 131  # The actual iteration
 132  u[1:-1, 1:-1] = ((u[0:-2, 1:-1] + u[2:, 1:-1])*dy2 + 
 133  (u[1:-1,0:-2] + u[1:-1, 2:])*dx2)*dnr_inv
 134  
 135  return g.computeError()
 136 
 137  def blitzTimeStep(self, dt=0.0): 
 138  """Takes a time step using a numeric expression that has been
 139  blitzed using weave.""" 
 140  g = self.grid
 141  dx2, dy2 = g.dx**2, g.dy**2
 142  dnr_inv = 0.5/(dx2 + dy2)
 143  u = g.u
 144  g.old_u = u.copy()
 145 
 146  # The actual iteration
 147  expr = "u[1:-1, 1:-1] = ((u[0:-2, 1:-1] + u[2:, 1:-1])*dy2 + "\
 148  "(u[1:-1,0:-2] + u[1:-1, 2:])*dx2)*dnr_inv"
 149  weave.blitz(expr, check_size=0)
 150 
 151  return g.computeError()
 152 
 153  def inlineTimeStep(self, dt=0.0): 
 154  """Takes a time step using inlined C code -- this version uses
 155  blitz arrays.""" 
 156  g = self.grid
 157  nx, ny = g.u.shape
 158  dx2, dy2 = g.dx**2, g.dy**2
 159  dnr_inv = 0.5/(dx2 + dy2)
 160  u = g.u
 161  
 162  code = """
 163  #line 120 "laplace.py"
 164  double tmp, err, diff;
 165  err = 0.0;
 166  for (int i=1; i<nx-1; ++i) {
 167  for (int j=1; j<ny-1; ++j) {
 168  tmp = u(i,j);
 169  u(i,j) = ((u(i-1,j) + u(i+1,j))*dy2 +
 170  (u(i,j-1) + u(i,j+1))*dx2)*dnr_inv;
 171  diff = u(i,j) - tmp;
 172  err += diff*diff;
 173  }
 174  }
 175  return_val = sqrt(err);
 176 """
 177  # compiler keyword only needed on windows with MSVC installed
 178  err = weave.inline(code,
 179  ['u', 'dx2', 'dy2', 'dnr_inv', 'nx','ny'],
 180  type_converters = converters.blitz,
 181  compiler = 'gcc')
 182  return err
 183 
 184  def fastInlineTimeStep(self, dt=0.0):
 185  """Takes a time step using inlined C code -- this version is
 186  faster, dirtier and manipulates the numeric array in C. This
 187  code was contributed by Eric Jones. """
 188  g = self.grid
 189  nx, ny = g.u.shape
 190  dx2, dy2 = g.dx**2, g.dy**2
 191  dnr_inv = 0.5/(dx2 + dy2)
 192  u = g.u
 193  
 194  code = """
 195  #line 151 "laplace.py"
 196  double tmp, err, diff;
 197  double *uc, *uu, *ud, *ul, *ur;
 198  err = 0.0;
 199  for (int i=1; i<nx-1; ++i) {
 200  uc = u+i*ny+1;
 201  ur = u+i*ny+2; ul = u+i*ny;
 202  ud = u+(i+1)*ny+1; uu = u+(i-1)*ny+1;
 203  for (int j=1; j<ny-1; ++j) {
 204  tmp = *uc;
 205  *uc = ((*ul + *ur)*dy2 +
 206  (*uu + *ud)*dx2)*dnr_inv;
 207  diff = *uc - tmp;
 208  err += diff*diff;
 209  uc++;ur++;ul++;ud++;uu++;
 210  }
 211  }
 212  return_val = sqrt(err);
 213 """
 214  # compiler keyword only needed on windows with MSVC installed
 215  err = weave.inline(code,
 216  ['u', 'dx2', 'dy2', 'dnr_inv', 'nx','ny'],
 217  compiler='gcc')
 218  return err
 219 
 220  def fortranTimeStep(self, dt=0.0):
 221  """Takes a time step using a simple fortran module that
 222  implements the loop in fortran. Use f2py to compile
 223  flaplace.f like so: f2py -c flaplace.c -m flaplace. You need
 224  the latest f2py version for this to work. This Fortran
 225  example was contributed by Pearu Peterson. """
 226  g = self.grid
 227  g.u, err = flaplace.timestep(g.u, g.dx, g.dy)
 228  return err
 229 
 230  def pyrexTimeStep(self, dt=0.0):
 231  """Takes a time step using a function written in Pyrex. Use
 232  the given setup.py to build the extension using the command
 233  python setup.py build_ext --inplace. You will need Pyrex
 234  installed to run this.""" 
 235  g = self.grid
 236  err = pyx_lap.pyrexTimeStep(g.u, g.dx, g.dy)
 237  return err
 238 
 239  def setTimeStepper(self, stepper='numeric'): 
 240  """Sets the time step scheme to be used while solving given a
 241  string which should be one of ['slow', 'numeric', 'blitz',
 242  'inline', 'fastinline', 'fortran'].""" 
 243  if stepper == 'slow':
 244  self.timeStep = self.slowTimeStep
 245  elif stepper == 'numeric':
 246  self.timeStep = self.numericTimeStep
 247  elif stepper == 'blitz':
 248  self.timeStep = self.blitzTimeStep
 249  elif stepper == 'inline':
 250  self.timeStep = self.inlineTimeStep
 251  elif stepper.lower() == 'fastinline':
 252  self.timeStep = self.fastInlineTimeStep
 253  elif stepper == 'fortran':
 254  self.timeStep = self.fortranTimeStep
 255  elif stepper == 'pyrex':
 256  self.timeStep = self.pyrexTimeStep
 257  else:
 258  self.timeStep = self.numericTimeStep 
 259  
 260  def solve(self, n_iter=0, eps=1.0e-16): 
 261  """Solves the equation given an error precision -- eps. If
 262  n_iter=0 the solving is stopped only on the eps condition. If
 263  n_iter is finite then solution stops in that many iterations
 264  or when the error is less than eps whichever is earlier.
 265  Returns the error if the loop breaks on the n_iter condition
 266  and returns the iterations if the loop breaks on the error
 267  condition.""" 
 268  err = self.timeStep()
 269  count = 1
 270 
 271  while err > eps:
 272  if n_iter and count >= n_iter:
 273  return err
 274  err = self.timeStep()
 275  count = count + 1
 276 
 277  return count
 278 
 279 
 280 def BC(x, y): 
 281  """Used to set the boundary condition for the grid of points.
 282  Change this as you feel fit.""" 
 283  return (x**2 - y**2)
 284 
 285 
 286 def test(nmin=5, nmax=30, dn=5, eps=1.0e-16, n_iter=0, stepper='numeric'):
 287  iters = []
 288  n_grd = numpy.arange(nmin, nmax, dn)
 289  times = []
 290  for i in n_grd:
 291  g = Grid(nx=i, ny=i)
 292  g.setBCFunc(BC)
 293  s = LaplaceSolver(g, stepper)
 294  t1 = time.clock()
 295  iters.append(s.solve(n_iter=n_iter, eps=eps))
 296  dt = time.clock() - t1
 297  times.append(dt)
 298  print "Solution for nx = ny = %d, took %f seconds"%(i, dt)
 299  return (n_grd**2, iters, times)
 300 
 301 
 302 def time_test(nx=500, ny=500, eps=1.0e-16, n_iter=100, stepper='numeric'):
 303  g = Grid(nx, ny)
 304  g.setBCFunc(BC)
 305  s = LaplaceSolver(g, stepper)
 306  t = time.clock()
 307  s.solve(n_iter=n_iter, eps=eps)
 308  return time.clock() - t
 309  
 310 
 311 def main(n=500, n_iter=100):
 312  print "Doing %d iterations on a %dx%d grid"%(n_iter, n, n)
 313  for i in ['numeric', 'blitz', 'inline', 'fastinline', 'fortran',
 314  'pyrex']:
 315  print i,
 316  sys.stdout.flush()
 317  print "took", time_test(n, n, stepper=i, n_iter=n_iter), "seconds"
 318 
 319  print "slow (1 iteration)",
 320  sys.stdout.flush()
 321  s = time_test(n, n, stepper='slow', n_iter=1)
 322  print "took", s, "seconds"
 323  print "%d iterations should take about %f seconds"%(n_iter, s*n_iter)
 324 
 325  try:
 326  import psyco
 327  except ImportError:
 328  print "You don't have Psyco installed!"
 329  else:
 330  psyco.bind(LaplaceSolver)
 331  psyco.bind(Grid)
 332  print "slow with Psyco (1 iteration)",
 333  sys.stdout.flush()
 334  s = time_test(n, n, stepper='slow', n_iter=1)
 335  print "took", s, "seconds"
 336  print "%d iterations should take about %f seconds"%\
 337  (n_iter, s*n_iter)
 338 
 339 
 340 if __name__ == "__main__":
 341  main()

New Attachment

File to upload
Rename to
Overwrite existing attachment of same name

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2007年04月18日 09:25:14, 0.8 KB) [[attachment:laplace.m]]
  • [get | view] (2006年03月26日 18:39:53, 11.5 KB) [[attachment:laplace.py]]
  • [get | view] (2006年03月26日 18:39:24, 10.8 KB) [[attachment:perfpy.tgz]]
  • [get | view] (2009年11月20日 11:08:51, 12.8 KB) [[attachment:perfpy_2.tgz]]
  • [get | view] (2009年11月20日 11:07:56, 12.8 KB) [[attachment:perfy_2.tgz]]

AltStyle によって変換されたページ (->オリジナル) /