Coverage for /usr/local/lib/python3.11/dist-packages/grond/analysers/target_balancing/analyser.py: 90%
83 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-10-26 16:25 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2023-10-26 16:25 +0000
1import copy
2import time
3import logging
4import numpy as num
5from pyrocko.guts import Int, Float, Bool
6from pyrocko import gf
8from grond.meta import Forbidden, GrondError
10from ..base import Analyser, AnalyserConfig, AnalyserResult
12logger = logging.getLogger('grond.analysers.target_balancer')
15guts_prefix = 'grond'
18class TargetBalancingAnalyser(Analyser):
19 """ Estimating target weights that balance the signal amplitudes.
21 Signal amplitudes depend on the source-receiver distance, on the
22 phase type and the taper used. Large signals have in general
23 a higher contribution to the misfit than smaller signals,
24 without carrying more information. With this function, weights are
25 estimated that shall balance the phase contributions.
27 The weight estimation is based on synthetic waveforms that stem from
28 a given number of random forward models. The inverse of the mean
29 synthetic signal amplitudes gives the balancing weight. This is
30 described as adaptive station weighting in Heimann (2011).
31 """
33 def __init__(self, niter, use_reference_magnitude, cutoff):
34 Analyser.__init__(self)
35 self.niter = niter
36 self.use_reference_magnitude = use_reference_magnitude
37 self.cutoff = cutoff
39 def log_progress(self, problem, iiter, niter):
40 t = time.time()
41 if self._tlog_last < t - 10. \
42 or iiter == 0 \
43 or iiter == niter - 1:
45 logger.info(
46 'Target balancing for "%s" at %i/%i.' % (
47 problem.name,
48 iiter, niter))
50 self._tlog_last = t
52 def analyse(self, problem, ds):
53 if self.niter == 0:
54 return
56 wtargets = []
57 if not problem.has_waveforms:
58 return
60 for target in problem.waveform_targets:
61 wtarget = copy.copy(target)
62 wtarget.flip_norm = True
63 wtarget.weight = 1.0
64 wtargets.append(wtarget)
66 wproblem = problem.copy()
67 if hasattr(problem, 'cmt_problem'):
68 if problem.cmt_problem is not None:
69 wproblem = problem.cmt_problem.copy()
71 wproblem.targets = wtargets
73 xbounds = wproblem.get_parameter_bounds()
75 misfits = num.zeros((self.niter, wproblem.ntargets, 2))
76 rstate = self.get_rstate(problem)
78 isbad_mask = None
80 self._tlog_last = 0
81 for iiter in range(self.niter):
82 self.log_progress(problem, iiter, self.niter)
83 while True:
84 if self.use_reference_magnitude:
85 try:
86 fixed_magnitude = wproblem.base_source.get_magnitude()
87 except gf.DerivedMagnitudeError:
88 raise GrondError(
89 'Cannot use use_reference_magnitude for this type '
90 'of source model.')
91 else:
92 fixed_magnitude = None
94 x = wproblem.random_uniform(
95 xbounds, rstate, fixed_magnitude=fixed_magnitude)
97 try:
98 x = wproblem.preconstrain(x)
99 break
101 except Forbidden:
102 pass
104 if isbad_mask is not None and num.any(isbad_mask):
105 isok_mask = num.logical_not(isbad_mask)
106 else:
107 isok_mask = None
108 misfits[iiter, :, :] = wproblem.misfits(x, mask=isok_mask)
110 isbad_mask = num.isnan(misfits[iiter, :, 1])
112 mean_ms = num.mean(misfits[:, :, 0], axis=0)
113 mean_ps = num.mean(misfits[:, :, 1], axis=0)
115 weights = 1. / mean_ps
116 families, nfamilies = wproblem.get_family_mask()
118 for ifamily in range(nfamilies):
119 weights[families == ifamily] /= (
120 num.nansum(weights[families == ifamily]) /
121 num.nansum(num.isfinite(weights[families == ifamily])))
123 if self.cutoff is not None:
124 weights[mean_ms / mean_ps > self.cutoff] = 0.0
126 for weight, target in zip(weights, problem.waveform_targets):
127 target.analyser_results['target_balancing'] = \
128 TargetBalancingAnalyserResult(weight=float(weight))
130 for itarget, target in enumerate(problem.waveform_targets):
131 logger.info((
132 'Balancing analysis for target "%s":\n'
133 ' m/p: %g\n'
134 ' weight: %g\n'
135 ) % (
136 target.string_id(),
137 mean_ms[itarget] / mean_ps[itarget],
138 weights[itarget]))
141class TargetBalancingAnalyserResult(AnalyserResult):
142 weight = Float.T()
145class TargetBalancingAnalyserConfig(AnalyserConfig):
146 """Configuration parameters of the target balancing."""
147 niterations = Int.T(
148 default=1000,
149 help='Number of random forward models for mean phase amplitude '
150 'estimation')
152 use_reference_magnitude = Bool.T(
153 default=False,
154 help='Fix magnitude of random sources to the magnitude of the '
155 'reference event.')
157 cutoff = Float.T(
158 optional=True,
159 help='Remove targets where ratio m/p > cutoff, where m is the misfit '
160 'between synthetics and observations and p is the misfit between '
161 'synthetics and zero-traces. Magnitude should be fixed to use '
162 'this.')
164 def get_analyser(self):
165 return TargetBalancingAnalyser(
166 niter=self.niterations,
167 use_reference_magnitude=self.use_reference_magnitude,
168 cutoff=self.cutoff)
171__all__ = '''
172 TargetBalancingAnalyser
173 TargetBalancingAnalyserConfig
174'''.split()