Coverage for /usr/local/lib/python3.11/dist-packages/grond/apps/grond.py: 53%

732 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2024-11-27 15:15 +0000

1# https://pyrocko.org/grond - GPLv3 

2# 

3# The Grond Developers, 21st Century 

4import sys 

5import os.path as op 

6import logging 

7from optparse import OptionParser, OptionValueError, IndentedHelpFormatter 

8from io import StringIO 

9 

10from pyrocko import util, marker 

11import grond 

12 

13logger = logging.getLogger('grond.main') 

14km = 1e3 

15 

16 

17class GrondHelpFormatter(IndentedHelpFormatter): 

18 

19 def _format_text(self, text): 

20 """ 

21 Format a paragraph of free-form text for inclusion in the 

22 help output at the current indentation level. 

23 """ 

24 import textwrap 

25 

26 def fill(text, *args, **kwargs): 

27 return '\n\n'.join( 

28 textwrap.fill(part, *args, **kwargs) 

29 for part in text.split('\n\n')) 

30 

31 text_width = max(self.width - self.current_indent, 11) 

32 indent = " "*self.current_indent 

33 return fill( 

34 text, 

35 text_width, 

36 initial_indent=indent, 

37 subsequent_indent=indent) 

38 

39 

40class Color: 

41 PURPLE = '\033[95m' 

42 CYAN = '\033[96m' 

43 DARKCYAN = '\033[36m' 

44 BLUE = '\033[94m' 

45 GREEN = '\033[92m' 

46 YELLOW = '\033[93m' 

47 RED = '\033[91m' 

48 BOLD = '\033[1m' 

49 UNDERLINE = '\033[4m' 

50 END = '\033[0m' 

51 

52 

53def d2u(d): 

54 if isinstance(d, dict): 

55 return dict((k.replace('-', '_'), v) for (k, v) in d.items()) 

56 else: 

57 return d.replace('-', '_') 

58 

59 

60subcommand_descriptions = { 

61 'init': 'initialise new project structure or print configuration', 

62 'scenario': 'create a forward-modelled scenario project', 

63 'events': 'print available event names for given configuration', 

64 'check': 'check data and configuration', 

65 'go': 'run Grond optimisation', 

66 'forward': 'run forward modelling', 

67 'harvest': 'manually run harvesting', 

68 'cluster': 'run cluster analysis on result ensemble', 

69 'plot': 'plot optimisation result', 

70 'movie': 'visualize optimiser evolution', 

71 'export': 'export results', 

72 'tag': 'add user-defined label to run directories', 

73 'report': 'create result report', 

74 'diff': 'compare two configs or other normalized Grond YAML files', 

75 'qc-polarization': 'check sensor orientations with polarization analysis', 

76 'upgrade-config': 'upgrade config file to the latest version of Grond', 

77 'version': 'print version number of Grond and its main dependencies', 

78} 

79 

80subcommand_usages = { 

81 'init': ( 

82 'init list [options]', 

83 'init <example> [options]', 

84 'init <example> <projectdir> [options]'), 

85 'scenario': 'scenario [options] <projectdir>', 

86 'events': 'events <configfile>', 

87 'check': 'check <configfile> <eventnames> ... [options]', 

88 'go': 'go <configfile> <eventnames> ... [options]', 

89 'forward': ( 

90 'forward <rundir> [options]', 

91 'forward <configfile> <eventnames> ... [options]'), 

92 'harvest': 'harvest <rundir> [options]', 

93 'cluster': ( 

94 'cluster <method> <rundir> [options]', 

95 'cluster <clusteringconfigfile> <rundir> [options]'), 

96 'plot': ( 

97 'plot <plotnames> ( <rundir> | <configfile> <eventname> ) [options]', 

98 'plot all ( <rundir> | <configfile> <eventname> ) [options]', 

99 'plot <plotconfigfile> ( <rundir> | <configfile> <eventname> ) [options]', # noqa 

100 'plot list ( <rundir> | <configfile> <eventname> ) [options]', 

101 'plot config ( <rundir> | <configfile> <eventname> ) [options]'), 

102 'movie': 'movie <rundir> <xpar> <ypar> <filetemplate> [options]', 

103 'export': 'export (best|mean|ensemble|stats) <rundirs> ... [options]', 

104 'tag': ( 

105 'tag add <tag> <rundir>', 

106 'tag remove <tag> <rundir>', 

107 'tag list <rundir>'), 

108 'report': ( 

109 'report <rundir> ... [options]', 

110 'report <configfile> <eventnames> ...'), 

111 'diff': 'diff <left_path> <right_path>', 

112 'qc-polarization': 'qc-polarization <configfile> <eventname> ' 

113 '<target_group_path> [options]', 

114 'upgrade-config': 'upgrade-config <configfile>', 

115 'version': 'version', 

116} 

117 

118subcommands = subcommand_descriptions.keys() 

119 

120program_name = 'grond' 

121 

122usage_tdata = d2u(subcommand_descriptions) 

123usage_tdata['program_name'] = program_name 

124usage_tdata['version_number'] = grond.__version__ 

125 

126 

127usage = '''%(program_name)s <subcommand> [options] [--] <arguments> ... 

128 

129Grond is a probabilistic earthquake source inversion framework. 

130 

131This is Grond version %(version_number)s. 

132 

133Subcommands: 

134 

135 scenario %(scenario)s 

136 init %(init)s 

137 events %(events)s 

138 check %(check)s 

139 go %(go)s 

140 forward %(forward)s 

141 harvest %(harvest)s 

142 cluster %(cluster)s 

143 plot %(plot)s 

144 movie %(movie)s 

145 export %(export)s 

146 tag %(tag)s 

147 report %(report)s 

148 diff %(diff)s 

149 qc-polarization %(qc_polarization)s 

150 upgrade-config %(upgrade_config)s 

151 version %(version)s 

152 

153To get further help and a list of available options for any subcommand run: 

154 

155 %(program_name)s <subcommand> --help 

156 

157What do you want to bust today?! 

158''' % usage_tdata 

159 

160 

161class CLIHints(object): 

162 init = ''' 

163We created a folder structure in {project_dir}. 

164Check out the YAML configuration in {config} and start the optimisation by: 

165 

166 grond go {config} 

167''' 

168 scenario = ''' 

169To start the scenario's optimisation, change to folder 

170 

171 cd {project_dir} 

172 

173Check out the YAML configuration in {config} and start the optimisation by: 

174 

175 grond go {config} 

176''' 

177 report = ''' 

178To open the report in your web browser, run 

179 

180 grond report -s --open {config} 

181''' 

182 check = ''' 

183To start the optimisation, run 

184 

185 grond go {config} 

186''' 

187 go = ''' 

188To look at the results, run 

189 

190 grond report -so {rundir} 

191''' 

192 

193 def __new__(cls, command, **kwargs): 

194 return '{c.BOLD}Hint{c.END}\n'.format(c=Color) +\ 

