@@ -299,9 +299,6 @@ def _update_trajectory(self, xm, ym):
299299class InvalidIndexError (Exception ):
300300 pass
301301
302- class OutOfBounds (Exception ):
303- pass
304-
305302
306303# Integrator definitions
307304#========================
@@ -401,66 +398,67 @@ def _rk12(x0, y0, dmap, f):
401398 xf_traj = []
402399 yf_traj = []
403400
404- try :
405- while dmap .grid .valid_index (xi , yi ):
406- xf_traj .append (xi )
407- yf_traj .append (yi )
408- try :
409- k1x , k1y = f (xi , yi )
410- k2x , k2y = f (xi + ds * k1x ,
411- yi + ds * k1y )
412- except IndexError :
413- # Out of the domain on one of the intermediate steps
414- raise OutOfBounds
415-
416- dx1 = ds * k1x
417- dy1 = ds * k1y
418- dx2 = ds * 0.5 * (k1x + k2x )
419- dy2 = ds * 0.5 * (k1y + k2y )
420-
421- nx , ny = dmap .grid .shape
422- # Error is normalized to the axes coordinates
423- error = np .sqrt (((dx2 - dx1 )/ nx )** 2 + ((dy2 - dy1 )/ ny )** 2 )
424-
425- # Only save step if within error tolerance
426- if error < maxerror :
427- xi += dx2
428- yi += dy2
429- try :
430- dmap .update_trajectory (xi , yi )
431- except InvalidIndexError :
432- break
433- if (stotal + ds ) > 2 :
434- break
435- stotal += ds
436-
437- # recalculate stepsize based on step error
438- ds = min (maxds , 0.85 * ds * (maxerror / error )** 0.2 )
439-
440- except OutOfBounds :
441- ## This is raised when the edge of the domain is
442- ## reached. Here, a simple Euler integration is used up to the
443- ## boundary of the domain, improving the neatness of the
444- ## figure.
401+ while dmap .grid .valid_index (xi , yi ):
402+ xf_traj .append (xi )
403+ yf_traj .append (yi )
404+ try :
405+ k1x , k1y = f (xi , yi )
406+ k2x , k2y = f (xi + ds * k1x ,
407+ yi + ds * k1y )
408+ except IndexError :
409+ # Out of the domain on one of the intermediate integration steps.
410+ # Take an Euler step to the boundary to improve neatness.
411+ ds , xf_traj , yf_traj = _euler_step (xf_traj , yf_traj , dmap , f )
412+ stotal += ds
413+ break
414+
415+ dx1 = ds * k1x
416+ dy1 = ds * k1y
417+ dx2 = ds * 0.5 * (k1x + k2x )
418+ dy2 = ds * 0.5 * (k1y + k2y )
419+
445420 nx , ny = dmap .grid .shape
446- xi = xf_traj [- 1 ] # in data coordinates
447- yi = yf_traj [- 1 ]
448- cx , cy = f (xi , yi ) # ds.cx is in data coordinates, ds in axis coord.
449- if cx > 0 :
450- dsx = (nx - 1 - xi ) / cx
451- else :
452- dsx = xi / - cx
453- if cy > 0 :
454- dsy = (ny - 1 - yi ) / cy
455- else :
456- dsy = yi / - cy
457- ds = min (dsx , dsy )
458- xf_traj .append (xi + cx * ds )
459- yf_traj .append (yi + cy * ds )
460- stotal += ds
421+ # Error is normalized to the axes coordinates
422+ error = np .sqrt (((dx2 - dx1 )/ nx )** 2 + ((dy2 - dy1 )/ ny )** 2 )
423+
424+ # Only save step if within error tolerance
425+ if error < maxerror :
426+ xi += dx2
427+ yi += dy2
428+ try :
429+ dmap .update_trajectory (xi , yi )
430+ except InvalidIndexError :
431+ break
432+ if (stotal + ds ) > 2 :
433+ break
434+ stotal += ds
435+
436+ # recalculate stepsize based on step error
437+ ds = min (maxds , 0.85 * ds * (maxerror / error )** 0.2 )
461438
462439 return stotal , xf_traj , yf_traj
463440
441+
442+ def _euler_step (xf_traj , yf_traj , dmap , f ):
443+ """Simple Euler integration step."""
444+ nx , ny = dmap .grid .shape
445+ xi = xf_traj [- 1 ]
446+ yi = yf_traj [- 1 ]
447+ cx , cy = f (xi , yi ) # ds.cx is in data coordinates, ds in axis coord.
448+ if cx > 0 :
449+ dsx = (nx - 1 - xi ) / cx
450+ else :
451+ dsx = xi / - cx
452+ if cy > 0 :
453+ dsy = (ny - 1 - yi ) / cy
454+ else :
455+ dsy = yi / - cy
456+ ds = min (dsx , dsy )
457+ xf_traj .append (xi + cx * ds )
458+ yf_traj .append (yi + cy * ds )
459+ return ds , xf_traj , yf_traj
460+
461+
464462def _rk4 (x0 , y0 , dmap , f ):
465463 """4th-order Runge-Kutta algorithm with fixed step size"""
466464 ds = min (1. / dmap .mask .nx , 1. / dmap .mask .ny , 0.01 )
0 commit comments