Skip to content

Commit

Permalink
optional progress bar when running model with a (long) list of snowpacks
Browse files Browse the repository at this point in the history
  • Loading branch information
ghislainp committed Feb 6, 2018
1 parent d418d57 commit 6892197
Show file tree
Hide file tree
Showing 2 changed files with 238 additions and 3 deletions.
16 changes: 13 additions & 3 deletions smrt/core/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
from .plugin import import_class
from .sensitivity_study import SensitivityStudy
from .sensor import Sensor
from .progressbar import Progress


def make_model(emmodel, rtsolver, emmodel_kwargs=None, rtsolver_kwargs=None):
Expand Down Expand Up @@ -121,12 +122,13 @@ def __init__(self, emmodel, rtsolver, emmodel_kwargs=None, rtsolver_kwargs=None)
self.emmodel_kwargs = emmodel_kwargs if emmodel_kwargs is not None else dict()
self.rtsolver_kwargs = rtsolver_kwargs if rtsolver_kwargs is not None else dict()

def run(self, sensor, snowpack, atmosphere=None, snowpack_dimension=None):
def run(self, sensor, snowpack, atmosphere=None, snowpack_dimension=None, progressbar=False):
""" Run the model for the given sensor configuration and return the results
:param sensor: sensor to use for the calculation
:param snowpack: snowpack to use for the calculation. Can be a singel snowpack, a list or a SensitivityStudy object.
:param snowpack_dimension: name and values (as a tuple) of the dimension to create for the results when a list of snowpack is provided. E.g. time, point, longitude, latitude. By default the dimension is called 'snowpack' and the values are from 1 to the number of snowpacks.
:param progressbar: if True, display a progress bar during multi-snowpacks computation
:returns: result of the calculation(s) as a :py:class:`Results` instance
"""

Expand Down Expand Up @@ -164,9 +166,17 @@ def run(self, sensor, snowpack, atmosphere=None, snowpack_dimension=None):
if dimension_values is None:
dimension_values = range(len(snowpack))

result_list = [self.run(sensor, sp, atmosphere=atmosphere) for sp in snowpack] # parallel computation would be better !
return concat_results(result_list, (dimension_name, dimension_values))
if progressbar:
pb = Progress(len(snowpack))

result_list = list()
for i, sp in enumerate(snowpack): # parallel computation would be better !
res = self.run(sensor, sp, atmosphere=atmosphere)
result_list.append(res)
if progressbar:
pb.animate(i + 1)

return concat_results(result_list, (dimension_name, dimension_values))

# not need to iterate anymore, either because the solver deals with the dimension or sensor config has single values.
# prepare to run
Expand Down
225 changes: 225 additions & 0 deletions smrt/core/progressbar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
# -*- python -*-
# -*- coding: utf-8 -*-
#
# This file is part of the easydev software
#
# Copyright (c) 2011-2017
#
# File author(s): Thomas Cokelaer <cokelaer@gmail.com>
#
# Distributed under the GPLv3 License.
# See accompanying file LICENSE.txt or copy at
# http://www.gnu.org/licenses/gpl-3.0.html
#
# Website: https://github.com/cokelaer/easydev
# Documentation: http://easydev-python.readthedocs.io
#
##############################################################################
"""A progress bar copied and adapted from pyMC code (dec 2014)"""
from __future__ import print_function

import sys
import time
import uuid


try:
from IPython.core.display import HTML, Javascript, display
except ImportError:
pass


__all__ = ['progress_bar', 'TextProgressBar', 'Progress']


class ProgressBar(object):
def __init__(self, iterations, interval=None):
self.iterations = iterations
# if no interval provided, set it to 1%
if interval is None:
if iterations <= 100:
interval = 1 # everything % of the data
else:
interval = int(iterations/100)
self.interval = interval
self.start = time.time()
self.last = 0

def _percentage(self, i):
if self.iterations !=0:
return 100 * i / float(self.iterations)
else:
# could be 100 ?
return 100

def _get_elapsed(self):
return time.time() - self.start
elapsed = property(_get_elapsed)


class TextProgressBar(ProgressBar):
"""Use :class:`Progress`"""
def __init__(self, iterations, printer, width=40, interval=None):
self.fill_char = '-'
self.width = width
self.printer = printer
ProgressBar.__init__(self, iterations, interval=interval)