195 getattr(cls, command).format(**kwargs) 

196 

197 

198def main(args=None): 

199 if not args: 

200 args = sys.argv 

201 

202 args = list(args) 

203 if len(args) < 2: 

204 sys.exit('Usage: %s' % usage) 

205 

206 args.pop(0) 

207 command = args.pop(0) 

208 

209 if command in subcommands: 

210 globals()['command_' + d2u(command)](args) 

211 

212 elif command in ('--help', '-h', 'help'): 

213 if command == 'help' and args: 

214 acommand = args[0] 

215 if acommand in subcommands: 

216 globals()['command_' + acommand](['--help']) 

217 

218 sys.exit('Usage: %s' % usage) 

219 

220 else: 

221 die('No such subcommand: %s' % command) 

222 

223 

224def add_common_options(parser): 

225 parser.add_option( 

226 '--loglevel', 

227 action='store', 

228 dest='loglevel', 

229 type='choice', 

230 choices=('critical', 'error', 'warning', 'info', 'debug'), 

231 help='set logger level to ' 

232 '"critical", "error", "warning", "info", or "debug". ' 

233 'Default is "info".') 

234 

235 parser.add_option( 

236 '--status', dest='status', 

237 type='choice', choices=['state', 'quiet'], 

238 help='status output selection (choices: state, quiet, default: ' 

239 'state)') 

240 

241 parser.add_option( 

242 '--parallel', dest='nparallel', type=int, 

243 help='set number of events to process in parallel, ' 

244 'if set to more than one, --status=quiet is implied.') 

245 

246 parser.add_option( 

247 '--threads', dest='nthreads', type=int, 

248 help='set number of threads per process (default: 1). ' 

249 'Set to 0 to use all available cores.') 

250 

251 parser.add_option( 

252 '--docs', 

253 dest='rst_docs', 

254 action='store_true') 

255 

256 

257def print_docs(command, parser): 

258 

259 from optparse import IndentedHelpFormatter 

260 

261 class DocsFormatter(IndentedHelpFormatter): 

262 

263 def format_heading(self, heading): 

264 return '%s\n%s\n\n' % (heading, '.'*len(heading)) 

265 

266 def format_usage(self, usage): 

267 lines = usage.splitlines() 

268 return self.format_heading('Usage') + \ 

269 '.. code-block:: none\n\n%s' % '\n'.join( 

270 ' '+line.strip() for line in lines) 

271 

272 def format_option(self, option): 

273 if not option.help: 

274 return '' 

275 

276 result = [] 

277 opts = self.option_strings[option] 

278 result.append('\n.. describe:: %s\n\n' % opts) 

279 

280 help_text = self.expand_default(option) 

281 result.append(' %s\n\n' % help_text) 

282 

283 return ''.join(result) 

284 

285 parser.formatter = DocsFormatter() 

286 parser.formatter.set_parser(parser) 

287 

288 def format_help(parser): 

289 formatter = parser.formatter 

290 result = [] 

291 

292 result.append(parser.format_description(formatter) + "\n") 

293 

294 if parser.usage: 

295 result.append(parser.get_usage() + "\n") 

296 

297 result.append('\n') 

298 

299 result.append(parser.format_option_help(formatter)) 

300 

301 result.append('\n') 

302 

303 result.append(parser.format_epilog(formatter)) 

304 return "".join(result) 

305 

306 print(command) 

307 print('-' * len(command)) 

308 print() 

309 print('.. program:: %s' % program_name) 

310 print() 

311 print('.. option:: %s' % command) 

312 print() 

313 print(format_help(parser)) 

314 

315 

316def process_common_options(command, parser, options): 

317 from grond.config import get_global_config 

318 

319 gconf = get_global_config() 

320 gconf.override_with_cli_arguments(options) 

321 

322 util.setup_logging(program_name, gconf.loglevel) 

323 if options.rst_docs: 

324 print_docs(command, parser) 

325 exit(0) 

326 

327 

328def cl_parse(command, args, setup=None, details=None): 

329 usage = subcommand_usages[command] 

330 descr = subcommand_descriptions[command] 

331 

332 if isinstance(usage, str): 

333 usage = [usage] 

334 

335 susage = '%s %s' % (program_name, usage[0]) 

336 for s in usage[1:]: 

337 susage += '\n%s%s %s' % (' '*7, program_name, s) 

338 

339 description = descr[0].upper() + descr[1:] + '.' 

340 

341 if details: 

342 description = description + '\n\n%s' % details 

343 

344 parser = OptionParser( 

345 usage=susage, 

346 description=description, 

347 formatter=GrondHelpFormatter()) 

348 

349 if setup: 

350 setup(parser) 

351 

352 add_common_options(parser) 

353 (options, args) = parser.parse_args(args) 

354 process_common_options(command, parser, options) 

355 return parser, options, args 

356 

357 

358def die(message, err='', prelude=''): 

359 if prelude: 

360 prelude = prelude + '\n' 

361 

362 if err: 

363 err = '\n' + err 

364 

365 sys.exit('%s%s failed: %s%s' % (prelude, program_name, message, err)) 

366 

367 

368def help_and_die(parser, message): 

369 sio = StringIO() 

370 parser.print_help(sio) 

371 die(message, prelude=sio.getvalue()) 

372 

373 

374def multiple_choice(option, opt_str, value, parser, choices): 

375 options = value.split(',') 

376 for opt in options: 

377 if opt not in choices: 

378 raise OptionValueError('Invalid option %s - valid options are: %s' 

379 % (opt, ', '.join(choices))) 

380 setattr(parser.values, option.dest, options) 

381 

382 

383def magnitude_range(option, opt_str, value, parser): 

384 mag_range = value.split('-') 

385 if len(mag_range) != 2: 

386 raise OptionValueError( 

387 'Invalid magnitude %s - valid range is e.g. 6-7.' % value) 

388 try: 

389 mag_range = tuple(map(float, mag_range)) 

390 except ValueError: 

391 raise OptionValueError('Magnitudes must be numbers.') 

392 

393 if mag_range[0] > mag_range[1]: 

394 raise OptionValueError('Minimum magnitude must be larger than' 

395 ' maximum magnitude.') 

396 setattr(parser.values, option.dest, mag_range) 

397 

398 

399def command_scenario(args): 

400 

401 STORE_STATIC = 'crust2_ib_static' 

402 STORE_WAVEFORMS = 'crust2_ib' 

403 

404 def setup(parser): 

405 parser.add_option( 

406 '--targets', action='callback', dest='targets', type=str, 

407 callback=multiple_choice, callback_kwargs={ 

408 'choices': ('waveforms', 'gnss', 'insar') 

409 }, 

410 default='waveforms', 

411 help='forward modelling targets for the scenario. Select from:' 

412 ' waveforms, gnss and insar. ' 

413 '(default: --targets=%default,' 

414 ' multiple selection by --targets=waveforms,gnss,insar)') 

