From 7b158e4431e35c5011a03f4edeb8169914f2ec02 Mon Sep 17 00:00:00 2001 From: philippkraft Date: Tue, 28 Aug 2018 16:09:59 +0200 Subject: [PATCH] A working CLI implementation (#139, #143) --- spotpy/cli.py | 94 +++++++++++++++++++++++++++++++ spotpy/examples/cli_cmf_lumped.py | 16 ++++++ spotpy/examples/spotpy.conf | 4 ++ 3 files changed, 114 insertions(+) create mode 100644 spotpy/cli.py create mode 100644 spotpy/examples/cli_cmf_lumped.py create mode 100644 spotpy/examples/spotpy.conf diff --git a/spotpy/cli.py b/spotpy/cli.py new file mode 100644 index 00000000..28c113c2 --- /dev/null +++ b/spotpy/cli.py @@ -0,0 +1,94 @@ +from __future__ import division, print_function, unicode_literals + +import click +import inspect +from . import algorithms, database, describe +import click +import inspect +import io +import os + + +def get_config_from_file(): + """ + Gets the spotpy configuration from a config file 'spotpy.conf'. + + Example: + + sampler = mc + dbtype = csv + parallel = seq + # This is a comment + runs = 10 + """ + config = {} + if os.path.exists('spotpy.conf'): + with io.open('spotpy.conf') as f: + for line in f: + if not line.strip().startswith('#'): + try: + k, v = line.split('=', 1) + config[k.strip()] = v.strip() + except ValueError: + pass + return config + + +def get_sampler_from_string(sampler_name): + return getattr(algorithms, sampler_name) + + +def make_type_from_module(module, *exclude): + + def use(cl): + # Check if class name starts with an exclusion term + return inspect.isclass(cl) and not any([cl.__name__.startswith(ex) for ex in ('_', ) + exclude]) + members = inspect.getmembers(module, use) + return click.Choice([n for n, m in members if not n.startswith('_')]) + + + +@click.group(context_settings=dict(help_option_names=['-h', '--help'])) +def cli(): + pass + + +@cli.command() +@click.pass_context +@click.option('--sampler', '-s', type=make_type_from_module(algorithms), default='mc', + help='Select the spotpy sampler') +@click.option('--dbformat', type=make_type_from_module(database, 'Pick'), default='ram', + help='The type of the database') +@click.option('--dbname', type=click.STRING, help='The name of the database, leave open for ram') +@click.option('--parallel', '-p', type=click.Choice(['seq', 'mpc', 'mpi']), default='seq', + help='Parallelization: seq = no parallelization, mpi = MPI (for clusters), mpc = multiprocessing') +@click.option('--runs', '-n', type=click.INT, default=1, help='Number of runs') +def run(ctx, **kwargs): + """ + Runs a sampler for automatic calibration + """ + setup = ctx.obj + sampler_class = get_sampler_from_string(kwargs.pop('sampler')) + runs = kwargs.pop('runs') + sampler = sampler_class(setup, **kwargs) + sampler.sample(runs) + + +@cli.command() +@click.pass_context +def gui(ctx): + """ + Shows a GUI for manual calibration + """ + from spotpy.gui.mpl import GUI + setup = ctx.obj + gui = GUI(setup) + gui.show() + + +def main(setup): + # Prevent help text from wrapping + cli.help = '\b\n' + describe.setup(setup).replace('\n\n', '\n\b\n') + config = get_config_from_file() + cli(obj=setup, auto_envvar_prefix='SPOTPY', default_map=config) + diff --git a/spotpy/examples/cli_cmf_lumped.py b/spotpy/examples/cli_cmf_lumped.py new file mode 100644 index 00000000..54f99991 --- /dev/null +++ b/spotpy/examples/cli_cmf_lumped.py @@ -0,0 +1,16 @@ +""" +Shows the usage of the matplotlib GUI + +Needs at least Python 3.5 +""" + +from __future__ import division, print_function, unicode_literals + + +from spotpy.cli import main +#from spotpy.examples.spot_setup_cmf_lumped import SingleStorage as spot_setup +from spotpy.examples.spot_setup_rosenbrock import spot_setup + +if __name__ == '__main__': + setup = spot_setup() + main(setup) \ No newline at end of file diff --git a/spotpy/examples/spotpy.conf b/spotpy/examples/spotpy.conf new file mode 100644 index 00000000..87ef1caf --- /dev/null +++ b/spotpy/examples/spotpy.conf @@ -0,0 +1,4 @@ +sampler = abc +dbtype = csv +parallel = seq +# This is a comment