예제 #1
0
파일: rng.py 프로젝트: dham/parcels
 def lib(self, compiler=GNUCompiler()):
     if self._lib is None:
         with open(self.src_file, 'w') as f:
             f.write(self.ccode)
         compiler.compile(self.src_file, self.lib_file, self.log_file)
         print("Compiled %s ==> %s" % ("random", self.lib_file))
         self._lib = npct.load_library(self.lib_file, '.')
     return self._lib
예제 #2
0
    def execute(self,
                pyfunc=AdvectionRK4,
                endtime=None,
                runtime=None,
                dt=1.,
                moviedt=None,
                recovery=None,
                output_file=None,
                movie_background_field=None,
                verbose_progress=None):
        """Execute a given kernel function over the particle set for
        multiple timesteps. Optionally also provide sub-timestepping
        for particle output.

        :param pyfunc: Kernel function to execute. This can be the name of a
                       defined Python function or a :class:`parcels.kernel.Kernel` object.
                       Kernels can be concatenated using the + operator
        :param endtime: End time for the timestepping loop.
                        It is either a datetime object or a positive double.
        :param runtime: Length of the timestepping loop. Use instead of endtime.
                        It is either a timedelta object or a positive double.
        :param dt: Timestep interval to be passed to the kernel.
                   It is either a timedelta object or a double.
                   Use a negative value for a backward-in-time simulation.
        :param moviedt:  Interval for inner sub-timestepping (leap), which dictates
                         the update frequency of animation.
                         It is either a timedelta object or a positive double.
                         None value means no animation.
        :param output_file: :mod:`parcels.particlefile.ParticleFile` object for particle output
        :param recovery: Dictionary with additional `:mod:parcels.tools.error`
                         recovery kernels to allow custom recovery behaviour in case of
                         kernel errors.
        :param movie_background_field: field plotted as background in the movie if moviedt is set.
                                       'vector' shows the velocity as a vector field.
        :param verbose_progress: Boolean for providing a progress bar for the kernel execution loop.

        """

        # check if pyfunc has changed since last compile. If so, recompile
        if self.kernel is None or (self.kernel.pyfunc is not pyfunc
                                   and self.kernel is not pyfunc):
            # Generate and store Kernel
            if isinstance(pyfunc, Kernel):
                self.kernel = pyfunc
            else:
                self.kernel = self.Kernel(pyfunc)
            # Prepare JIT kernel execution
            if self.ptype.uses_jit:
                self.kernel.remove_lib()
                self.kernel.compile(compiler=GNUCompiler())
                self.kernel.load_lib()

        # Convert all time variables to seconds
        if isinstance(endtime, delta):
            raise RuntimeError('endtime must be either a datetime or a double')
        if isinstance(endtime, datetime):
            endtime = np.datetime64(endtime)
        if isinstance(endtime, np.datetime64):
            if self.time_origin.calender is None:
                raise NotImplementedError(
                    'If fieldset.time_origin is not a date, execution endtime must be a double'
                )
            endtime = self.time_origin.reltime(endtime)
        if isinstance(runtime, delta):
            runtime = runtime.total_seconds()
        if isinstance(dt, delta):
            dt = dt.total_seconds()
        outputdt = output_file.outputdt if output_file else np.infty
        if isinstance(outputdt, delta):
            outputdt = outputdt.total_seconds()
        if isinstance(moviedt, delta):
            moviedt = moviedt.total_seconds()

        assert runtime is None or runtime >= 0, 'runtime must be positive'
        assert outputdt is None or outputdt >= 0, 'outputdt must be positive'
        assert moviedt is None or moviedt >= 0, 'moviedt must be positive'

        # Set particle.time defaults based on sign of dt, if not set at ParticleSet construction
        for p in self:
            if np.isnan(p.time):
                mintime, maxtime = self.fieldset.gridset.dimrange('time_full')
                p.time = mintime if dt >= 0 else maxtime

        # Derive _starttime and endtime from arguments or fieldset defaults
        if runtime is not None and endtime is not None:
            raise RuntimeError(
                'Only one of (endtime, runtime) can be specified')
        _starttime = min([p.time for p in self]) if dt >= 0 else max(
            [p.time for p in self])
        if self.repeatdt is not None and self.repeat_starttime is None:
            self.repeat_starttime = _starttime
        if runtime is not None:
            endtime = _starttime + runtime * np.sign(dt)
        elif endtime is None:
            mintime, maxtime = self.fieldset.gridset.dimrange('time_full')
            endtime = maxtime if dt >= 0 else mintime

        if abs(endtime - _starttime) < 1e-5 or dt == 0 or runtime == 0:
            dt = 0
            runtime = 0
            endtime = _starttime
            logger.warning_once(
                "dt or runtime are zero, or endtime is equal to Particle.time. "
                "The kernels will be executed once, without incrementing time")

        # Initialise particle timestepping
        for p in self:
            p.dt = dt

        # First write output_file, because particles could have been added
        if output_file:
            output_file.write(self, _starttime)
        if moviedt:
            self.show(field=movie_background_field,
                      show_time=_starttime,
                      animation=True)

        if moviedt is None:
            moviedt = np.infty
        time = _starttime
        if self.repeatdt:
            next_prelease = self.repeat_starttime + (
                abs(time - self.repeat_starttime) // self.repeatdt +
                1) * self.repeatdt * np.sign(dt)
        else:
            next_prelease = np.infty if dt > 0 else -np.infty
        next_output = time + outputdt if dt > 0 else time - outputdt
        next_movie = time + moviedt if dt > 0 else time - moviedt
        next_input = self.fieldset.computeTimeChunk(time, np.sign(dt))

        tol = 1e-12
        if verbose_progress is None:
            walltime_start = time_module.time()
        if verbose_progress:
            try:
                pbar = progressbar.ProgressBar(
                    max_value=abs(endtime - _starttime)).start()
            except:  # for old versions of progressbar
                pbar = progressbar.ProgressBar(
                    maxvalue=abs(endtime - _starttime)).start()
        while (time < endtime and dt > 0) or (time > endtime
                                              and dt < 0) or dt == 0:
            if verbose_progress is None and time_module.time(
            ) - walltime_start > 10:
                # Showing progressbar if runtime > 10 seconds
                pbar = progressbar.ProgressBar(
                    max_value=abs(endtime - _starttime)).start()
                verbose_progress = True
            if dt > 0:
                time = min(next_prelease, next_input, next_output, next_movie,
                           endtime)
            else:
                time = max(next_prelease, next_input, next_output, next_movie,
                           endtime)
            self.kernel.execute(self,
                                endtime=time,
                                dt=dt,
                                recovery=recovery,
                                output_file=output_file)
            if abs(time - next_prelease) < tol:
                pset_new = ParticleSet(fieldset=self.fieldset,
                                       time=time,
                                       lon=self.repeatlon,
                                       lat=self.repeatlat,
                                       depth=self.repeatdepth,
                                       pclass=self.repeatpclass)
                for p in pset_new:
                    p.dt = dt
                self.add(pset_new)
                next_prelease += self.repeatdt * np.sign(dt)
            if abs(time - next_output) < tol:
                if output_file:
                    output_file.write(self, time)
                next_output += outputdt * np.sign(dt)
            if abs(time - next_movie) < tol:
                self.show(field=movie_background_field,
                          show_time=time,
                          animation=True)
                next_movie += moviedt * np.sign(dt)
            next_input = self.fieldset.computeTimeChunk(time, dt)
            if dt == 0:
                break
            if verbose_progress:
                pbar.update(abs(time - _starttime))

        if output_file:
            output_file.write(self, time)
        if verbose_progress:
            pbar.finish()
    def execute(self,
                pyfunc=AdvectionRK4,
                endtime=None,
                runtime=None,
                dt=1.,
                moviedt=None,
                recovery=None,
                output_file=None,
                movie_background_field=None,
                verbose_progress=None,
                postIterationCallbacks=None,
                callbackdt=None):
        """Execute a given kernel function over the particle set for
        multiple timesteps. Optionally also provide sub-timestepping
        for particle output.

        :param pyfunc: Kernel function to execute. This can be the name of a
                       defined Python function or a :class:`parcels.kernel.Kernel` object.
                       Kernels can be concatenated using the + operator
        :param endtime: End time for the timestepping loop.
                        It is either a datetime object or a positive double.
        :param runtime: Length of the timestepping loop. Use instead of endtime.
                        It is either a timedelta object or a positive double.
        :param dt: Timestep interval to be passed to the kernel.
                   It is either a timedelta object or a double.
                   Use a negative value for a backward-in-time simulation.
        :param moviedt:  Interval for inner sub-timestepping (leap), which dictates
                         the update frequency of animation.
                         It is either a timedelta object or a positive double.
                         None value means no animation.
        :param output_file: :mod:`parcels.particlefile.ParticleFile` object for particle output
        :param recovery: Dictionary with additional `:mod:parcels.tools.error`
                         recovery kernels to allow custom recovery behaviour in case of
                         kernel errors.
        :param movie_background_field: field plotted as background in the movie if moviedt is set.
                                       'vector' shows the velocity as a vector field.
        :param verbose_progress: Boolean for providing a progress bar for the kernel execution loop.
        :param postIterationCallbacks: (Optional) Array of functions that are to be called after each iteration (post-process, non-Kernel)
        :param callbackdt: (Optional, in conjecture with 'postIterationCallbacks) timestep inverval to (latestly) interrupt the running kernel and invoke post-iteration callbacks from 'postIterationCallbacks'
        """

        # check if pyfunc has changed since last compile. If so, recompile
        if self.kernel is None or (self.kernel.pyfunc is not pyfunc
                                   and self.kernel is not pyfunc):
            # Generate and store Kernel
            if isinstance(pyfunc, Kernel):
                self.kernel = pyfunc
            else:
                self.kernel = self.Kernel(pyfunc)
            # Prepare JIT kernel execution
            if self.ptype.uses_jit:
                self.kernel.remove_lib()
                cppargs = ['-DDOUBLE_COORD_VARIABLES'
                           ] if self.lonlatdepth_dtype == np.float64 else None
                self.kernel.compile(compiler=GNUCompiler(cppargs=cppargs))
                self.kernel.load_lib()

        # Convert all time variables to seconds
        if isinstance(endtime, delta):
            raise RuntimeError('endtime must be either a datetime or a double')
        if isinstance(endtime, datetime):
            endtime = np.datetime64(endtime)
        if isinstance(endtime, np.datetime64):
            if self.time_origin.calendar is None:
                raise NotImplementedError(
                    'If fieldset.time_origin is not a date, execution endtime must be a double'
                )
            endtime = self.time_origin.reltime(endtime)
        if isinstance(runtime, delta):
            runtime = runtime.total_seconds()
        if isinstance(dt, delta):
            dt = dt.total_seconds()
        outputdt = output_file.outputdt if output_file else np.infty
        if isinstance(outputdt, delta):
            outputdt = outputdt.total_seconds()
        if isinstance(moviedt, delta):
            moviedt = moviedt.total_seconds()
        if isinstance(callbackdt, delta):
            callbackdt = callbackdt.total_seconds()

        assert runtime is None or runtime >= 0, 'runtime must be positive'
        assert outputdt is None or outputdt >= 0, 'outputdt must be positive'
        assert moviedt is None or moviedt >= 0, 'moviedt must be positive'

        mintime, maxtime = self.fieldset.gridset.dimrange(
            'time_full') if self.fieldset is not None else (0, 1)
        if np.any(np.isnan(self.particle_data['time'])):
            self.particle_data['time'][np.isnan(
                self.particle_data['time'])] = mintime if dt >= 0 else maxtime

        # Derive _starttime and endtime from arguments or fieldset defaults
        if runtime is not None and endtime is not None:
            raise RuntimeError(
                'Only one of (endtime, runtime) can be specified')
        _starttime = self.particle_data['time'].min(
        ) if dt >= 0 else self.particle_data['time'].max()
        if self.repeatdt is not None and self.repeat_starttime is None:
            self.repeat_starttime = _starttime
        if runtime is not None:
            endtime = _starttime + runtime * np.sign(dt)
        elif endtime is None:
            mintime, maxtime = self.fieldset.gridset.dimrange('time_full')
            endtime = maxtime if dt >= 0 else mintime

        execute_once = False
        if abs(endtime - _starttime) < 1e-5 or dt == 0 or runtime == 0:
            dt = 0
            runtime = 0
            endtime = _starttime
            logger.warning_once(
                "dt or runtime are zero, or endtime is equal to Particle.time. "
                "The kernels will be executed once, without incrementing time")
            execute_once = True

        self.particle_data['dt'][:] = dt

        # First write output_file, because particles could have been added
        if output_file:
            output_file.write(self, _starttime)
        if moviedt:
            self.show(field=movie_background_field,
                      show_time=_starttime,
                      animation=True)

        if moviedt is None:
            moviedt = np.infty
        if callbackdt is None:
            interupt_dts = [np.infty, moviedt, outputdt]
            if self.repeatdt is not None:
                interupt_dts.append(self.repeatdt)
            callbackdt = np.min(np.array(interupt_dts))
        time = _starttime
        if self.repeatdt:
            next_prelease = self.repeat_starttime + (
                abs(time - self.repeat_starttime) // self.repeatdt +
                1) * self.repeatdt * np.sign(dt)
        else:
            next_prelease = np.infty if dt > 0 else -np.infty
        next_output = time + outputdt if dt > 0 else time - outputdt
        next_movie = time + moviedt if dt > 0 else time - moviedt
        next_callback = time + callbackdt if dt > 0 else time - callbackdt
        next_input = self.fieldset.computeTimeChunk(
            time, np.sign(dt)) if self.fieldset is not None else np.inf

        tol = 1e-12
        if verbose_progress is None:
            walltime_start = time_module.time()
        if verbose_progress:
            pbar = self.__create_progressbar(_starttime, endtime)
        while (time < endtime and dt > 0) or (time > endtime
                                              and dt < 0) or dt == 0:
            if verbose_progress is None and time_module.time(
            ) - walltime_start > 10:
                # Showing progressbar if runtime > 10 seconds
                if output_file:
                    logger.info('Temporary output files are stored in %s.' %
                                output_file.tempwritedir_base)
                    logger.info(
                        'You can use "parcels_convert_npydir_to_netcdf %s" to convert these '
                        'to a NetCDF file during the run.' %
                        output_file.tempwritedir_base)
                pbar = self.__create_progressbar(_starttime, endtime)
                verbose_progress = True
            if dt > 0:
                time = min(next_prelease, next_input, next_output, next_movie,
                           next_callback, endtime)
            else:
                time = max(next_prelease, next_input, next_output, next_movie,
                           next_callback, endtime)
            self.kernel.execute(self,
                                endtime=time,
                                dt=dt,
                                recovery=recovery,
                                output_file=output_file,
                                execute_once=execute_once)
            if abs(time - next_prelease) < tol:
                pset_new = ParticleSet(
                    fieldset=self.fieldset,
                    time=time,
                    lon=self.repeatlon,
                    lat=self.repeatlat,
                    depth=self.repeatdepth,
                    pclass=self.repeatpclass,
                    lonlatdepth_dtype=self.lonlatdepth_dtype,
                    partitions=False,
                    pid_orig=self.repeatpid,
                    **self.repeatkwargs)
                p = pset_new.data_accessor()
                for i in range(pset_new.size):
                    p.set_index(i)
                    p.dt = dt
                self.add(pset_new)
                next_prelease += self.repeatdt * np.sign(dt)
            if abs(time - next_output) < tol:
                if output_file:
                    output_file.write(self, time)
                next_output += outputdt * np.sign(dt)
            if abs(time - next_movie) < tol:
                self.show(field=movie_background_field,
                          show_time=time,
                          animation=True)
                next_movie += moviedt * np.sign(dt)
            # ==== insert post-process here to also allow for memory clean-up via external func ==== #
            if abs(time - next_callback) < tol:
                if postIterationCallbacks is not None:
                    for extFunc in postIterationCallbacks:
                        extFunc()
                next_callback += callbackdt * np.sign(dt)
            if time != endtime:
                next_input = self.fieldset.computeTimeChunk(time, dt)
            if dt == 0:
                break
            if verbose_progress:
                pbar.update(abs(time - _starttime))

        if output_file:
            output_file.write(self, time)
        if verbose_progress:
            pbar.finish()