415 parser.add_option( 

416 '--problem', dest='problem', default='cmt', 

417 type='choice', choices=['cmt', 'rectangular'], 

418 help='problem to generate: \'dc\' (double couple)' 

419 ' or \'rectangular\' (rectangular finite fault)' 

420 ' (default: \'%default\')') 

421 parser.add_option( 

422 '--magnitude-range', dest='magnitude_range', type=str, 

423 action='callback', callback=magnitude_range, default=[6.0, 7.0], 

424 help='Magnitude range min_mag-max_mag (default: %default)') 

425 parser.add_option( 

426 '--nstations', dest='nstations', type=int, default=20, 

427 help='number of seismic stations to create (default: %default)') 

428 parser.add_option( 

429 '--gnss_nstations', dest='gnss_nstations', type=int, default=20, 

430 help='number of GNSS campaign stations to create' 

431 ' (default: %default)') 

432 parser.add_option( 

433 '--nevents', dest='nevents', type=int, default=1, 

434 help='number of events to create (default: %default)') 

435 parser.add_option( 

436 '--lat', dest='lat', type=float, default=41.0, 

437 help='center latitude of the scenario (default: %default)') 

438 parser.add_option( 

439 '--lon', dest='lon', type=float, default=33.3, 

440 help='center latitude of the scenario (default: %default)') 

441 parser.add_option( 

442 '--radius', dest='radius', type=float, default=100., 

443 help='radius of the scenario in [km] (default: %default)') 

444 parser.add_option( 

445 '--source-radius', dest='source_radius', type=float, default=10., 

446 help='radius of the source area in [km] (default: %default)') 

447 parser.add_option( 

448 '--stations-paths', dest='stations_paths', type=str, default=None, 

449 help='paths to a Pyrocko station file, seperated by \',\'' 

450 '(default: %default)') 

451 parser.add_option( 

452 '--stationxml-paths', dest='stationxml_paths', type=str, 

453 default=None, 

454 help='paths to StationXML files, seperated by \',\'' 

455 '(default: %default)') 

456 parser.add_option( 

457 '--gf-waveforms', dest='store_waveforms', type=str, 

458 default=STORE_WAVEFORMS, 

459 help='Green\'s function store for waveform modelling, ' 

460 '(default: %default)') 

461 parser.add_option( 

462 '--gf-static', dest='store_statics', type=str, 

463 default=STORE_STATIC, 

464 help='Green\'s function store for static modelling, ' 

465 '(default: %default)') 

466 parser.add_option( 

467 '--force', dest='force', action='store_true', 

468 help='overwrite existing project folder.') 

469 parser.add_option( 

470 '--gf-store-superdirs', 

471 dest='gf_store_superdirs', 

472 help='Comma-separated list of directories containing GF stores') 

473 parser.add_option( 

474 '--no-map', 

475 dest='make_map', 

476 default=True, 

477 action='store_false', 

478 help='suppress generation of map') 

479 parser.add_option( 

480 '--rebuild', 

481 dest='rebuild', action='store_true', default=False, 

482 help='Rebuild a manually configured grond scenario') 

483 

484 parser, options, args = cl_parse('scenario', args, setup) 

485 

486 gf_store_superdirs = None 

487 if options.gf_store_superdirs: 

488 gf_store_superdirs = options.gf_store_superdirs.split(',') 

489 

490 if len(args) == 1: 

491 project_dir = args[0] 

492 else: 

493 parser.print_help() 

494 sys.exit(1) 

495 

496 from grond import scenario as grond_scenario 

497 

498 try: 

499 scenario = grond_scenario.GrondScenario( 

500 project_dir, 

501 center_lat=options.lat, center_lon=options.lon, 

502 radius=options.radius*km) 

503 

504 scenario.rebuild = options.rebuild 

505 if options.rebuild: 

506 options.force = True 

507 

508 if 'waveforms' in options.targets: 

509 if options.stationxml_paths: 

510 options.stationxml_paths = [ 

511 op.abspath(path) for path in 

512 options.stationxml_paths.split(',')] 

513 

514 if options.stations_paths: 

515 options.stations_paths = [ 

516 op.abspath(path) for path in 

517 options.stations_paths.split(',')] 

518 

519 obs = grond_scenario.WaveformObservation( 

520 nstations=options.nstations, 

521 store_id=options.store_waveforms, 

522 stations_paths=options.stations_paths, 

523 stationxml_paths=options.stationxml_paths) 

524 scenario.add_observation(obs) 

525 

526 if 'insar' in options.targets: 

527 obs = grond_scenario.InSARObservation( 

528 store_id=options.store_statics) 

529 scenario.add_observation(obs) 

530 

531 if 'gnss' in options.targets: 

532 obs = grond_scenario.GNSSCampaignObservation( 

533 nstations=options.gnss_nstations, 

534 store_id=options.store_statics) 

535 scenario.add_observation(obs) 

536 

537 if options.problem == 'cmt': 

538 problem = grond_scenario.DCSourceProblem( 

539 nevents=options.nevents, 

540 radius=options.source_radius*km, 

541 magnitude_min=options.magnitude_range[0], 

542 magnitude_max=options.magnitude_range[1]) 

543 elif options.problem == 'rectangular': 

544 problem = grond_scenario.RectangularSourceProblem( 

545 nevents=options.nevents) 

546 scenario.set_problem(problem) 

547 

548 scenario.build( 

549 force=options.force, 

550 interactive=True, 

551 gf_store_superdirs=gf_store_superdirs, 

552 make_map=options.make_map) 

553 

554 logger.info(CLIHints('scenario', 

555 config=scenario.get_grond_config_path(), 

556 project_dir=project_dir)) 

557 

558 except grond.GrondError as e: 

559 die(str(e)) 

560 

561 

562def command_init(args): 

563 

564 from .cmd_init import GrondInit 

565 

566 grond_init = GrondInit() 

567 

568 def print_section(entries): 

569 if len(entries) == 0: 

570 return '\tNone available.' 

571 

572 padding = max([len(n) for n in entries.keys()]) 

573 rstr = [] 

574 lcat = None 

575 for name, desc in entries.items(): 

576 

577 cat = name.split('_')[0] 

578 if lcat is not None and lcat != cat: 

579 rstr.append('') 

580 lcat = cat 

581 

582 rstr.append(' {c.BOLD}{name:<{padding}}{c.END} : {desc}'.format( 

583 name=name, desc=desc, padding=padding, c=Color)) 

584 return '\n'.join(rstr) 

585 

586 help_text = '''Available configuration examples for Grond. 

587 

588{c.BOLD}Example Projects{c.END} 

589 

590 Deploy a full project structure into a directory. 

591 

592 usage: grond init <example> <projectdir> 

593 

594 where <example> is any of the following: 

595 

596{examples_list} 

597 

598{c.BOLD}Config Sections{c.END} 

599 

600 Print out configuration snippets for various components. 

601 

602 usage: grond init <section> 

603 

604 where <section> is any of the following: 

605 

606{sections_list} 

607'''.format(c=Color, 

608 examples_list=print_section(grond_init.get_examples()), 

609 sections_list=print_section(grond_init.get_sections())) 

