1# http://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6import logging
8from ..common import ldq
9from pyrocko.squirrel.error import ToolError
11logger = logging.getLogger('psq.cli.response')
13headline = 'Print instrument response information.'
16def indent(prefix, nfirst, n, message):
17 return '\n'.join(
18 prefix + ' ' * (nfirst if i == 0 else n) + line
19 for i, line in enumerate(message.splitlines()))
22def make_subparser(subparsers):
23 return subparsers.add_parser(
24 'response',
25 help=headline,
26 description=headline)
29def setup(parser):
30 parser.add_squirrel_selection_arguments()
31 parser.add_squirrel_query_arguments(without=['kinds'])
33 level_choices = ['response', 'stages']
35 parser.add_argument(
36 '--level',
37 choices=level_choices,
38 dest='level',
39 default='response',
40 help='Set level of detail to be printed. Choices: %s'
41 % ldq(level_choices))
43 parser.add_argument(
44 '--problems',
45 action='store_true',
46 dest='problems',
47 default=False,
48 help='Print attached warnings and error messages.')
50 parser.add_argument(
51 '--plot',
52 action='store_true',
53 dest='plot',
54 default=False,
55 help='Create Bode plot showing the responses.')
57 parser.add_argument(
58 '--fmin',
59 dest='fmin',
60 type=float,
61 default=0.01,
62 help='Minimum frequency [Hz], default: 0.01')
64 parser.add_argument(
65 '--fmax',
66 dest='fmax',
67 type=float,
68 default=100.,
69 help='Maximum frequency [Hz], default: 100')
71 parser.add_argument(
72 '--normalize',
73 dest='normalize',
74 action='store_true',
75 help='Normalize response to be 1 on flat part.')
77 parser.add_argument(
78 '--save',
79 dest='out_path',
80 help='Save figure to file.')
82 parser.add_argument(
83 '--dpi',
84 dest='dpi',
85 type=float,
86 default=100.,
87 help='DPI setting for pixel image output, default: 100')
89 parser.add_argument(
90 '--stages',
91 dest='stages',
92 metavar='START:STOP',
93 help='Show response of selected stages. Indexing is Python style: '
94 'count starts at zero, negative values count from the end.')
96 input_quantity_choices = ['displacement', 'velocity', 'acceleration']
98 parser.add_argument(
99 '--input-quantity',
100 dest='input_quantity',
101 choices=input_quantity_choices,
102 metavar='QUANTITY',
103 help='Show converted response for given input quantity. choices: %s'
104 % ldq(input_quantity_choices))
106 parser.add_argument(
107 '--show-breakpoints',
108 dest='show_breakpoints',
109 action='store_true',
110 default=False,
111 help='Show breakpoints of pole-zero responses.')
113 parser.add_argument(
114 '--index-labels',
115 dest='index_labels',
116 action='store_true',
117 default=False,
118 help='Label graphs only by index and print details to terminal '
119 'to save space when many labels would be shown. Aggregate '
120 'identical responses under a common index.')
123def run(parser, args):
124 squirrel = args.make_squirrel()
126 stages = (None, None)
127 if args.stages:
128 words = args.stages.split(':')
129 try:
130 if len(words) == 1:
131 stages = (int(words[0]), int(words[0])+1)
132 elif len(words) == 2:
133 stages = tuple(int(word) if word else None for word in words)
134 else:
135 raise ValueError()
137 except ValueError:
138 raise ToolError('Invalid --stages argument.')
140 data = []
141 for response in squirrel.get_responses(**args.squirrel_query):
142 print(response.summary)
143 if args.problems:
144 for level, message, _ in response.log:
145 print('! %s: %s' % (
146 level.capitalize(),
147 indent('!', 0, 5, message)))
149 if args.level == 'stages':
150 for stage in response.stages[slice(*stages)]:
151 print(' %s' % stage.summary)
152 if args.problems:
153 for level, message, _ in stage.log:
154 print('! %s: %s' % (
155 level.capitalize(),
156 indent('!', 0, 7, message)))
158 data.append((
159 ', '.join([
160 str(response.codes),
161 response.str_time_span_short,
162 response.summary_log,
163 response.summary_quantities]),
164 response.get_effective(
165 input_quantity=args.input_quantity,
166 stages=stages)))
168 if args.plot:
169 if not data:
170 logger.warning('No response objects found.')
171 return
173 labels, resps = [list(x) for x in zip(*data)]
175 from pyrocko.plot.response import plot
176 plot(
177 resps,
178 fmin=args.fmin,
179 fmax=args.fmax,
180 nf=200,
181 normalize=args.normalize,
182 labels=labels,
183 filename=args.out_path,
184 dpi=args.dpi,
185 show_breakpoints=args.show_breakpoints,
186 separate_combined_labels='print' if args.index_labels else None)