예제 #4
0
    def execute(self, pyfunc=AdvectionRK4, starttime=None, endtime=None, dt=1.,
                runtime=None, interval=None, recovery=None, output_file=None,
                show_movie=False):
        """Execute a given kernel function over the particle set for
        multiple timesteps. Optionally also provide sub-timestepping
        for particle output.

        :param pyfunc: Kernel function to execute. This can be the name of a
                       defined Python function or a :class:`parcels.kernel.Kernel` object.
                       Kernels can be concatenated using the + operator
        :param starttime: Starting time for the timestepping loop. Defaults to 0.0.
        :param endtime: End time for the timestepping loop
        :param runtime: Length of the timestepping loop. Use instead of endtime.
        :param dt: Timestep interval to be passed to the kernel
        :param interval: Interval for inner sub-timestepping (leap), which dictates
                         the update frequency of file output and animation.
        :param output_file: :mod:`parcels.particlefile.ParticleFile` object for particle output
        :param recovery: Dictionary with additional `:mod:parcels.kernels.error`
                         recovery kernels to allow custom recovery behaviour in case of
                         kernel errors.
        :param show_movie: True shows particles; name of field plots that field as background
        """

        # check if pyfunc has changed since last compile. If so, recompile
        if self.kernel is None or (self.kernel.pyfunc is not pyfunc and self.kernel is not pyfunc):
            # Generate and store Kernel
            if isinstance(pyfunc, Kernel):
                self.kernel = pyfunc
            else:
                self.kernel = self.Kernel(pyfunc)
            # Prepare JIT kernel execution
            if self.ptype.uses_jit:
                self.kernel.remove_lib()
                self.kernel.compile(compiler=GNUCompiler())
                self.kernel.load_lib()

        # Convert all time variables to seconds
        if isinstance(starttime, delta):
            starttime = starttime.total_seconds()
        if isinstance(endtime, delta):
            endtime = endtime.total_seconds()
        if isinstance(runtime, delta):
            runtime = runtime.total_seconds()
        if isinstance(dt, delta):
            dt = dt.total_seconds()
        if isinstance(interval, delta):
            interval = interval.total_seconds()
        if isinstance(starttime, datetime):
            starttime = (starttime - self.time_origin).total_seconds()
        if isinstance(endtime, datetime):
            endtime = (endtime - self.time_origin).total_seconds()

        # Derive starttime, endtime and interval from arguments or fieldset defaults
        if runtime is not None and endtime is not None:
            raise RuntimeError('Only one of (endtime, runtime) can be specified')
        if starttime is None:
            starttime = self.fieldset.U.time[0] if dt > 0 else self.fieldset.U.time[-1]
        if runtime is not None:
            if runtime < 0:
                runtime = np.abs(runtime)
                logger.warning("Negating runtime because it has to be positive")
            endtime = starttime + runtime * np.sign(dt)
        else:
            if endtime is None:
                endtime = self.fieldset.U.time[-1] if dt > 0 else self.fieldset.U.time[0]
        if interval is None:
            interval = endtime - starttime

        # Ensure that dt and interval have the correct sign
        if endtime > starttime:  # Time-forward mode
            if dt < 0:
                dt *= -1.
                logger.warning("Negating dt because running in time-forward mode")
            if interval < 0:
                interval *= -1.
                logger.warning("Negating interval because running in time-forward mode")
        if endtime < starttime:  # Time-backward mode
            if dt > 0.:
                dt *= -1.
                logger.warning("Negating dt because running in time-backward mode")
            if interval > 0.:
                interval *= -1.
                logger.warning("Negating interval because running in time-backward mode")

        # Initialise particle timestepping
        for p in self:
            p.time = starttime
            p.dt = dt
        # Execute time loop in sub-steps (timeleaps)
        timeleaps = int((endtime - starttime) / interval)
        assert(timeleaps >= 0)
        leaptime = starttime
        for _ in range(timeleaps):
            # First write output_file, because particles could have been added
            if output_file:
                output_file.write(self, leaptime)
            if show_movie:
                self.show(field=show_movie, show_time=leaptime)
            leaptime += interval
            self.kernel.execute(self, endtime=leaptime, dt=dt,
                                recovery=recovery)
        # Write out a final output_file
        if output_file:
            output_file.write(self, leaptime)