610 

611 def setup(parser): 

612 parser.add_option( 

613 '--force', dest='force', action='store_true') 

614 

615 parser, options, args = cl_parse( 

616 'init', args, setup, 

617 'Use grond init list to show available examples.') 

618 

619 if len(args) not in (1, 2): 

620 help_and_die(parser, '1 or 2 arguments required') 

621 

622 if args[0] == 'list': 

623 print(help_text) 

624 return 

625 

626 if args[0].startswith('example_'): 

627 if len(args) == 1: 

628 config = grond_init.get_content_example(args[0]) 

629 if not config: 

630 help_and_die(parser, 'Unknown example: %s' % args[0]) 

631 

632 sys.stdout.write(config+'\n\n') 

633 

634 logger.info('Hint: To create a project, use: grond init <example> ' 

635 '<projectdir>') 

636 

637 elif op.exists(op.abspath(args[1])) and not options.force: 

638 help_and_die( 

639 parser, 

640 'Directory "%s" already exists! Use --force to overwrite.' 

641 % args[1]) 

642 else: 

643 try: 

644 grond_init.init_example(args[0], args[1], force=options.force) 

645 except OSError as e: 

646 print(str(e)) 

647 

648 else: 

649 sec = grond_init.get_content_snippet(args[0]) 

650 if not sec: 

651 help_and_die(parser, 'Unknown snippet: %s' % args[0]) 

652 

653 sys.stdout.write(sec) 

654 

655 

656def command_init_old(args): 

657 

658 from . import cmd_init as init 

659 

660 def setup(parser): 

661 parser.add_option( 

662 '--targets', action='callback', dest='targets', type=str, 

663 callback=multiple_choice, callback_kwargs={ 

664 'choices': ('waveforms', 'gnss', 'insar', 'all') 

665 }, 

666 default='waveforms', 

667 help='select from:' 

668 ' waveforms, gnss and insar. ' 

669 '(default: --targets=%default,' 

670 ' multiple selection by --targets=waveforms,gnss,insar)') 

671 parser.add_option( 

672 '--problem', dest='problem', 

673 type='choice', choices=['cmt', 'rectangular'], 

674 help='problem to generate: \'dc\' (double couple)' 

675 ' or\'rectangular\' (rectangular finite fault)' 

676 ' (default: \'%default\')') 

677 parser.add_option( 

678 '--force', dest='force', action='store_true', 

679 help='overwrite existing project folder') 

680 

681 parser, options, args = cl_parse('init', args, setup) 

682 

683 try: 

684 project = init.GrondProject() 

685 

686 if 'all' in options.targets: 

687 targets = ['waveforms', 'gnss', 'insar'] 

688 else: 

689 targets = options.targets 

690 

691 if not options.problem: 

692 if 'insar' in targets or 'gnss' in targets: 

693 problem = 'rectangular' 

694 else: 

695 problem = 'cmt' 

696 else: 

697 problem = options.problem 

698 

699 if problem == 'rectangular': 

700 project.set_rectangular_source() 

701 elif problem == 'cmt': 

702 project.set_cmt_source() 

703 

704 if 'waveforms' in targets: 

705 project.add_waveforms() 

706 

707 if 'insar' in targets: 

708 project.add_insar() 

709 

710 if 'gnss' in targets: 

711 project.add_gnss() 

712 

713 if len(args) == 1: 

714 project_dir = args[0] 

715 project.build(project_dir, options.force) 

716 logger.info(CLIHints( 

717 'init', project_dir=project_dir, 

718 config=op.join(project_dir, 'config', 'config.gronf'))) 

719 else: 

720 sys.stdout.write(project.dump()) 

721 

722 except grond.GrondError as e: 

723 die(str(e)) 

724 

725 

726def command_events(args): 

727 def setup(parser): 

728 pass 

729 

730 parser, options, args = cl_parse('events', args, setup) 

731 if len(args) != 1: 

732 help_and_die(parser, 'missing arguments') 

733 

734 config_path = args[0] 

735 try: 

736 config = grond.read_config(config_path) 

737 

738 for event_name in grond.get_event_names(config): 

739 print(event_name) 

740 

741 except grond.GrondError as e: 

742 die(str(e)) 

743 

744 

745def command_check(args): 

746 

747 from grond.environment import Environment 

748 

749 def setup(parser): 

750 parser.add_option( 

751 '--target-ids', dest='target_string_ids', metavar='TARGET_IDS', 

752 help='process only selected targets. TARGET_IDS is a ' 

753 'comma-separated list of target IDs. Target IDs have the ' 

754 'form SUPERGROUP.GROUP.NETWORK.STATION.LOCATION.CHANNEL.') 

755 

756 parser.add_option( 

757 '--waveforms', dest='show_waveforms', action='store_true', 

758 help='show raw, restituted, projected, and processed waveforms') 

759 

760 parser.add_option( 

761 '--nrandom', dest='n_random_synthetics', metavar='N', type=int, 

762 default=10, 

763 help='set number of random synthetics to forward model (default: ' 

764 '10). If set to zero, create synthetics for the reference ' 

765 'solution.') 

766 

767 parser.add_option( 

768 '--save-stations-used', dest='stations_used_path', 

769 metavar='FILENAME', 

770 help='aggregate all stations used by the setup into a file') 

771 

772 details = ''' 

773Will first check if the configuration of the inversion problem and targets are 

774consistent with the GF database (distance range, depth range, and, if 

775applicable, frequency range). Then it will try to run *nrandom* forward models 

776(default=10) to check if the modelling works and if it can find and read the 

777input data. 

778'''.strip() 

779 

780 parser, options, args = cl_parse('check', args, setup, details) 

781 if len(args) < 1: 

782 help_and_die(parser, 'missing arguments') 

783 

784 try: 

785 env = Environment(args) 

786 config = env.get_config() 

787 

788 target_string_ids = None 

789 if options.target_string_ids: 

790 target_string_ids = options.target_string_ids.split(',') 

791 

792 grond.check( 

793 config, 

794 event_names=env.get_selected_event_names(), 

795 target_string_ids=target_string_ids, 

796 show_waveforms=options.show_waveforms, 

797 n_random_synthetics=options.n_random_synthetics, 

798 stations_used_path=options.stations_used_path) 

799 

800 logger.info(CLIHints('check', config=env.get_config_path())) 

801 

802 except grond.GrondError as e: 

803 die(str(e)) 

804 

805 

806def command_go(args): 

807 

808 from grond.environment import Environment 

809 

810 def setup(parser): 

811 parser.add_option( 

812 '--force', dest='force', action='store_true', 

813 help='overwrite existing run directory') 

814 parser.add_option( 

815 '--preserve', dest='preserve', action='store_true', 

816 help='preserve old rundir') 

817 