def animate(self, i, dummy=None):
# dummy=None is for back-compatibility
if dummy is not None:
print("second argument in easydev.progress_bar.animate is deprecated. Update your code")
# +1 if i starts at 0 and finishes at N-1
if divmod(i, self.interval)[1] != 0 and i != self.iterations:
pass
else:
self.printer(self.progbar(i))

def progbar(self, i):
# +1 if i starts at 0 and finishes at N-1
bar = self.bar(self._percentage(i))
return "[%s] %i of %i complete in %.1f sec" % (
bar, (i), self.iterations, round(self.elapsed, 1))

def bar(self, percent):
all_full = self.width - 2
num_hashes = int(percent / 100 * all_full)

bar = self.fill_char * num_hashes + ' ' * (all_full - num_hashes)

info = '%d%%' % percent
loc = (len(bar) - len(info)) // 2
return replace_at(bar, info, loc, loc + len(info))


def replace_at(str, new, start, stop):
return str[:start] + new + str[stop:]


def consoleprint(s):
if sys.platform.lower().startswith('win'):
print(s, '\r', end='')
else:
print('\r', s, end='')
sys.stdout.flush()


def ipythonprint(s):
print('\r', s, end='')
sys.stdout.flush()


class IPythonNotebookPB(ProgressBar):
"""Use :class:`Progress`"""
def __init__(self, iterations, interval=None):
self.divid = str(uuid.uuid4())
self.sec_id = str(uuid.uuid4())

pb = HTML(
"""
<div style="float: left; border: 1px solid black; width:500px">
<div id="%s" style="background-color:blue; width:0%%">&nbsp;</div>
</div>
<label id="%s" style="padding-left: 10px;" text = ""/>
""" % (self.divid, self.sec_id))
display(pb)

ProgressBar.__init__(self, iterations, interval=interval)

def animate(self, i, dummy=None):
if dummy is not None:
print("second argument in easydev.progress_bar.animate is deprecated. Update your code")

# +1 if i starts at 0 and finishes at N-1
if divmod(i, self.interval)[1] != 0 and i != self.iterations :
pass
else:
percentage = self._percentage(i)
fraction = percentage
display(
Javascript("$('div#%s').width('%i%%')" %
(self.divid, percentage)))
display(
Javascript("$('label#%s').text('%i%% in %.1f sec')" %
(self.sec_id, fraction, round(self.elapsed, 1))))


def _run_from_ipython():
try:
__IPYTHON__
return True
except NameError:
return False


def progress_bar(iters, interval=None):
"""A progress bar for Python/IPython/IPython notebook
:param int iters: number of iterations (steps in the loop)
:param interval: number of intervals to use to update the progress bar (20
by default)
::
from easydev import progress_bar
pb = progress_bar(10)
for i in range(1,10):
import time
time.sleep(0.1)
pb.animate(i)
"""
if _run_from_ipython():
if in_ipynb() is True:
return IPythonNotebookPB(iters, interval=interval)
else:
return TextProgressBar(iters, printer=ipythonprint,
interval=interval)
else:
return TextProgressBar(iters, printer=consoleprint,
interval=interval)


class Progress(object):
"""Generic progress bar for python, IPython, IPython notebook
::
from easydev import Progress
pb = Progress(100, interval=1)
pb.animate(10)
"""
def __init__(self, iters, interval=None):
self.pb = progress_bar(iters, interval=interval)

def animate(self, i):
self.pb.animate(i)

def _get_elapsed(self):
return self.pb.elapsed
elapsed = property(_get_elapsed)


def in_ipynb():
"""Checks if we are in an ipython notebook
:return: True if in an ipython notebook otherwise returns False
"""
try:
cfg = get_ipython().config
if 'parent_appname' in cfg['IPKernelApp'].keys() and \
cfg['IPKernelApp']['parent_appname'] == 'ipython-notebook':
return True
elif "connection_file" in cfg['IPKernelApp'].keys():
if "jupyter" in cfg['IPKernelApp']['connection_file']:
return True
return False
except NameError:
return False





0 comments on commit 6892197

Please sign in to comment.