Browse Source

unit tests

station_distr_new
Sebastian Heimann 2 years ago
parent
commit
29c309985b
12 changed files with 455 additions and 94 deletions
  1. +2
    -3
      setup.cfg
  2. +0
    -1
      src/apps/cmd_init.py
  3. +18
    -15
      src/apps/grond.py
  4. +73
    -0
      src/config.py
  5. +6
    -0
      src/dataset.py
  6. +4
    -0
      src/toy.py
  7. +150
    -37
      test/common.py
  8. +16
    -0
      test/test_clustering.py
  9. +53
    -0
      test/test_commands.py
  10. +91
    -0
      test/test_examples.py
  11. +2
    -2
      test/test_problem.py
  12. +40
    -36
      test/test_scenario.py

+ 2
- 3
setup.cfg View File

@ -11,9 +11,8 @@ inplace=0
[nosetests]
verbosity=2
detailed-errors=1
#with-coverage=0
#cover-erase=1
#cover-package=grond
cover-erase=1
cover-package=grond
[coverage:report]
exclude_lines =


+ 0
- 1
src/apps/cmd_init.py View File

@ -100,7 +100,6 @@ class GrondInit(object):
def get_content_example(self, abbrv):
try:
print(abbrv, self.abbrv_to_example_dir(abbrv))
fn = self._get_example_config(
self.abbrv_to_example_dir(abbrv))
except OSError:


+ 18
- 15
src/apps/grond.py View File