예제 #5
0
    def execute(self,
                pyfunc=AdvectionRK4,
                endtime=None,
                runtime=None,
                dt=1.,
                interval=None,
                recovery=None,
                output_file=None,
                show_movie=False):
        """Execute a given kernel function over the particle set for
        multiple timesteps. Optionally also provide sub-timestepping
        for particle output.

        :param pyfunc: Kernel function to execute. This can be the name of a
                       defined Python function or a :class:`parcels.kernel.Kernel` object.
                       Kernels can be concatenated using the + operator
        :param endtime: End time for the timestepping loop
        :param runtime: Length of the timestepping loop. Use instead of endtime.
        :param dt: Timestep interval to be passed to the kernel
        :param interval: Interval for inner sub-timestepping (leap), which dictates
                         the update frequency of file output and animation.
        :param output_file: :mod:`parcels.particlefile.ParticleFile` object for particle output
        :param recovery: Dictionary with additional `:mod:parcels.kernels.error`
                         recovery kernels to allow custom recovery behaviour in case of
                         kernel errors.
        :param show_movie: True shows particles; name of field plots that field as background
        """

        # check if pyfunc has changed since last compile. If so, recompile
        if self.kernel is None or (self.kernel.pyfunc is not pyfunc
                                   and self.kernel is not pyfunc):
            # Generate and store Kernel
            if isinstance(pyfunc, Kernel):
                self.kernel = pyfunc
            else:
                self.kernel = self.Kernel(pyfunc)
            # Prepare JIT kernel execution
            if self.ptype.uses_jit:
                self.kernel.remove_lib()
                self.kernel.compile(compiler=GNUCompiler())
                self.kernel.load_lib()

        # Convert all time variables to seconds
        if isinstance(endtime, delta):
            endtime = endtime.total_seconds()
        elif isinstance(endtime, datetime):
            endtime = (endtime - self.time_origin).total_seconds()
        if isinstance(runtime, delta):
            runtime = runtime.total_seconds()
        if isinstance(dt, delta):
            dt = dt.total_seconds()
        if isinstance(interval, delta):
            interval = interval.total_seconds()

        # Set particle.time defaults based on sign of dt, if not set at ParticleSet construction
        for p in self:
            if np.isnan(p.time):
                p.time = self.fieldset.U.grid.time[
                    0] if dt >= 0 else self.fieldset.U.grid.time[-1]

        # Derive _starttime, endtime and interval from arguments or fieldset defaults
        if runtime is not None and endtime is not None:
            raise RuntimeError(
                'Only one of (endtime, runtime) can be specified')
        _starttime = min([p.time for p in self]) if dt >= 0 else max(
            [p.time for p in self])
        if self.repeatdt is not None and self.repeat_starttime is None:
            self.repeat_starttime = _starttime
        if runtime is not None:
            if runtime < 0:
                runtime = np.abs(runtime)
                logger.warning(
                    "Negating runtime because it has to be positive")
            endtime = _starttime + runtime * np.sign(dt)
        elif endtime is None:
            endtime = self.fieldset.U.grid.time[
                -1] if dt >= 0 else self.fieldset.U.grid.time[0]
        if interval is None:
            interval = endtime - _starttime
        elif dt < 0 and interval > 0.:
            interval *= -1.
            logger.warning(
                "Negating interval because running in time-backward mode")

        if abs(endtime -
               _starttime) < 1e-5 or interval == 0 or dt == 0 or runtime == 0:
            timeleaps = 1
            dt = 0
            runtime = 0
            endtime = _starttime
            logger.warning_once(
                "dt or runtime are zero, or endtime is equal to Particle.time. "
                "The kernels will be executed once, without incrementing time")
        else:
            timeleaps = int((endtime - _starttime) / interval)

        if self.repeatdt is not None and self.repeatdt % interval != 0:
            raise ("repeatdt should be multiple of interval")

        # Initialise particle timestepping
        for p in self:
            p.dt = dt
            # set dt_initial to the original dt
            p.dt_initial = dt
        # Execute time loop in sub-steps (timeleaps)
        assert (timeleaps >= 0)
        leaptime = _starttime
        for _ in range(timeleaps):
            # First write output_file, because particles could have been added
            if output_file:
                output_file.write(self, leaptime)
            if show_movie:
                self.show(field=show_movie, show_time=leaptime)
            leaptime += interval
            self.kernel.execute(self,
                                endtime=leaptime,
                                dt=dt,
                                recovery=recovery)
            # Add new particles if repeatdt is used
            if self.repeatdt is not None and abs(
                    leaptime - self.repeat_starttime) % self.repeatdt == 0:
                self.add(
                    ParticleSet(fieldset=self.fieldset,
                                time=leaptime,
                                lon=self.repeatlon,
                                lat=self.repeatlat,
                                depth=self.repeatdepth,
                                pclass=self.repeatpclass))
        # Write out a final output_file
        if output_file:
            output_file.write(self, leaptime)