818 details = ''' 

819Runs the inversion as defined in the configuration file. <eventname> defines 

820which event is inverted, replace <eventname> by the word `all` to invert all 

821events included in the config file. Use option --parallel to run inversions of 

822different events in parallel. 

823'''.strip() 

824 

825 parser, options, args = cl_parse('go', args, setup, details) 

826 

827 try: 

828 env = Environment(args) 

829 

830 grond.go( 

831 env, 

832 force=options.force, 

833 preserve=options.preserve) 

834 

835 if len(env.get_selected_event_names()) == 1: 

836 logger.info(CLIHints( 

837 'go', rundir=env.get_rundir_path())) 

838 

839 except grond.GrondError as e: 

840 die(str(e)) 

841 

842 

843def command_forward(args): 

844 

845 from grond.environment import Environment 

846 

847 def setup(parser): 

848 parser.add_option( 

849 '--show', dest='show', metavar='WHAT', 

850 default='filtered', 

851 choices=('filtered', 'processed'), 

852 help='select whether to show only "filtered" or fully "processed" ' 

853 '(i.e. tapered) waveforms (default "%default").') 

854 

855 parser, options, args = cl_parse('forward', args, setup) 

856 if len(args) < 1: 

857 help_and_die(parser, 'missing arguments') 

858 

859 try: 

860 env = Environment(args) 

861 grond.forward(env, show=options.show) 

862 except grond.GrondError as e: 

863 die(str(e)) 

864 

865 

866def command_harvest(args): 

867 def setup(parser): 

868 parser.add_option( 

869 '--force', dest='force', action='store_true', 

870 help='overwrite existing harvest directory') 

871 parser.add_option( 

872 '--neach', dest='neach', type=int, default=10, 

873 help='take NEACH best samples from each chain (default: %default)') 

874 parser.add_option( 

875 '--weed', dest='weed', type=int, default=0, 

876 help='weed out bootstrap samples with bad global performance. ' 

877 '0: no weeding (default), ' 

878 '1: only bootstrap chains where all NEACH best samples ' 

879 'global misfit is less than the global average misfit of all ' 

880 'NEACH best in all chains plus one standard deviation are ' 

881 'included in the harvest ensemble, ' 

882 '2: same as 1 but additionally individual samples are ' 

883 'removed if their global misfit is greater than the global ' 

884 'average misfit of all NEACH best in all chains, ' 

885 '3: harvesting is done on the global chain only, bootstrap ' 

886 'chains are excluded') 

887 parser.add_option( 

888 '--export-fits', dest='export_fits', default='', 

889 help='additionally export details about the fit of individual ' 

890 'targets. "best" - export fits of best model, "mean" - ' 

891 'export fits of ensemble mean model, "ensemble" - export ' 

892 'fits of all models in harvest ensemble.') 

893 

894 details = ''' 

895Can be called after running `grond go` to tweak the results extraction. The 

896user can apply a weeding of bootstrap chains, and define how many best samples 

897of each chain to consider. In addition, `grond harvest` can be used to retrieve 

898the misfits of all targets for best, mean, or the ensemble of solutions. 

899'''.strip() 

900 

901 parser, options, args = cl_parse('harvest', args, setup, details) 

902 if len(args) < 1: 

903 help_and_die(parser, 'no rundir') 

904 

905 export_fits = [] 

906 if options.export_fits.strip(): 

907 export_fits = [x.strip() for x in options.export_fits.split(',')] 

908 

909 for run_path in args: 

910 try: 

911 grond.harvest( 

912 run_path, 

913 force=options.force, 

914 nbest=options.neach, 

915 weed=options.weed, 

916 export_fits=export_fits) 

917 

918 except grond.DirectoryAlreadyExists as e: 

919 die(str(e) + '\n Use --force to overwrite.') 

920 

921 except grond.GrondError as e: 

922 die(str(e)) 

923 

924 

925def command_cluster(args): 

926 from grond import Clustering 

927 from grond.clustering import metrics, methods, read_config, write_config 

928 

929 def setup(parser): 

930 parser.add_option( 

931 '--metric', dest='metric', metavar='METRIC', 

932 default='kagan_angle', 

933 choices=metrics.metrics, 

934 help='metric to measure model distances. Choices: [%s]. Default: ' 

935 'kagan_angle' % ', '.join(metrics.metrics)) 

936 

937 parser.add_option( 

938 '--write-config', 

939 dest='write_config', 

940 metavar='FILE', 

941 help='write configuration (or default configuration) to FILE') 

942 

943 method = args[0] if args else '' 

944 try: 

945 parser, options, args = cl_parse( 

946 'cluster', args[1:], setup=Clustering.cli_setup(method, setup), 

947 details='Available clustering methods: [%s]. Use ' 

948 '"grond cluster <method> --help" to get list of method ' 

949 'dependent options.' % ', '.join(methods)) 

950 

951 if method not in Clustering.name_to_class and not op.exists(method): 

952 help_and_die( 

953 parser, 

954 'no such clustering method: %s' % method if method else 

955 'no clustering method specified') 

956 

957 if op.exists(method): 

958 clustering = read_config(method) 

959 else: 

960 clustering = Clustering.cli_instantiate(method, options) 

961 

962 if options.write_config: 

963 write_config(clustering, options.write_config) 

964 else: 

965 if len(args) != 1: 

966 help_and_die(parser, 'no rundir') 

967 run_path, = args 

968 

969 grond.cluster(run_path, clustering, metric=options.metric) 

970 

971 except grond.GrondError as e: 

972 die(str(e)) 

973 

974 

975def command_plot(args): 

976 

977 def setup(parser): 

978 parser.add_option( 

979 '--show', dest='show', action='store_true', 

980 help='show plot for interactive inspection') 

981 

982 details = ''' 

983Manually plot the inversion results. To get an overview of available plots that 

984can be generated use the command: `grond plot list`. Plots are saved in 

985directory `plots/` inside the run directory. 

986'''.strip() 

987 

988 parser, options, args = cl_parse('plot', args, setup, details) 

989 

990 if not options.show: 

991 import matplotlib 

992 matplotlib.use('Agg') 

993 

994 from grond.environment import Environment 

995 

996 if len(args) not in (1, 2, 3): 

997 help_and_die(parser, '1, 2 or 3 arguments required') 

998 

999 if len(args) > 1: 

1000 env = Environment(args[1:]) 

1001 else: 

1002 env = None 

1003 

1004 from grond import plot 

1005 if args[0] == 'list': 

1006 

1007 def get_doc_title(doc): 

1008 for ln in doc.split('\n'): 

1009 ln = ln.strip() 

1010 if ln != '': 

1011 ln = ln.strip('.') 

1012 return ln 

1013 return 'Undocumented.' 

1014 

1015 if env: 

1016 plot_classes = env.get_plot_classes() 

1017 else: 

1018 plot_classes = plot.get_all_plot_classes() 

1019 