@ -7,6 +7,7 @@ import os.path as op
import logging
from optparse import OptionParser, OptionValueError
import grond
from io import StringIO
try:
from pyrocko import util, marker
@ -181,7 +182,7 @@ def main(args=None):
if not args:
args = sys.argv
args = list(sys.argv)
args = list(args)
if len(args) < 2:
sys.exit('Usage: %s' % usage)
@ -200,7 +201,7 @@ def main(args=None):
sys.exit('Usage: %s' % usage)
else:
die('no such subcommand: %s' % command)
die('No such subcommand: %s' % command)
def add_common_options(parser):
@ -314,17 +315,20 @@ def cl_parse(command, args, setup=None, details=None):
return parser, options, args
def die(message, err=''):
def die(message, err='', prelude=''):
if prelude:
prelude = prelude + '\n'
if err:
sys.exit('%s failed: %s \n %s' % (program_name, message, err))
else:
sys.exit('%s failed: %s' % (program_name, message))
err = '\n' + err
sys.exit('%s%s failed: %s%s' % (prelude, program_name, message, err))
def help_and_die(parser, message):
parser.print_help(sys.stderr)
sys.stderr.write('\n')
die(message)
sio = StringIO()
parser.print_help(sio)
die(message, prelude=sio.getvalue())
def multiple_choice(option, opt_str, value, parser, choices):
@ -486,7 +490,7 @@ def command_init(args):
name=name, desc=desc, padding=padding, c=Color))
return '\n'.join(rstr)
help_text = '''Available configuration examples for grond.
help_text = '''Available configuration examples for Grond.
{c.BOLD}Example Projects{c.END}
@ -524,7 +528,7 @@ def command_init(args):
if args[0] == 'list':
print(help_text)
sys.exit(0)
return
if args[0].startswith('example_'):
if len(args) == 1:
@ -532,11 +536,10 @@ def command_init(args):
if not config:
help_and_die(parser, 'Unknown example: %s' % args[0])
sys.stdout.write(config)
sys.stdout.write(config+'\n\n')
print('\n{c.BOLD}Hint{c.END}\n\n'
'To create a project, use: grond init <example> <projectdir>'
.format(c=Color, example=args[0]))
logger.info('Hint: To create a project, use: grond init <example> '
'<projectdir>'.format(c=Color, example=args[0]))
elif op.exists(op.abspath(args[1])) and not options.force:
help_and_die(


+ 73
- 0
src/config.py View File

@ -1,3 +1,4 @@
import re
import os.path as op
from pyrocko import guts, gf
from pyrocko.guts import Bool, List
@ -131,6 +132,15 @@ class Config(HasPaths):
self.setup_modelling_environment(problem)
return problem
def get_elements(self, ypath):
return list(guts.iter_elements(self, ypath))
def set_elements(self, ypath, value):
guts.set_elements(self, ypath, value, regularize=True)
def clone(self):
return guts.clone(self)
def read_config(path):
try:
@ -184,6 +194,69 @@ def diff_configs(path1, path2):
sys.stdout.writelines(result)
class YPathError(GrondError):
pass
def parse_yname(yname):
ident = r'[a-zA-Z][a-zA-Z0-9_]*'
rint = r'-?[0-9]+'
m = re.match(
r'^(%s)(\[((%s)?(:)(%s)?|(%s))\])?$'
% (ident, rint, rint, rint), yname)
if not m:
raise YPathError('Syntax error in component: "%s"' % yname)
d = dict(
name=m.group(1))
if m.group(2):
if m.group(5):
istart = iend = None
if m.group(4):
istart = int(m.group(4))
if m.group(6):
iend = int(m.group(6))
d['slice'] = (istart, iend)
else:
d['index'] = int(m.group(7))
return d
def _decend(obj, ynames):
if ynames:
for sobj in iter_get_obj(obj, ynames):
yield sobj
else:
yield obj
def iter_get_obj(obj, ynames):
yname = ynames.pop(0)
d = parse_yname(yname)
if d['name'] not in obj.T.propnames:
raise AttributeError(d['name'])
obj = getattr(obj, d['name'])
if 'index' in d:
sobj = obj[d['index']]
for ssobj in _decend(sobj, ynames):
yield ssobj
elif 'slice' in d:
for i in range(*slice(*d['slice']).indices(len(obj))):
sobj = obj[i]
for ssobj in _decend(sobj, ynames):
yield ssobj
else:
for sobj in _decend(obj, ynames):
yield sobj
__all__ = '''
EngineConfig
Config


+ 6
- 0
src/dataset.py View File

@ -126,6 +126,9 @@ class Dataset(object):
if stationxml_filenames is not None and len(stationxml_filenames) > 0:
for stationxml_filename in stationxml_filenames:
if not op.exists(stationxml_filename):
continue
logger.debug(
'Loading stations from StationXML file "%s"...' %
stationxml_filename)
@ -181,6 +184,9 @@ class Dataset(object):
if stationxml_filenames:
for stationxml_filename in stationxml_filenames:
if not op.exists(stationxml_filename):
continue
logger.debug(
'Loading StationXML responses from "%s"...' %
stationxml_filename)


+ 4
- 0
src/toy.py View File

@ -5,6 +5,7 @@ from pyrocko.guts import Object, Float, Dict, List, String
from pyrocko import gf
from grond import Problem, Parameter, MisfitTarget
from grond.optimisers.highscore.plot import HighScoreOptimiserPlot
from grond.targets import MisfitResult
guts_prefix = 'grond.toy'
@ -93,6 +94,9 @@ class ToyProblem(Problem):
[t.obs_distance for t in self.targets],
dtype=num.float)
def evaluate(self, x):
raise NotImplementedError('Toy problem does not have evaluate()')
def misfits(self, x, mask=None):
self._setup_modelling()
distances = num.sqrt(


+ 150
- 37
test/common.py View File

@ -1,30 +1,30 @@
from __future__ import absolute_import
import logging
import tempfile
import os
import shutil
from grond import dataset
from grond import problem # noqa
from grond import targets # noqa
from grond import optimizers # noqa
import sys
from io import StringIO
from pyrocko import util
from grond.apps.grond import main
from grond import Environment
from grond.meta import expand_template
url = 'http://data.pyrocko.org/testing/grond/'
logger = logging.getLogger('grond.test')
op = os.path
test_data = op.join(op.abspath(__file__), 'data')
def get_test_data(self, path):
fn = op.join(test_data, path)
def test_data_path(fn):
return op.abspath(os.path.join(os.path.split(__file__)[0], 'data', fn))
if not os.exists(test_data):
os.mkdir(test_data)
if not os.exists(fn):
def get_test_data(path):
fn = test_data_path(path)
if not op.exists(fn):
util.ensuredirs(fn)
if path.endswith('/'):
util.download_dir(op.join(url, path), fn)
else:
@ -33,38 +33,151 @@ def get_test_data(self, path):
return fn
def get_dateset_config():
def put_test_data(path, dest=None):
if dest is None:
dest = path
fn = get_test_data(path)
util.ensuredirs(dest)
shutil.copytree(fn, dest)
def link_test_data(path, dest=None):
if dest is None:
dest = path
fn = get_test_data(path)
util.ensuredirs(dest)
os.symlink(fn, dest.rstrip(os.sep))
class GrondExit(Exception):
def __init__(self, res):
Exception.__init__(self, str(res))
self.result = res
class Capture(object):
def __init__(self, tee=False):
self.file = StringIO()
self.tee = tee
def __enter__(self):
self.orig_stdout = sys.stdout
self.orig_exit = sys.exit
sys.stdout = self
def my_exit(res):
raise GrondExit(res)
sys.exit = my_exit
def __exit__(self, *args):
sys.stdout = self.orig_stdout
sys.exit = self.orig_exit
def write(self, data):
self.file.write(data)
if self.tee:
self.orig_stdout.write(data)
def writelines(self, lines):
for l in lines:
self.write(l)
def flush(self):
self.file.flush()
def isatty(self):
return False
def getvalue(self):
return self.file.getvalue()
def grond(*args, tee=False):
# tee = True
cap = Capture(tee=tee)
with cap:
main(['grond'] + list(args))
return cap.getvalue()
def assert_grond_usage(*args):
res = None
try:
grond(*args)
except GrondExit as e:
res = e.result
assert res.startswith('Usage')
def get_playground_dir():
playground_dir = 'test_playground'
util.ensuredir(playground_dir)
return playground_dir
def get_rundir_paths(config_path, event_names):
env = Environment([config_path] + event_names)
conf = env.get_config()
rundir_paths = []
for event_name in event_names:
env.set_current_event_name(event_name)
problem_name = env.get_problem().name
rundir_paths.append(expand_template(
conf.rundir_template,
dict(problem_name=problem_name)))
return rundir_paths
class chdir(object):
def __init__(self, path):
self._path = path
def __enter__(self):
self._oldwd = os.getcwd()
os.chdir(self._path)
def __exit__(self, *args):
os.chdir(self._oldwd)
def create_test_dataset(self, *args, **kwargs):
dataset.DatasetConfig.__init__(self, *args, **kwargs)
tmpdir = tempfile.mkdtemp(prefix='grond')
dirs = ['gf_store/', 'waveforms/', 'insar/', 'gps/', 'meta/']
def run_in_project(
main,
project_dir_source,
project_dir,
event_name,
config_path):
for d in dirs:
os.mkdir(op.join(tmpdir, d))
shutil.copy(os.get_test_data(d), tmpdir)
playground_dir = get_playground_dir()
with chdir(playground_dir):
self.path_prefix = tmpdir
self.waveform_paths = ['waveforms/']
self.stations_stationxml_paths = ['meta/station.xml']
self.responses_stationxml_paths = ['meta/station.xml']
self.picks_path = ['meta/picks.txt']
if os.path.exists(project_dir):
shutil.rmtree(project_dir)
self.set_basepath(tmpdir)
logger.info('Initialising new dataset')
put_test_data(project_dir_source + '/', project_dir)
def delete_test_dataset(self):
os.rmdir(self.tmpdir)
with chdir(project_dir):
DatasetConfig = dataset.DatasetConfig
DatasetConfig.__init__ = create_test_dataset
return DatasetConfig
link_test_data(
'events/%s/' % event_name, 'data/events/%s/' % event_name)
env = Environment([config_path, event_name])
conf = env.get_config()
def get_grond_target_config():
pass
store_ids = conf.get_elements('target_groups[:].store_id')
for store_id in store_ids:
store_path = 'gf_stores/%s/' % store_id
if not os.path.exists(store_path):
link_test_data(store_path)
problem_name = env.get_problem().name
rundir_path = expand_template(
conf.rundir_template,
dict(problem_name=problem_name))
def get_grond_configs():
pass
return main(env, rundir_path)

+ 16
- 0
test/test_clustering.py View File

@ -0,0 +1,16 @@
from __future__ import absolute_import
from .common import grond, run_in_project
def test_clustering():
def main(env, rundir_path):
grond('cluster', 'dbscan', rundir_path)
run_in_project(
main,
project_dir_source='example_regional_cmt_full',
project_dir='example_regional_cmt_full_clustering',
event_name='gfz2018pmjk',
config_path='config/regional_cmt_ampspec.gronf')

+ 53
- 0
test/test_commands.py View File

@ -0,0 +1,53 @@
from __future__ import absolute_import
import re
import tempfile
from pyrocko import model
from . import common
from .common import grond, run_in_project
from grond import config
def test_usage():
common.assert_grond_usage()
def test_version():
assert grond('version').startswith('--- !grond.VersionInfo')
assert re.match(r'^\d+\.\d+\.\d+$', grond('version', '--short'))
assert all(len(x.split(': ')) == 2
for x in grond('version', '--failsafe').split('\n') if x)
def test_init():
common.assert_grond_usage('init')
assert grond('init', 'list').startswith('Available')
with tempfile.NamedTemporaryFile() as f:
f.write(grond('init', 'example_regional_cmt').encode('utf8'))
f.flush()
config.read_config(f.name)
def test_export():
def main(env, rundir_path):
def export_test(what, nevents):
with tempfile.NamedTemporaryFile() as f:
f.write(grond('export', 'best', rundir_path).encode('utf8'))
f.flush()
events = model.load_events(f.name)
assert len(events) == 1
export_test('best', 1)
export_test('mean', 1)
export_test('ensemble', 1010)
run_in_project(
main,
project_dir_source='example_regional_cmt_full',
project_dir='example_regional_cmt_full_export',
event_name='gfz2018pmjk',
config_path='config/regional_cmt_ampspec.gronf')

+ 91
- 0
test/test_examples.py View File

@ -0,0 +1,91 @@
from __future__ import absolute_import
import os
import shutil
from pyrocko import model
from grond import config
from grond import Environment
from grond.meta import expand_template
from . import common
from .common import grond, chdir
_multiprocess_can_split = True
def test_example_regional_cmt():
playground_dir = common.get_playground_dir()
with chdir(playground_dir):
run_example(
'example_regional_cmt',
'config/regional_cmt.gronf',
'config/regional_cmt_quick.gronf',
'gfz2018pmjk')
def test_example_wphase():
playground_dir = common.get_playground_dir()
with chdir(playground_dir):
run_example(
'example_wphase',
'config/wphase_cmt.gronf',
'config/wphase_cmt_quick.gronf',
'gfz2015sfdd')
def test_example_insar():
playground_dir = common.get_playground_dir()
with chdir(playground_dir):
run_example(
'example_insar',
'config/insar_rectangular.gronf',
'config/insar_rectangular_quick.gronf',
'2009laquila')
def run_example(project_name, config_path, quick_config_path, event_name):
project_dir = project_name
if os.path.exists(project_dir):
shutil.rmtree(project_dir)
grond('init', project_name, project_dir)
with chdir(project_dir):
assert os.path.isdir('config')
common.link_test_data(
'events/%s/' % event_name, 'data/events/%s/' % event_name)
env = Environment([config_path, event_name])
conf = env.get_config()
store_ids = conf.get_elements('target_groups[:].store_id')
for store_id in store_ids:
store_path = 'gf_stores/%s/' % store_id
if not os.path.exists(store_path):
common.link_test_data(store_path)
problem_name = env.get_problem().name
rundir_path = expand_template(
conf.rundir_template,
dict(problem_name=problem_name))
grond(
'check', config_path, event_name,
'--save-stations-used=used_stations.txt')
sorted(
s.station for s in model.load_stations('used_stations.txt'))
mod_conf = conf.clone()
mod_conf.set_elements(
'analyser_configs[:].niterations', 100)
mod_conf.set_elements(
'optimiser_config.sampler_phases[:].niterations', 100)
mod_conf.set_elements(
'optimiser_config.nbootstrap', 10)
mod_conf.set_basepath(conf.get_basepath())
config.write_config(mod_conf, quick_config_path)
grond('go', quick_config_path, event_name)
grond('report', rundir_path)
# assert os.path.isdir(os.path.join('report', event_name))

+ 2
- 2
test/test_problem.py View File

@ -32,7 +32,7 @@ def test_combine_misfits():
xg[:, 1] = num.tile(num.repeat(cy, ngx), ngz)
xg[:, 2] = num.repeat(cz, ngx*ngy)
misfitss = p.evaluate_many(xg)
misfitss = p.misfits_many(xg)
# misfitss[imodel, itarget, 0], misfitss[imodel, itarget, 1]
gms = p.combine_misfits(misfitss)
gms_contrib = p.combine_misfits(misfitss, get_contributions=True)
@ -51,7 +51,7 @@ def test_combine_misfits():
# gms_2_contrib[imodel, ibootstrap, itarget]
for ix, x in enumerate(xg):
misfits = p.evaluate(x)
misfits = p.misfits(x)
# misfits[itarget, 0], misfits[itarget, 1]
gm = p.combine_misfits(misfits)
# gm is scalar


+ 40
- 36
test/test_scenario.py View File

@ -1,41 +1,45 @@
from grond import scenario as grond_scenario
import tempfile
from __future__ import absolute_import
import os
import shutil
km = 1e3
from grond import config
from grond import Environment
STORE_STATIC = 'crust2_ib_static'
STORE_WAVEFORMS = 'crust2_ib'
from . import common
from .common import grond, chdir
_multiprocess_can_split = True
def test_scenario():
observations = [
grond_scenario.WaveformObservation(
nstations=20,
store_id=STORE_WAVEFORMS),
grond_scenario.InSARObservation(
store_id=STORE_STATIC),
grond_scenario.GNSSCampaignObservation(
nstations=20,
store_id=STORE_STATIC)
]
problems = [
grond_scenario.DCSourceProblem(
nevents=1),
grond_scenario.RectangularSourceProblem()
]
for prob in problems:
with tempfile.TemporaryDirectory(prefix='grond') as project_dir:
scenario = grond_scenario.GrondScenario(
project_dir,
center_lat=41., center_lon=33.3,
radius=200.*km)
for obs in observations:
scenario.add_observation(obs)
scenario.set_problem(prob)
scenario.build(force=True, interactive=False)
def test_scenario():
playground_dir = common.get_playground_dir()
with chdir(playground_dir):
scenario_dir = 'scenario'
if os.path.exists(scenario_dir):
shutil.rmtree(scenario_dir)
grond('scenario', '--targets=waveforms,insar', '--nevents=2',
'--nstations=3', scenario_dir)
with chdir(scenario_dir):
config_path = 'config/scenario.gronf'
quick_config_path = 'config/scenario_quick.gronf'
event_names = grond('events', config_path).strip().split('\n')
env = Environment([config_path] + event_names)
conf = env.get_config()
mod_conf = conf.clone()
mod_conf.set_elements(
'analyser_configs[:].niterations', 100)
mod_conf.set_elements(
'optimiser_config.sampler_phases[:].niterations', 100)
mod_conf.set_elements(
'optimiser_config.nbootstrap', 5)
mod_conf.set_basepath(conf.get_basepath())
config.write_config(mod_conf, quick_config_path)
grond('diff', config_path, quick_config_path)
grond('check', quick_config_path, *event_names)
grond('go', quick_config_path, '--parallel=2', *event_names)
rundir_paths = common.get_rundir_paths(config_path, event_names)
grond('report', '--parallel=2', *rundir_paths)

Loading…
Cancel
Save