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

744 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-10-25 10:12 +0000

1#!/usr/bin/env python 

2 

3from __future__ import print_function, absolute_import 

4 

5import sys 

6import os.path as op 

7import logging 

8from optparse import OptionParser, OptionValueError 

9import grond 

10from io import StringIO 

11 

12try: 

13 from pyrocko import util, marker 

14except ImportError: 

15 print('Pyrocko is required for Grond!' 

16 'Go to https://pyrocko.org/ for installation instructions.') 

17 

18 

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

20km = 1e3 

21 

22 

23class Color: 

24 PURPLE = '\033[95m' 

25 CYAN = '\033[96m' 

26 DARKCYAN = '\033[36m' 

27 BLUE = '\033[94m' 

28 GREEN = '\033[92m' 

29 YELLOW = '\033[93m' 

30 RED = '\033[91m' 

31 BOLD = '\033[1m' 

32 UNDERLINE = '\033[4m' 

33 END = '\033[0m' 

34 

35 

36def d2u(d): 

37 if isinstance(d, dict): 

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

39 else: 

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

41 

42 

43subcommand_descriptions = { 

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

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

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

47 'check': 'check data and configuration', 

48 'go': 'run Grond optimisation', 

49 'continue': 'continue a Grond optimisation in case of an' 

50 ' interruption or more iterations are needed', 

51 'forward': 'run forward modelling', 

52 'harvest': 'manually run harvesting', 

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

54 'plot': 'plot optimisation result', 

55 'movie': 'visualize optimiser evolution', 

56 'export': 'export results', 

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

58 'report': 'create result report', 

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

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

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

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

63} 

64 

