Coverage for /usr/local/lib/python3.11/dist-packages/grond/synthetic_tests.py: 36%
80 statements
« prev ^ index » next coverage.py v6.5.0, created at 2025-04-03 09:31 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2025-04-03 09:31 +0000
1# https://pyrocko.org/grond - GPLv3
2#
3# The Grond Developers, 21st Century
4import numpy as num
5import logging
7from pyrocko import trace
8from pyrocko.guts import (Object, Dict, String, Float, Bool, Int)
10logger = logging.getLogger('grond.synthetic_tests')
12guts_prefix = 'grond'
15class SyntheticWaveformNotAvailable(Exception):
16 pass
19class SyntheticTest(Object):
20 inject_solution = Bool.T(default=False)
21 respect_data_availability = Bool.T(default=False)
22 real_noise_scale = Float.T(default=0.0)
23 white_noise_scale = Float.T(default=0.0)
24 relative_white_noise_scale = Float.T(default=0.0)
25 random_response_scale = Float.T(default=0.0)
26 real_noise_toffset = Float.T(default=-3600.)
27 random_seed = Int.T(optional=True)
28 x = Dict.T(String.T(), Float.T())
30 def __init__(self, **kwargs):
31 Object.__init__(self, **kwargs)
32 self._problem = None
33 self._synthetics = None
35 def set_problem(self, problem):
36 self._problem = problem
37 self._synthetics = None
39 def get_problem(self):
40 if self._problem is None:
41 raise SyntheticWaveformNotAvailable(
42 'SyntheticTest.set_problem() has not been called yet')
44 return self._problem
46 def get_x(self):
47 problem = self.get_problem()
48 if self.x:
49 x = problem.preconstrain(
50 problem.get_parameter_array(self.x))
52 else:
53 x = problem.preconstrain(
54 problem.pack(
55 problem.base_source))
57 return x
59 def get_synthetics(self):
60 problem = self.get_problem()
61 if self._synthetics is None:
62 x = self.get_x()
63 results = problem.forward(x)
64 synthetics = {}
65 for iresult, result in enumerate(results):
66 tr = result.trace.pyrocko_trace()
67 tfade = tr.tmax - tr.tmin
68 tr_orig = tr.copy()
69 tr.extend(tr.tmin - tfade, tr.tmax + tfade)
70 rstate = num.random.RandomState(
71 (self.random_seed or 0) + iresult)
73 if self.random_response_scale != 0:
74 tf = RandomResponse(scale=self.random_response_scale)
75 tf.set_random_state(rstate)
76 tr = tr.transfer(
77 tfade=tfade,
78 transfer_function=tf)
80 if self.white_noise_scale != 0.0:
81 u = rstate.normal(
82 scale=self.white_noise_scale,
83 size=tr.data_len())
85 tr.ydata += u
87 if self.relative_white_noise_scale != 0.0:
88 u = rstate.normal(
89 scale=self.relative_white_noise_scale * num.std(
90 tr_orig.ydata),
91 size=tr.data_len())
93 tr.ydata += u
95 synthetics[result.trace.codes] = tr
97 self._synthetics = synthetics
99 return self._synthetics
101 def get_waveform(self, nslc, tmin, tmax, tfade=0., freqlimits=None):
102 synthetics = self.get_synthetics()
103 if nslc not in synthetics:
104 s = 'no synthetics for %s available' % '.'.join(nslc)
105 logger.warning(s)
106 from grond.dataset import NotFound
107 raise NotFound(s)
109 tr = synthetics[nslc]
110 tr.extend(tmin - tfade * 2.0, tmax + tfade * 2.0)
112 tr = tr.transfer(
113 tfade=tfade,
114 freqlimits=freqlimits)
116 tr.chop(tmin, tmax)
117 return tr
120class RandomResponse(trace.FrequencyResponse):
122 scale = Float.T(default=0.0)
124 def set_random_state(self, rstate):
125 self._rstate = rstate
127 def evaluate(self, freqs):
128 n = freqs.size
129 return 1.0 + (
130 self._rstate.normal(scale=self.scale, size=n) +
131 0.0J * self._rstate.normal(scale=self.scale, size=n))
134__all__ = '''
135SyntheticTest
136SyntheticWaveformNotAvailable
137'''.split()