1020 plot_names, plot_doc = zip(*[(pc.name, pc.__doc__) 

1021 for pc in plot_classes]) 

1022 

1023 plot_descs = [get_doc_title(doc) for doc in plot_doc] 

1024 left_spaces = max([len(pn) for pn in plot_names]) 

1025 

1026 for name, desc in zip(plot_names, plot_descs): 

1027 print('{name:<{ls}} - {desc}'.format( 

1028 ls=left_spaces, name=name, desc=desc)) 

1029 

1030 elif args[0] == 'config': 

1031 plot_config_collection = plot.get_plot_config_collection(env) 

1032 print(plot_config_collection) 

1033 

1034 elif args[0] == 'all': 

1035 if env is None: 

1036 help_and_die(parser, 'two or three arguments required') 

1037 plot_names = plot.get_plot_names(env) 

1038 plot.make_plots(env, plot_names=plot_names, show=options.show) 

1039 

1040 elif op.exists(args[0]): 

1041 if env is None: 

1042 help_and_die(parser, 'two or three arguments required') 

1043 plots = plot.PlotConfigCollection.load(args[0]) 

1044 plot.make_plots(env, plots, show=options.show) 

1045 

1046 else: 

1047 if env is None: 

1048 help_and_die(parser, 'two or three arguments required') 

1049 plot_names = [name.strip() for name in args[0].split(',')] 

1050 plot.make_plots(env, plot_names=plot_names, show=options.show) 

1051 

1052 

1053def command_movie(args): 

1054 

1055 import matplotlib 

1056 matplotlib.use('Agg') 

1057 

1058 def setup(parser): 

1059 pass 

1060 

1061 parser, options, args = cl_parse('movie', args, setup) 

1062 

1063 if len(args) != 4: 

1064 help_and_die(parser, 'four arguments required') 

1065 

1066 run_path, xpar_name, ypar_name, movie_filename_template = args 

1067 

1068 from grond import plot 

1069 

1070 movie_filename = movie_filename_template % { 

1071 'xpar': xpar_name, 

1072 'ypar': ypar_name} 

1073 

1074 try: 

1075 plot.make_movie(run_path, xpar_name, ypar_name, movie_filename) 

1076 

1077 except grond.GrondError as e: 

1078 die(str(e)) 

1079 

1080 

1081def command_export(args): 

1082 

1083 def setup(parser): 

1084 parser.add_option( 

1085 '--type', dest='type', metavar='TYPE', 

1086 choices=('event', 'event-yaml', 'source', 'vector'), 

1087 help='select type of objects to be exported. Choices: ' 

1088 '"event" (default), "event-yaml", "source", "vector".') 

1089 

1090 parser.add_option( 

1091 '--parameters', dest='parameters', metavar='PLIST', 

1092 help='select parameters to be exported. PLIST is a ' 

1093 'comma-separated list where each entry has the form ' 

1094 '"<parameter>[.<measure>]". Available measures: "best", ' 

1095 '"mean", "std", "minimum", "percentile16", "median", ' 

1096 '"percentile84", "maximum".') 

1097 

1098 parser.add_option( 

1099 '--selection', dest='selection', metavar='EXPRESSION', 

1100 help='only export data for runs which match EXPRESSION. ' 

1101 'Example expression: "tags_contains:excellent,good"') 

1102 

1103 parser.add_option( 

1104 '--output', dest='filename', metavar='FILE', 

1105 help='write output to FILE') 

1106 

1107 parser.add_option( 

1108 '--effective-lat-lon', dest='effective_lat_lon', 

1109 action='store_true', 

1110 help='convert north_shift/east_shift offsets to true lat/lon ' 

1111 'coordinates (when outputting event objects).') 

1112 

1113 parser, options, args = cl_parse('export', args, setup) 

1114 if len(args) < 2: 

1115 help_and_die(parser, 'arguments required') 

1116 

1117 what = args[0] 

1118 

1119 dirnames = args[1:] 

1120 

1121 what_choices = ('best', 'mean', 'ensemble', 'stats') 

1122 

1123 if what not in what_choices: 

1124 help_and_die( 

1125 parser, 

1126 'invalid choice: %s (choose from %s)' % ( 

1127 repr(what), ', '.join(repr(x) for x in what_choices))) 

1128 

1129 if options.parameters: 

1130 pnames = options.parameters.split(',') 

1131 else: 

1132 pnames = None 

1133 

1134 try: 

1135 grond.export( 

1136 what, 

1137 dirnames, 

1138 filename=options.filename, 

1139 type=options.type, 

1140 pnames=pnames, 

1141 selection=options.selection, 

1142 effective_lat_lon=options.effective_lat_lon) 

1143 

1144 except grond.GrondError as e: 

1145 die(str(e)) 

1146 

1147 

1148def command_tag(args): 

1149 

1150 def setup(parser): 

1151 parser.add_option( 

1152 '-d', '--dir-names', 

1153 dest='show_dirnames', 

1154 action='store_true', 

1155 help='show directory names instead of run names') 

1156 

1157 parser, options, args = cl_parse('tag', args, setup) 

1158 if len(args) < 2: 

1159 help_and_die(parser, 'two or more arguments required') 

1160 

1161 action = args.pop(0) 

1162 

1163 if action not in ('add', 'remove', 'list'): 

1164 help_and_die(parser, 'invalid action: %s' % action) 

1165 

1166 if action in ('add', 'remove'): 

1167 if len(args) < 2: 

1168 help_and_die(parser, 'three or more arguments required') 

1169 

1170 tag = args.pop(0) 

1171 

1172 rundirs = args 

1173 

1174 if action == 'list': 

1175 rundirs = args 

1176 

1177 from grond.environment import Environment 

1178 

1179 errors = False 

1180 for rundir in rundirs: 

1181 try: 

1182 env = Environment([rundir]) 

1183 if options.show_dirnames: 

1184 name = rundir 

1185 else: 

1186 name = env.get_problem().name 

1187 

1188 info = env.get_run_info() 

1189 if action == 'add': 

1190 info.add_tag(tag) 

1191 env.set_run_info(info) 

1192 elif action == 'remove': 

1193 info.remove_tag(tag) 

1194 env.set_run_info(info) 

1195 elif action == 'list': 

1196 print('%-60s : %s' % ( 

1197 name, 

1198 ', '.join(info.tags))) 

1199 

1200 except grond.GrondError as e: 

1201 errors = True 

1202 logger.error(e) 

1203 

1204 if errors: 

1205 die('Errors occurred, see log messages above.') 

1206 

1207 

1208def make_report(env_args, event_name, conf, update_without_plotting): 

1209 from grond.environment import Environment 

1210 from grond.report import report 

1211 try: 

1212 env = Environment(env_args) 

1213 if event_name: 

1214 env.set_current_event_name(event_name) 

1215 

1216 report( 

1217 env, conf, 

1218 update_without_plotting=update_without_plotting, 

1219 make_index=False, 

1220 make_archive=False) 

1221 