65subcommand_usages = { 

66 'init': ( 

67 'init list [options]', 

68 'init <example> [options]', 

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

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

71 'events': 'events <configfile>', 

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

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

74 'continue': 'continue <configfile> <eventnames> ... [options]', 

75 'forward': ( 

76 'forward <rundir> [options]', 

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

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

79 'cluster': ( 

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

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

82 'plot': ( 

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

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

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

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

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

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

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

90 'tag': ( 

91 'tag add <tag> <rundir>', 

92 'tag remove <tag> <rundir>', 

93 'tag list <rundir>'), 

94 'report': ( 

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

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

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

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

99 '<target_group_path> [options]', 

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

101 'version': 'version', 

102} 

103 

104subcommands = subcommand_descriptions.keys() 

105 

106program_name = 'grond' 

107 

108usage_tdata = d2u(subcommand_descriptions) 

109usage_tdata['program_name'] = program_name 

110usage_tdata['version_number'] = grond.__version__ 

111 

112 

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

114 

115Grond is a probabilistic earthquake source inversion framework. 

116 

117This is Grond version %(version_number)s. 

118 

119Subcommands: 

120 

121 scenario %(scenario)s 

122 init %(init)s 

123 events %(events)s 

124 check %(check)s 

125 go %(go)s 

126 continue %(continue)s 

127 forward %(forward)s 

128 harvest %(harvest)s 

129 cluster %(cluster)s 

130 plot %(plot)s 

131 movie %(movie)s 

132 export %(export)s 

133 tag %(tag)s 

134 report %(report)s 

135 diff %(diff)s 

136 qc-polarization %(qc_polarization)s 

137 upgrade-config %(upgrade_config)s 

138 version %(version)s 

139 

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

141 

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

143 

144What do you want to bust today?! 

145''' % usage_tdata 

146 

147 

148class CLIHints(object): 

149 init = ''' 

150We created a folder structure in {project_dir}. 

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

152 

153 grond go {config} 

154''' 

155 scenario = ''' 

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

157 

158 cd {project_dir} 

159 

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

161 

162 grond go {config} 

163''' 

164 report = ''' 

165To open the report in your web browser, run 

166 

167 grond report -s --open {config} 

168''' 

169 check = ''' 

170To start the optimisation, run 

171 

172 grond go {config} 

173''' 

174 go = ''' 

175To look at the results, run 

176 

177 grond report -so {rundir} 

178''' 

179 

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

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

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

183 

184 

185def main(args=None): 

186 if not args: 

187 args = sys.argv 

188 

189 args = list(args) 

190 if len(args) < 2: 

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

192 

193 args.pop(0) 

194 command = args.pop(0) 

195 

196 if command in subcommands: 

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

198 

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

200 if command == 'help' and args: 

201 acommand = args[0] 

202 if acommand in subcommands: 

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

204 

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

206 

207 else: 

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

209 

210 

211def add_common_options(parser): 

212 parser.add_option( 

213 '--loglevel', 

214 action='store', 

215 dest='loglevel', 

216 type='choice', 

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

218 default='info', 

219 help='set logger level to ' 

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

221 'Default is "%default".') 

222 

223 parser.add_option( 

224 '--docs', 

225 dest='rst_docs', 

226 action='store_true') 

227 

228 

229def print_docs(command, parser): 

230 

231 from optparse import IndentedHelpFormatter 

232 

233 class DocsFormatter(IndentedHelpFormatter): 

234 

235 def format_heading(self, heading): 

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

237 

238 def format_usage(self, usage): 

239 lines = usage.splitlines() 

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

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

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

243 

244 def format_option(self, option): 

245 if not option.help: 

246 return '' 

247 

248 result = [] 

249 opts = self.option_strings[option] 

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

251 

252 help_text = self.expand_default(option) 

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

254 

255 return ''.join(result) 

256 

257 parser.formatter = DocsFormatter() 

258 parser.formatter.set_parser(parser) 

259 

260 def format_help(parser): 

261 formatter = parser.formatter 

262 result = [] 

263 

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

265 

266 if parser.usage: 

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

268 

269 result.append('\n') 

270 

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

272 

273 result.append('\n') 

274 

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

276 return "".join(result) 

277 

278 print(command) 

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

280 print() 

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

282 print() 

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

284 print() 

285 print(format_help(parser)) 

286 

287 

288def process_common_options(command, parser, options): 

289 util.setup_logging(program_name, options.loglevel) 

290 if options.rst_docs: 

291 print_docs(command, parser) 

292 exit(0) 

293 

294 

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

296 usage = subcommand_usages[command] 

297 descr = subcommand_descriptions[command] 

298 

299 if isinstance(usage, str): 

300 usage = [usage] 

301 

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

303 for s in usage[1:]: 

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

305 

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

307 

308 if details: 

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

310 

311 parser = OptionParser(usage=susage, description=description) 

312 

313 if setup: 

314 setup(parser) 

315 

316 add_common_options(parser) 

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

318 process_common_options(command, parser, options) 

319 return parser, options, args 

320 

321 

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

323 if prelude: 

324 prelude = prelude + '\n' 

325 

326 if err: 

327 err = '\n' + err 

328 

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

330 

331 

332def help_and_die(parser, message): 

333 sio = StringIO() 

334 parser.print_help(sio) 

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

336 

337 

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

339 options = value.split(',') 

340 for opt in options: 

341 if opt not in choices: 

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

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

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

345 

346 

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

348 mag_range = value.split('-') 

349 if len(mag_range) != 2: 

350 raise OptionValueError( 

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

352 try: 

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

354 except ValueError: 

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

356 

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

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

359 ' maximum magnitude.') 

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

361 

362 

363def command_scenario(args): 

364 

365 STORE_STATIC = 'crust2_ib_static' 

366 STORE_WAVEFORMS = 'crust2_ib' 

367 

368 def setup(parser): 

369 parser.add_option( 

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

371 callback=multiple_choice, callback_kwargs={ 

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

373 }, 

374 default='waveforms', 

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

376 ' waveforms, gnss and insar. Multiple selection by ' 

377 '--targets=waveforms,gnss,insar' 

378 '(default: --targets=%default)') 

379 parser.add_option( 

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

381 type='choice', choices=['cmt', 'rectangular', 'dynamic_rupture'], 

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

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

384 '\'dynamic_rupture\' for a dynamic rupture scenario.' 

385 ' (default: \'%default\')') 

386 parser.add_option( 

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

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

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

390 parser.add_option( 

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

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

393 parser.add_option( 

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

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

396 ' (default: %default)') 

397 parser.add_option( 

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

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

400 parser.add_option( 

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

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

403 parser.add_option( 

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

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

406 parser.add_option( 

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

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

409 parser.add_option( 

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

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

412 parser.add_option( 

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

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

415 '(default: %default)') 

416 parser.add_option( 

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

418 default=None, 

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

420 '(default: %default)') 

421 parser.add_option( 

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

423 default=STORE_WAVEFORMS, 

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

425 '(default: %default)') 

426 parser.add_option( 

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

428 default=STORE_STATIC, 

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

430 '(default: %default)') 

431 parser.add_option( 

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

433 help='overwrite existing project folder.') 

434 parser.add_option( 

435 '--gf-store-superdirs', 

436 dest='gf_store_superdirs', 

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

438 parser.add_option( 

439 '--no-map', 

440 dest='make_map', 

441 default=True, 

442 action='store_false', 

443 help='suppress generation of map') 

444 parser.add_option( 

445 '--rebuild', 

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

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

448 

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

450 

451 gf_store_superdirs = None 

452 if options.gf_store_superdirs: 

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

454 

455 if len(args) == 1: 

456 project_dir = args[0] 

457 else: 

458 parser.print_help() 

459 sys.exit(1) 

460 

461 from grond import scenario as grond_scenario 

462 

463 try: 

464 scenario = grond_scenario.GrondScenario( 

465 project_dir, 

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

467 radius=options.radius*km) 

468 

469 scenario.rebuild = options.rebuild 

470 if options.rebuild: 

471 options.force = True 

472 

473 if 'waveforms' in options.targets: 

474 if options.stationxml_paths: 

475 options.stationxml_paths = [ 

476 op.abspath(path) for path in 

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

478 

479 if options.stations_paths: 

480 options.stations_paths = [ 

481 op.abspath(path) for path in 

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

483 

484 obs = grond_scenario.WaveformObservation( 

485 nstations=options.nstations, 

486 store_id=options.store_waveforms, 

487 stations_paths=options.stations_paths, 

488 stationxml_paths=options.stationxml_paths) 

489 scenario.add_observation(obs) 

490 

491 if 'insar' in options.targets: 

492 obs = grond_scenario.InSARObservation( 

493 store_id=options.store_statics) 

494 scenario.add_observation(obs) 

495 

496 if 'gnss' in options.targets: 

497 obs = grond_scenario.GNSSCampaignObservation( 

498 nstations=options.gnss_nstations, 

499 store_id=options.store_statics) 

500 scenario.add_observation(obs) 

501 

502 src_args = dict( 

503 nevents=options.nevents, 

504 magnitude_min=options.magnitude_range[0], 

505 magnitude_max=options.magnitude_range[1]) 

506 if options.problem == 'cmt': 

507 problem = grond_scenario.DCSourceProblem(**src_args) 

508 elif options.problem == 'rectangular': 

509 problem = grond_scenario.RectangularSourceProblem(**src_args) 

510 elif options.problem == 'dynamic_rupture': 

511 problem = grond_scenario.PseudoDynamicRuptureProblem(**src_args) 

512 scenario.set_problem(problem) 

513 

514 scenario.build( 

515 force=options.force, 

516 interactive=True, 

517 gf_store_superdirs=gf_store_superdirs, 

518 make_map=options.make_map) 

519 

520 logger.info(CLIHints('scenario', 

521 config=scenario.get_grond_config_path(), 

522 project_dir=project_dir)) 

523 

524 except grond.GrondError as e: 

525 die(str(e)) 

526 

527 

528def command_init(args): 

529 

530 from .cmd_init import GrondInit 

531 

532 grond_init = GrondInit() 

533 

534 def print_section(entries): 

535 if len(entries) == 0: 

536 return '\tNone available.' 

537 

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

539 rstr = [] 

540 lcat = None 

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

542 

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

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

545 rstr.append('') 

546 lcat = cat 

547 

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

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

550 return '\n'.join(rstr) 

551 

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

553 

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

555 

556 Deploy a full project structure into a directory. 

557 

558 usage: grond init <example> <projectdir> 

559 

560 where <example> is any of the following: 

561 

562{examples_list} 

563 

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

565 

566 Print out configuration snippets for various components. 

567 

568 usage: grond init <section> 

569 

570 where <section> is any of the following: 

571 

572{sections_list} 

573'''.format(c=Color, 

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

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

576 

577 def setup(parser): 

578 parser.add_option( 

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

580 

581 parser, options, args = cl_parse( 

582 'init', args, setup, 

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

584 

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

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

587 

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

589 print(help_text) 

590 return 

591 

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

593 if len(args) == 1: 

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

595 if not config: 

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

597 

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

599 

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

601 '<projectdir>') 

602 

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

604 help_and_die( 

605 parser, 

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

607 % args[1]) 

608 else: 

609 try: 

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

611 except OSError as e: 

612 print(str(e)) 

613 

614 else: 

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

616 if not sec: 

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

618 

619 sys.stdout.write(sec) 

620 

621 

622def command_init_old(args): 

623 

624 from . import cmd_init as init 

625 

626 def setup(parser): 

627 parser.add_option( 

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

629 callback=multiple_choice, callback_kwargs={ 

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

631 }, 

632 default='waveforms', 

633 help='select from:' 

634 ' waveforms, gnss and insar. ' 

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

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

637 parser.add_option( 

638 '--problem', dest='problem', 

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

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

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

642 ' (default: \'%default\')') 

643 parser.add_option( 

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

645 help='overwrite existing project folder') 

646 

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

648 

649 try: 

650 project = init.GrondProject() 

651 

652 if 'all' in options.targets: 

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

654 else: 

655 targets = options.targets 

656 

657 if not options.problem: 

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

659 problem = 'rectangular' 

660 else: 

661 problem = 'cmt' 

662 else: 

663 problem = options.problem 

664 

665 if problem == 'rectangular': 

666 project.set_rectangular_source() 

667 elif problem == 'cmt': 

668 project.set_cmt_source() 

669 

670 if 'waveforms' in targets: 

671 project.add_waveforms() 

672 

673 if 'insar' in targets: 

674 project.add_insar() 

675 

676 if 'gnss' in targets: 

677 project.add_gnss() 

678 

679 if len(args) == 1: 

680 project_dir = args[0] 

681 project.build(project_dir, options.force) 

682 logger.info(CLIHints( 

683 'init', project_dir=project_dir, 

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

685 else: 

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

687 

688 except grond.GrondError as e: 

689 die(str(e)) 

690 

691 

692def command_events(args): 

693 def setup(parser): 

694 pass 

695 

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

697 if len(args) != 1: 

698 help_and_die(parser, 'missing arguments') 

699 

700 config_path = args[0] 

701 try: 

702 config = grond.read_config(config_path) 

703 

704 for event_name in grond.get_event_names(config): 

705 print(event_name) 

706 

707 except grond.GrondError as e: 

708 die(str(e)) 

709 

710 

711def command_check(args): 

712 

713 from grond.environment import Environment 

714 

715 def setup(parser): 

716 parser.add_option( 

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

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

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

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

721 

722 parser.add_option( 

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

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

725 

726 parser.add_option( 

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

728 default=10, 

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

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

731 'solution.') 

732 

733 parser.add_option( 

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

735 metavar='FILENAME', 

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

737 

738 parser, options, args = cl_parse('check', args, setup) 

739 if len(args) < 1: 

740 help_and_die(parser, 'missing arguments') 

741 

742 try: 

743 env = Environment(args) 

744 config = env.get_config() 

745 

746 target_string_ids = None 

747 if options.target_string_ids: 

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

749 

750 grond.check( 

751 config, 

752 event_names=env.get_selected_event_names(), 

753 target_string_ids=target_string_ids, 

754 show_waveforms=options.show_waveforms, 

755 n_random_synthetics=options.n_random_synthetics, 

756 stations_used_path=options.stations_used_path) 

757 

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

759 

760 except grond.GrondError as e: 

761 die(str(e)) 

762 

763 

764def command_go(args): 

765 

766 from grond.environment import Environment 

767 

768 def setup(parser): 

769 parser.add_option( 

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

771 help='overwrite existing run directory') 

772 parser.add_option( 

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

774 help='preserve old rundir') 

775 parser.add_option( 

776 '--status', dest='status', default='state', 

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

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

779 'state)') 

780 parser.add_option( 

781 '--parallel', dest='nparallel', type=int, default=1, 

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

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

784 parser.add_option( 

785 '--threads', dest='nthreads', type=int, default=1, 

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

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

788 

789 parser, options, args = cl_parse('go', args, setup) 

790 

791 try: 

792 env = Environment(args) 

793 

794 status = options.status 

795 if options.nparallel != 1: 

796 status = 'quiet' 

797 

798 grond.go( 

799 env, 

800 force=options.force, 

801 preserve=options.preserve, 

802 status=status, 

803 nparallel=options.nparallel, 

804 nthreads=options.nthreads) 

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

806 logger.info(CLIHints( 

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

808 

809 except grond.GrondError as e: 

810 die(str(e)) 

811 

812 

813def command_continue(args): 

814 

815 from grond.environment import Environment 

816 

817 def setup(parser): 

818 parser.add_option( 

819 '--no-preserve', dest='no_preserve', action='store_true', 

820 help='do not preserve old rundir') 

821 parser.add_option( 

822 '--status', dest='status', default='state', 

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

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

825 'state)') 

826 parser.add_option( 

827 '--parallel', dest='nparallel', type=int, default=1, 

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

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

830 parser.add_option( 

831 '--threads', dest='nthreads', type=int, default=1, 

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

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

834 

835 parser, options, args = cl_parse('continue', args, setup) 

836 

837 try: 

838 env = Environment(args) 

839 

840 status = options.status 

841 if options.nparallel != 1: 

842 status = 'quiet' 

843 

844 grond.continue_run( 

845 env, 

846 preserve=~bool(options.no_preserve), 

847 status=status, 

848 nparallel=options.nparallel, 

849 nthreads=options.nthreads) 

850 

851 except grond.GrondError as e: 

852 die(str(e)) 

853 

854 

855def command_forward(args): 

856 

857 from grond.environment import Environment 

858 

859 def setup(parser): 

860 parser.add_option( 

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

862 default='filtered', 

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

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

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

866 

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

868 if len(args) < 1: 

869 help_and_die(parser, 'missing arguments') 

870 

871 try: 

872 env = Environment(args) 

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

874 except grond.GrondError as e: 

875 die(str(e)) 

876 

877 

878def command_harvest(args): 

879 def setup(parser): 

880 parser.add_option( 

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

882 help='overwrite existing harvest directory') 

883 parser.add_option( 

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

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

886 parser.add_option( 

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

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

889 '0: no weeding (default), ' 

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

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

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

893 'included in the harvest ensemble, ' 

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

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

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

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

898 'chains are excluded') 

899 parser.add_option( 

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

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

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

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

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

905 

906 parser, options, args = cl_parse('harvest', args, setup) 

907 if len(args) < 1: 

908 help_and_die(parser, 'no rundir') 

909 

910 export_fits = [] 

911 if options.export_fits.strip(): 

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

913 

914 for run_path in args: 

915 try: 

916 grond.harvest( 

917 run_path, 

918 force=options.force, 

919 nbest=options.neach, 

920 weed=options.weed, 

921 export_fits=export_fits) 

922 

923 except grond.DirectoryAlreadyExists as e: 

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

925 

926 except grond.GrondError as e: 

927 die(str(e)) 

928 

929 

930def command_cluster(args): 

931 from grond import Clustering 

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

933 

934 def setup(parser): 

935 parser.add_option( 

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

937 default='kagan_angle', 

938 choices=metrics.metrics, 

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

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

941 

942 parser.add_option( 

943 '--write-config', 

944 dest='write_config', 

945 metavar='FILE', 

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

947 

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

949 try: 

950 parser, options, args = cl_parse( 

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

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

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

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

955 

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

957 help_and_die( 

958 parser, 

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

960 'no clustering method specified') 

961 

962 if op.exists(method): 

963 clustering = read_config(method) 

964 else: 

965 clustering = Clustering.cli_instantiate(method, options) 

966 

967 if options.write_config: 

968 write_config(clustering, options.write_config) 

969 else: 

970 if len(args) != 1: 

971 help_and_die(parser, 'no rundir') 

972 run_path, = args 

973 

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

975 

976 except grond.GrondError as e: 

977 die(str(e)) 

978 

979 

980def command_plot(args): 

981 

982 def setup(parser): 

983 parser.add_option( 

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

985 help='show plot for interactive inspection') 

986 

987 details = '' 

988 

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

990 

991 if not options.show: 

992 import matplotlib 

993 matplotlib.use('Agg') 

994 

995 from grond.environment import Environment 

996 

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

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

999 

1000 if len(args) > 1: 

1001 env = Environment(args[1:]) 

1002 else: 

1003 env = None 

1004 

1005 from grond import plot 

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

1007 

1008 def get_doc_title(doc): 

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

1010 ln = ln.strip() 

1011 if ln != '': 

1012 ln = ln.strip('.') 

1013 return ln 

1014 return 'Undocumented.' 

1015 

1016 if env: 

1017 plot_classes = env.get_plot_classes() 

1018 else: 

1019 plot_classes = plot.get_all_plot_classes() 

1020 

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

1022 for pc in plot_classes]) 

1023 

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

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

1026 

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

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

1029 ls=left_spaces, name=name, desc=desc)) 

1030 

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

1032 plot_config_collection = plot.get_plot_config_collection(env) 

1033 print(plot_config_collection) 

1034 

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

1036 if env is None: 

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

1038 plot_names = plot.get_plot_names(env) 

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

1040 

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

1042 if env is None: 

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

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

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

1046 

1047 else: 

1048 if env is None: 

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

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

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

1052 

1053 

1054def command_movie(args): 

1055 

1056 import matplotlib 

1057 matplotlib.use('Agg') 

1058 

1059 def setup(parser): 

1060 pass 

1061 

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

1063 

1064 if len(args) != 4: 

1065 help_and_die(parser, 'four arguments required') 

1066 

1067 run_path, xpar_name, ypar_name, movie_filename_template = args 

1068 

1069 from grond import plot 

1070 

1071 movie_filename = movie_filename_template % { 

1072 'xpar': xpar_name, 

1073 'ypar': ypar_name} 

1074 

1075 try: 

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

1077 

1078 except grond.GrondError as e: 

1079 die(str(e)) 

1080 

1081 

1082def command_export(args): 

1083 

1084 def setup(parser): 

1085 parser.add_option( 

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

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

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

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

1090 

1091 parser.add_option( 

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

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

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

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

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

1097 '"percentile84", "maximum".') 

1098 

1099 parser.add_option( 

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

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

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

1103 

1104 parser.add_option( 

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

1106 help='write output to FILE') 

1107 

1108 parser.add_option( 

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

1110 action='store_true', 

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

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

1113 

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

1115 if len(args) < 2: 

1116 help_and_die(parser, 'arguments required') 

1117 

1118 what = args[0] 

1119 

1120 dirnames = args[1:] 

1121 

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

1123 

1124 if what not in what_choices: 

1125 help_and_die( 

1126 parser, 

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

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

1129 

1130 if options.parameters: 

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

1132 else: 

1133 pnames = None 

1134 

1135 try: 

1136 grond.export( 

1137 what, 

1138 dirnames, 

1139 filename=options.filename, 

1140 type=options.type, 

1141 pnames=pnames, 

1142 selection=options.selection, 

1143 effective_lat_lon=options.effective_lat_lon) 

1144 

1145 except grond.GrondError as e: 

1146 die(str(e)) 

1147 

1148 

1149def command_tag(args): 

1150 

1151 def setup(parser): 

1152 parser.add_option( 

1153 '-d', '--dir-names', 

1154 dest='show_dirnames', 

1155 action='store_true', 

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

1157 

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

1159 if len(args) < 2: 

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

1161 

1162 action = args.pop(0) 

1163 

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

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

1166 

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

1168 if len(args) < 2: 

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

1170 

1171 tag = args.pop(0) 

1172 

1173 rundirs = args 

1174 

1175 if action == 'list': 

1176 rundirs = args 

1177 

1178 from grond.environment import Environment 

1179 

1180 errors = False 

1181 for rundir in rundirs: 

1182 try: 

1183 env = Environment([rundir]) 

1184 if options.show_dirnames: 

1185 name = rundir 

1186 else: 

1187 name = env.get_problem().name 

1188 

1189 info = env.get_run_info() 

1190 if action == 'add': 

1191 info.add_tag(tag) 

1192 env.set_run_info(info) 

1193 elif action == 'remove': 

1194 info.remove_tag(tag) 

1195 env.set_run_info(info) 

1196 elif action == 'list': 

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

1198 name, 

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

1200 

1201 except grond.GrondError as e: 

1202 errors = True 

1203 logger.error(e) 

1204 

1205 if errors: 

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

1207 

1208 

1209def make_report(env_args, event_name, conf, update_without_plotting, nthreads): 

1210 from grond.environment import Environment 

1211 from grond.report import report 

1212 try: 

1213 env = Environment(env_args) 

1214 if event_name: 

1215 env.set_current_event_name(event_name) 

1216 

1217 report( 

1218 env, conf, 

1219 update_without_plotting=update_without_plotting, 

1220 make_index=False, 

1221 make_archive=False, 

1222 nthreads=nthreads) 

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 '--parallel', dest='nparallel', type=int, default=1, 

1300 help='set number of runs to process in parallel, ' 

1301 'If set to more than one, --status=quiet is implied.') 

1302 parser.add_option( 

1303 '--threads', dest='nthreads', type=int, default=1, 

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

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

1306 parser.add_option( 

1307 '--no-archive', 

1308 dest='no_archive', 

1309 action='store_true', 

1310 help='don\'t create archive file.') 

1311 

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

1313 

1314 s_conf = '' 

1315 if options.config: 

1316 try: 

1317 conf = read_config(options.config) 

1318 except grond.GrondError as e: 

1319 die(str(e)) 

1320 

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

1322 else: 

1323 from grond import plot 

1324 conf = ReportConfig( 

1325 plot_config_collection=plot.get_plot_config_collection()) 

1326 conf.set_basepath('.') 

1327 

1328 if options.write_config: 

1329 try: 

1330 write_config(conf, options.write_config) 

1331 sys.exit(0) 

1332 

1333 except grond.GrondError as e: 

1334 die(str(e)) 

1335 

1336 # commandline options that can override config values 

1337 if options.no_archive: 

1338 conf.make_archive = False 

1339 

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

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

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

1343 args = [] 

1344 

1345 report_base_path = conf.expand_path(conf.report_base_path) 

1346 

1347 if options.index_only: 

1348 report_index(conf) 

1349 report_archive(conf) 

1350 args = [] 

1351 

1352 entries_generated = False 

1353 

1354 payload = [] 

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

1356 rundirs = args 

1357 all_failed = True 

1358 for rundir in rundirs: 

1359 payload.append(( 

1360 [rundir], None, conf, options.update_without_plotting, 

1361 options.nthreads)) 

1362 

1363 elif args: 

1364 try: 

1365 env = Environment(args) 

1366 for event_name in env.get_selected_event_names(): 

1367 payload.append(( 

1368 args, event_name, conf, options.update_without_plotting, 

1369 options.nthreads)) 

1370 

1371 except grond.GrondError as e: 

1372 die(str(e)) 

1373 

1374 if payload: 

1375 entries_generated = [] 

1376 for result in parimap.parimap( 

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

1378 

1379 entries_generated.append(result) 

1380 

1381 all_failed = not any(entries_generated) 

1382 entries_generated = any(entries_generated) 

1383 

1384 if all_failed: 

1385 die('no report entries generated') 

1386 

1387 report_index(conf) 

1388 report_archive(conf) 

1389 

1390 if options.serve or options.serve_external: 

1391 if options.serve_external: 

1392 host = 'default' 

1393 else: 

1394 host = options.host 

1395 

1396 addr = serve_ip(host), options.port 

1397 

1398 serve_report( 

1399 addr, 

1400 report_config=conf, 

1401 fixed_port=options.fixed_port or options.serve_external, 

1402 open=options.open) 

1403 

1404 elif options.open: 

1405 import webbrowser 

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

1407 webbrowser.open(url) 

1408 

1409 else: 

1410 if not entries_generated and not options.index_only: 

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

1412 

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

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

1415 

1416 

1417def command_qc_polarization(args): 

1418 

1419 def setup(parser): 

1420 parser.add_option( 

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

1422 metavar='NUMBER', 

1423 default=0.5, 

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

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

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

1427 parser.add_option( 

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

1429 metavar='NUMBER', 

1430 default=0.5, 

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

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

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

1434 parser.add_option( 

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

1436 metavar='NUMBER', 

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

1438 parser.add_option( 

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

1440 metavar='NUMBER', 

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

1442 parser.add_option( 

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

1444 metavar='NUMBER', 

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

1446 parser.add_option( 

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

1448 metavar='NUMBER', 

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

1450 parser.add_option( 

1451 '--picks', dest='picks_filename', 

1452 metavar='FILENAME', 

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

1454 parser.add_option( 

1455 '--save', dest='output_filename', 

1456 metavar='FILENAME.FORMAT', 

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

1458 parser.add_option( 

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

1460 metavar='NUMBER', 

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

1462 

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

1464 if len(args) != 3: 

1465 help_and_die(parser, 'missing arguments') 

1466 

1467 if options.output_filename: 

1468 import matplotlib 

1469 matplotlib.use('Agg') 

1470 

1471 import grond.qc 

1472 

1473 config_path, event_name, target_group_path = args 

1474 

1475 try: 

1476 config = grond.read_config(config_path) 

1477 except grond.GrondError as e: 

1478 die(str(e)) 

1479 

1480 ds = config.get_dataset(event_name) 

1481 

1482 engine = config.engine_config.get_engine() 

1483 

1484 nsl_to_time = None 

1485 if options.picks_filename: 

1486 markers = marker.load_markers(options.picks_filename) 

1487 marker.associate_phases_to_events(markers) 

1488 

1489 nsl_to_time = {} 

1490 for m in markers: 

1491 if isinstance(m, marker.PhaseMarker): 

1492 ev = m.get_event() 

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

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

1495 

1496 if not nsl_to_time: 

1497 help_and_die( 

1498 parser, 

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

1500 event_name, options.picks_filename)) 

1501 

1502 target_group_paths_avail = [] 

1503 for target_group in config.target_groups: 

1504 name = target_group.path 

1505 if name == target_group_path: 

1506 imc = target_group.misfit_config 

1507 fmin = imc.fmin 

1508 fmax = imc.fmax 

1509 ffactor = imc.ffactor 

1510 

1511 store = engine.get_store(target_group.store_id) 

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

1513 

1514 grond.qc.polarization( 

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

1516 time_factor_pre=options.time_factor_pre, 

1517 time_factor_post=options.time_factor_post, 

1518 distance_min=options.distance_min, 

1519 distance_max=options.distance_max, 

1520 depth_min=options.depth_min, 

1521 depth_max=options.depth_max, 

1522 nsl_to_time=nsl_to_time, 

1523 output_filename=options.output_filename, 

1524 output_dpi=options.output_dpi) 

1525 

1526 return 

1527 

1528 target_group_paths_avail.append(name) 

1529 

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

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

1532 

1533 

1534def command_upgrade_config(args): 

1535 def setup(parser): 

1536 parser.add_option( 

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

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

1539 

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

1541 if len(args) != 1: 

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

1543 

1544 from grond import upgrade 

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

1546 

1547 

1548def command_diff(args): 

1549 def setup(parser): 

1550 pass 

1551 

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

1553 if len(args) != 2: 

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

1555 

1556 from grond.config import diff_configs 

1557 diff_configs(*args) 

1558 

1559 

1560def command_version(args): 

1561 def setup(parser): 

1562 parser.add_option( 

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

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

1565 parser.add_option( 

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

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

1568 

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

1570 

1571 if options.short: 

1572 print(grond.__version__) 

1573 return 

1574 

1575 elif not options.failsafe: 

1576 from grond import info 

1577 print(info.version_info()) 

1578 return 

1579 

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

1581 

1582 try: 

1583 import pyrocko 

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

1585 except ImportError: 

1586 print('pyrocko: N/A') 

1587 

1588 try: 

1589 import numpy 

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

1591 except ImportError: 

1592 print('numpy: N/A') 

1593 

1594 try: 

1595 import scipy 

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

1597 except ImportError: 

1598 print('scipy: N/A') 

1599 

1600 try: 

1601 import matplotlib 

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

1603 except ImportError: 

1604 print('matplotlib: N/A') 

1605 

1606 try: 

1607 from pyrocko.gui.qt_compat import Qt 

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

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

1610 except ImportError: 

1611 print('PyQt: N/A') 

1612 print('Qt: N/A') 

1613 

1614 import sys 

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

1616 

1617 if not options.failsafe: 

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

1619 

1620 

1621if __name__ == '__main__': 

1622 main()