1222 env.reset() 

1223 

1224 return True 

1225 

1226 except grond.GrondError as e: 

1227 logger.error(str(e)) 

1228 return False 

1229 

1230 

1231def command_report(args): 

1232 

1233 import matplotlib 

1234 matplotlib.use('Agg') 

1235 

1236 from pyrocko import parimap 

1237 

1238 from grond.environment import Environment 

1239 from grond.report import \ 

1240 report_index, report_archive, serve_ip, serve_report, read_config, \ 

1241 write_config, ReportConfig 

1242 

1243 def setup(parser): 

1244 parser.add_option( 

1245 '--index-only', 

1246 dest='index_only', 

1247 action='store_true', 

1248 help='create index only') 

1249 parser.add_option( 

1250 '--serve', '-s', 

1251 dest='serve', 

1252 action='store_true', 

1253 help='start http service') 

1254 parser.add_option( 

1255 '--serve-external', '-S', 

1256 dest='serve_external', 

1257 action='store_true', 

1258 help='shortcut for --serve --host=default --fixed-port') 

1259 parser.add_option( 

1260 '--host', 

1261 dest='host', 

1262 default='localhost', 

1263 help='<ip> to start the http server on. Special values for ' 

1264 '<ip>: "*" binds to all available interfaces, "default" ' 

1265 'to default external interface, "localhost" to "127.0.0.1".') 

1266 parser.add_option( 

1267 '--port', 

1268 dest='port', 

1269 type=int, 

1270 default=8383, 

1271 help='set default http server port. Will count up if port is ' 

1272 'already in use unless --fixed-port is given.') 

1273 parser.add_option( 

1274 '--fixed-port', 

1275 dest='fixed_port', 

1276 action='store_true', 

1277 help='fail if port is already in use') 

1278 parser.add_option( 

1279 '--open', '-o', 

1280 dest='open', 

1281 action='store_true', 

1282 help='open report in browser') 

1283 parser.add_option( 

1284 '--config', 

1285 dest='config', 

1286 metavar='FILE', 

1287 help='report configuration file to use') 

1288 parser.add_option( 

1289 '--write-config', 

1290 dest='write_config', 

1291 metavar='FILE', 

1292 help='write configuration (or default configuration) to FILE') 

1293 parser.add_option( 

1294 '--update-without-plotting', 

1295 dest='update_without_plotting', 

1296 action='store_true', 

1297 help='quick-and-dirty update parameter files without plotting') 

1298 parser.add_option( 

1299 '--archive', 

1300 dest='make_archive', 

1301 default=None, 

1302 action='store_true', 

1303 help='create archive file, even if `make_archive` in the report ' 

1304 'configuration file is set to `false`. The config file ' 

1305 'default creates no archive.') 

1306 parser.add_option( 

1307 '--no-archive', 

1308 dest='make_archive', 

1309 default=None, 

1310 action='store_false', 

1311 help='do not create archive file, even if `make_archive` in the ' 

1312 'report configuration file is set to `true`. The config file ' 

1313 'default creates no archive.') 

1314 

1315 parser, options, args = cl_parse('report', args, setup) 

1316 

1317 s_conf = '' 

1318 if options.config: 

1319 try: 

1320 conf = read_config(options.config) 

1321 except grond.GrondError as e: 

1322 die(str(e)) 

1323 

1324 s_conf = ' --config="%s"' % options.config 

1325 else: 

1326 from grond import plot 

1327 conf = ReportConfig( 

1328 plot_config_collection=plot.get_plot_config_collection()) 

1329 conf.set_basepath('.') 

1330 

1331 if options.write_config: 

1332 try: 

1333 write_config(conf, options.write_config) 

1334 sys.exit(0) 

1335 

1336 except grond.GrondError as e: 

1337 die(str(e)) 

1338 

1339 # commandline options that can override config values 

1340 if options.make_archive is not None: 

1341 conf.make_archive = options.make_archive 

1342 

1343 if len(args) == 1 and op.exists(op.join(args[0], 'index.html')): 

1344 conf.report_base_path = conf.rel_path(args[0]) 

1345 s_conf = ' %s' % args[0] 

1346 args = [] 

1347 

1348 report_base_path = conf.expand_path(conf.report_base_path) 

1349 

1350 if options.index_only: 

1351 report_index(conf) 

1352 report_archive(conf) 

1353 args = [] 

1354 

1355 entries_generated = False 

1356 

1357 payload = [] 

1358 if args and all(op.isdir(rundir) for rundir in args): 

1359 rundirs = args 

1360 all_failed = True 

1361 for rundir in rundirs: 

1362 payload.append(( 

1363 [rundir], None, conf, options.update_without_plotting)) 

1364 

1365 elif args: 

1366 try: 

1367 env = Environment(args) 

1368 for event_name in env.get_selected_event_names(): 

1369 payload.append(( 

1370 args, event_name, conf, options.update_without_plotting)) 

1371 

1372 except grond.GrondError as e: 

1373 die(str(e)) 

1374 

1375 if payload: 

1376 entries_generated = [] 

1377 for result in parimap.parimap( 

1378 make_report, *zip(*payload), nprocs=options.nparallel): 

1379 

1380 entries_generated.append(result) 

1381 

1382 all_failed = not any(entries_generated) 

1383 entries_generated = any(entries_generated) 

1384 

1385 if all_failed: 

1386 die('no report entries generated') 

1387 

1388 report_index(conf) 

1389 report_archive(conf) 

1390 

1391 if options.serve or options.serve_external: 

1392 if options.serve_external: 

1393 host = 'default' 

1394 else: 

1395 host = options.host 

1396 

1397 addr = serve_ip(host), options.port 

1398 

1399 serve_report( 

1400 addr, 

1401 report_config=conf, 

1402 fixed_port=options.fixed_port or options.serve_external, 

1403 open=options.open) 

1404 

1405 elif options.open: 

1406 import webbrowser 

1407 url = 'file://%s/index.html' % op.abspath(report_base_path) 

1408 webbrowser.open(url) 

1409 

1410 else: 

1411 if not entries_generated and not options.index_only: 

1412 logger.info('Nothing to do, see: grond report --help') 

1413 

1414 if entries_generated and not (options.serve or options.serve_external): 

1415 logger.info(CLIHints('report', config=s_conf)) 

1416 

1417 

1418def command_qc_polarization(args): 

1419 

1420 def setup(parser): 

1421 parser.add_option( 

1422 '--time-factor-pre', dest='time_factor_pre', type=float, 

1423 metavar='NUMBER', 

1424 default=0.5, 

1425 help='set duration to extract before synthetic P phase arrival, ' 

1426 'relative to 1/fmin. fmin is taken from the selected target ' 

1427 'group in the config file (default=%default)') 

1428 parser.add_option( 

1429 '--time-factor-post', dest='time_factor_post', type=float, 

1430 metavar='NUMBER', 

1431 default=0.5, 

1432 help='set duration to extract after synthetic P phase arrival, ' 

1433 'relative to 1/fmin. fmin is taken from the selected target ' 

1434 'group in the config file (default=%default)') 

1435 parser.add_option( 

1436 '--distance-min', dest='distance_min', type=float, 

1437 metavar='NUMBER', 

1438 help='minimum event-station distance [m]') 

1439 parser.add_option( 

1440 '--distance-max', dest='distance_max', type=float, 

1441 metavar='NUMBER', 

1442 help='maximum event-station distance [m]') 

1443 parser.add_option( 

1444 '--depth-min', dest='depth_min', type=float, 

1445 metavar='NUMBER', 

1446 help='minimum station depth [m]') 

1447 parser.add_option( 

1448 '--depth-max', dest='depth_max', type=float, 

1449 metavar='NUMBER', 

1450 help='maximum station depth [m]') 

1451 parser.add_option( 

1452 '--picks', dest='picks_filename', 

1453 metavar='FILENAME', 

1454 help='add file with P picks in Snuffler marker format') 

1455 parser.add_option( 

1456 '--save', dest='output_filename', 

1457 metavar='FILENAME.FORMAT', 

1458 help='save output to file FILENAME.FORMAT') 

1459 parser.add_option( 

1460 '--dpi', dest='output_dpi', type=float, default=120., 

1461 metavar='NUMBER', 

1462 help='DPI setting for raster formats (default=120)') 

1463 

1464 parser, options, args = cl_parse('qc-polarization', args, setup) 

1465 if len(args) != 3: 

1466 help_and_die(parser, 'missing arguments') 

1467 

1468 if options.output_filename: 

1469 import matplotlib 

1470 matplotlib.use('Agg') 

1471 

1472 import grond.qc 

1473 

1474 config_path, event_name, target_group_path = args 

1475 

1476 try: 

1477 config = grond.read_config(config_path) 

1478 except grond.GrondError as e: 

1479 die(str(e)) 

1480 

1481 ds = config.get_dataset(event_name) 

1482 

1483 engine = config.engine_config.get_engine() 

1484 

1485 nsl_to_time = None 

1486 if options.picks_filename: 

1487 markers = marker.load_markers(options.picks_filename) 

1488 marker.associate_phases_to_events(markers) 

1489 

1490 nsl_to_time = {} 

1491 for m in markers: 

1492 if isinstance(m, marker.PhaseMarker): 

1493 ev = m.get_event() 

1494 if ev is not None and ev.name == event_name: 

1495 nsl_to_time[m.one_nslc()[:3]] = m.tmin 

1496 

1497 if not nsl_to_time: 

1498 help_and_die( 

1499 parser, 

1500 'no markers associated with event "%s" found in file "%s"' % ( 

1501 event_name, options.picks_filename)) 

1502 

1503 target_group_paths_avail = [] 

1504 for target_group in config.target_groups: 

1505 name = target_group.path 

1506 if name == target_group_path: 

1507 imc = target_group.misfit_config 

1508 fmin = imc.fmin 

1509 fmax = imc.fmax 

1510 ffactor = imc.ffactor 

1511 

1512 store = engine.get_store(target_group.store_id) 

1513 timing = '{cake:P|cake:p|cake:P\\|cake:p\\}' 

1514 

1515 grond.qc.polarization( 

1516 ds, store, timing, fmin=fmin, fmax=fmax, ffactor=ffactor, 

1517 time_factor_pre=options.time_factor_pre, 

1518 time_factor_post=options.time_factor_post, 

1519 distance_min=options.distance_min, 

1520 distance_max=options.distance_max, 

1521 depth_min=options.depth_min, 

1522 depth_max=options.depth_max, 

1523 nsl_to_time=nsl_to_time, 

1524 output_filename=options.output_filename, 

1525 output_dpi=options.output_dpi) 

1526 

1527 return 

1528 

1529 target_group_paths_avail.append(name) 

1530 

1531 die('no target group with path "%s" found. Available: %s' % ( 

1532 target_group_path, ', '.join(target_group_paths_avail))) 

1533 

1534 

1535def command_upgrade_config(args): 

1536 def setup(parser): 

1537 parser.add_option( 

1538 '--diff', dest='diff', action='store_true', 

1539 help='create diff between normalized old and new versions') 

1540 

1541 parser, options, args = cl_parse('upgrade-config', args, setup) 

1542 if len(args) != 1: 

1543 help_and_die(parser, 'missing argument <configfile>') 

1544 

1545 from grond import upgrade 

1546 upgrade.upgrade_config_file(args[0], diff=options.diff) 

1547 

1548 

1549def command_diff(args): 

1550 def setup(parser): 

1551 pass 

1552 

1553 parser, options, args = cl_parse('diff', args, setup) 

1554 if len(args) != 2: 

1555 help_and_die(parser, 'requires exactly two arguments') 

1556 

1557 from grond.config import diff_configs 

1558 diff_configs(*args) 

1559 

1560 

1561def command_version(args): 

1562 def setup(parser): 

1563 parser.add_option( 

1564 '--short', dest='short', action='store_true', 

1565 help='only print Grond\'s version number') 

1566 parser.add_option( 

1567 '--failsafe', dest='failsafe', action='store_true', 

1568 help='do not get irritated when some dependencies are missing') 

1569 

1570 parser, options, args = cl_parse('version', args, setup) 

1571 

1572 if options.short: 

1573 print(grond.__version__) 

1574 return 

1575 

1576 elif not options.failsafe: 

1577 from grond import info 

1578 print(info.version_info()) 

1579 return 

1580 

1581 print("grond: %s" % grond.__version__) 

1582 

1583 try: 

1584 import pyrocko 

1585 print('pyrocko: %s' % pyrocko.long_version) 

1586 except ImportError: 

1587 print('pyrocko: N/A') 

1588 

1589 try: 

1590 import numpy 

1591 print('numpy: %s' % numpy.__version__) 

1592 except ImportError: 

1593 print('numpy: N/A') 

1594 

1595 try: 

1596 import scipy 

1597 print('scipy: %s' % scipy.__version__) 

1598 except ImportError: 

1599 print('scipy: N/A') 

1600 

1601 try: 

1602 import matplotlib 

1603 print('matplotlib: %s' % matplotlib.__version__) 

1604 except ImportError: 

1605 print('matplotlib: N/A') 

1606 

1607 try: 

1608 from pyrocko.gui.qt_compat import Qt 

1609 print('PyQt: %s' % Qt.PYQT_VERSION_STR) 

1610 print('Qt: %s' % Qt.QT_VERSION_STR) 

1611 except ImportError: 

1612 print('PyQt: N/A') 

1613 print('Qt: N/A') 

1614 

1615 import sys 

1616 print('python: %s.%s.%s' % sys.version_info[:3]) 

1617 

1618 if not options.failsafe: 

1619 die('fell back to failsafe version printing') 

1620 

1621 

1622if __name__ == '__main__': 

1623 main()