Coverage for /usr/local/lib/python3.11/dist-packages/grond/targets/base.py: 82%

150 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 copy 

5import logging 

6 

7import numpy as num 

8 

9from pyrocko import gf 

10from pyrocko.guts_array import Array 

11from pyrocko.guts import Object, Float, String, Dict, List, Choice, load, dump 

12 

13from grond.analysers.base import AnalyserResult 

14from grond.meta import has_get_plot_classes, GrondError 

15 

16logger = logging.getLogger('targets.base') 

17 

18guts_prefix = 'grond' 

19 

20 

21class TargetGroup(Object): 

22 normalisation_family = gf.StringID.T( 

23 optional=True, 

24 help='Group with common misfit normalisation') 

25 path = gf.StringID.T( 

26 optional=True, 

27 help='Targets.id will be prefixed with this path') 

28 weight = Float.T( 

29 default=1.0, 

30 help='Additional manual weight of the target group') 

31 interpolation = gf.InterpolationMethod.T( 

32 default='nearest_neighbor', 

33 help='Interpolation from pre-calculated GF store.') 

34 store_id = gf.StringID.T( 

35 optional=True, 

36 help='ID of the Green\'s function store for this TargetGroup.') 

37 

38 def get_targets(self, ds, event, default_path='none'): 

39 if not self._targets: 

40 raise NotImplementedError() 

41 

42 def is_gf_store_appropriate(self, store, depth_range): 

43 return True 

44 

45 def _is_gf_store_appropriate_check_extent(self, store, depth_range): 

46 gf_mindist = store.config.distance_min 

47 gf_maxdist = store.config.distance_max 

48 gf_mindepth = store.config.source_depth_min 

49 gf_maxdepth = store.config.source_depth_max 

50 

51 ok = True 

52 

53 if self.distance_min is not None and self.distance_min < gf_mindist: 

54 logger.error( 

55 'CONFIG: Target min. distance (%s) ' % self.distance_min 

56 + '< Greens function min. distance (%s)!' % gf_mindist) 

57 ok = False 

58 

59 if self.distance_max is not None and self.distance_max > gf_maxdist: 

60 logger.error( 

61 'CONFIG: Target max. distance (%s) ' % self.distance_max 

62 + '> Greens function max. distance (%s)!' % gf_maxdist) 

63 ok = False 

64 

65 if depth_range.start < gf_mindepth or gf_maxdepth < depth_range.stop: 

66 logger.error( 

67 'CONFIG: Depth range of problem not covered by GF store! ' 

68 + 'GF store: %s %s - %s ' 

69 % (self.store_id, gf_mindepth, gf_maxdepth) 

70 + 'Problem depth range: %s - %s' 

71 % (depth_range.start, depth_range.stop)) 

72 ok = False 

73 

74 return ok 

75 

76 def _is_gf_store_appropriate_check_sample_rate(self, store, depth_range): 

77 gf_sampling = store.config.sample_rate 

78 

79 if self.misfit_config.fmax > 0.5*gf_sampling: 

80 logger.error( 

81 'CONFIG: Target max. frequency (%s) ' % self.misfit_config.fmax 

82 + ' > Nyquist frequency of GF store %s (sampling %s)!' 

83 % (self.store_id, gf_sampling)) 

84 

85 return False 

86 

87 return True 

88 

89 

90class MisfitResult(Object): 

91 misfits = Array.T( 

92 shape=(None, 2), 

93 dtype=num.float64) 

94 

95 

96class MisfitConfig(Object): 

97 pass 

98 

99 

100@has_get_plot_classes 

101class MisfitTarget(Object): 

102 

103 manual_weight = Float.T( 

104 default=1.0, 

105 help='Relative weight of this target') 

106 analyser_results = Dict.T( 

107 gf.StringID.T(), 

108 AnalyserResult.T(), 

109 help='Dictionary of analyser results') 

110 normalisation_family = gf.StringID.T( 

111 optional=True, 

112 help='Normalisation family of this misfit target') 

113 path = gf.StringID.T( 

114 help='A path identifier used for plotting') 

115 misfit_config = MisfitConfig.T( 

116 default=MisfitConfig.D(), 

117 help='Misfit configuration') 

118 bootstrap_weights = Array.T( 

119 dtype=num.float64, 

120 serialize_as='base64', 

121 optional=True) 

122 bootstrap_residuals = Array.T( 

123 dtype=num.float64, 

124 serialize_as='base64', 

125 optional=True) 

126 

127 can_bootstrap_weights = False 

128 can_bootstrap_residuals = False 

129 

130 plot_misfits_cumulative = True 

131 

132 def __init__(self, **kwargs): 

133 Object.__init__(self, **kwargs) 

134 self.parameters = [] 

135 

136 self._ds = None 

137 self._result_mode = 'sparse' 

138 

139 self._combined_weight = None 

140 self._target_parameters = None 

141 self._target_ranges = None 

142 

143 self._combined_weight = None 

144 

145 @classmethod 

146 def get_plot_classes(cls): 

147 return [] 

148 

149 def set_dataset(self, ds): 

150 self._ds = ds 

151 

152 def get_dataset(self): 

153 return self._ds 

154 

155 def string_id(self): 

156 return str(self.path) 

157 

158 def misfits_string_ids(self): 

159 raise NotImplementedError('%s does not implement misfits_string_id' 

160 % self.__class__.__name__) 

161 

162 @property 

163 def nmisfits(self): 

164 return 1 

165 

166 def noise_weight_matrix(self): 

167 return num.array([[1]]) 

168 

169 @property 

170 def nparameters(self): 

171 if self._target_parameters is None: 

172 return 0 

173 return len(self._target_parameters) 

174 

175 @property 

176 def target_parameters(self): 

177 if self._target_parameters is None: 

178 self._target_parameters = copy.deepcopy(self.parameters) 

179 for p in self._target_parameters: 

180 p.set_groups([self.string_id()]) 

181 return self._target_parameters 

182 

183 @property 

184 def target_ranges(self): 

185 return {} 

186 

187 def set_parameter_values(self, model): 

188 for i, p in enumerate(self.parameters): 

189 self.parameter_values[p.name_nogroups] = model[i] 

190 

191 def set_result_mode(self, result_mode): 

192 self._result_mode = result_mode 

193 

194 def post_process(self, engine, source, statics): 

195 raise NotImplementedError() 

196 

197 def get_combined_weight(self): 

198 if self._combined_weight is None: 

199 w = num.full(self.nmisfits, self.manual_weight, dtype=float) 

200 for analyser in self.analyser_results.values(): 

201 w *= analyser.weight 

202 self._combined_weight = w 

203 

204 return self._combined_weight 

205 

206 def get_correlated_weights(self): 

207 pass 

208 

209 def set_bootstrap_weights(self, weights): 

210 self.bootstrap_weights = weights 

211 

212 def get_bootstrap_weights(self): 

213 if self.bootstrap_weights is None: 

214 raise Exception('Bootstrap weights have not been set!') 

215 nbootstraps = self.bootstrap_weights.size // self.nmisfits 

216 return self.bootstrap_weights.reshape(nbootstraps, self.nmisfits) 

217 

218 def init_bootstrap_residuals(self, nbootstrap, rstate=None): 

219 raise NotImplementedError() 

220 

221 def set_bootstrap_residuals(self, residuals): 

222 self.bootstrap_residuals = residuals 

223 

224 def get_bootstrap_residuals(self): 

225 if self.bootstrap_residuals is None: 

226 raise Exception('Bootstrap residuals have not been set!') 

227 nbootstraps = self.bootstrap_residuals.size // self.nmisfits 

228 return self.bootstrap_residuals.reshape(nbootstraps, self.nmisfits) 

229 

230 def prepare_modelling(self, engine, source, targets): 

231 ''' Prepare modelling target 

232 

233 This function shall return a list of :class:`pyrocko.gf.Target` 

234 for forward modelling in the :class:`pyrocko.gf.LocalEngine`. 

235 ''' 

236 return [self] 

237 

238 def finalize_modelling( 

239 self, engine, source, modelling_targets, modelling_results): 

240 ''' Manipulate modelling before misfit calculation 

241 

242 This function can be overloaded interact with the modelling results. 

243 ''' 

244 return modelling_results[0] 

245 

246 

247class MisfitResultError(Object): 

248 message = String.T() 

249 

250 

251class MisfitResultCollection(Object): 

252 results = List.T(List.T( 

253 Choice.T([MisfitResult.T(), MisfitResultError.T()]))) 

254 

255 

256def dump_misfit_result_collection(misfit_result_collection, path): 

257 dump(misfit_result_collection, filename=path) 

258 

259 

260def load_misfit_result_collection(path): 

261 try: 

262 obj = load(filename=path) 

263 

264 except OSError as e: 

265 raise GrondError( 

266 'Failed to read ensemble misfit results from file "%s" (%s)' % ( 

267 path, e)) 

268 

269 if not isinstance(obj, MisfitResultCollection): 

270 raise GrondError( 

271 'File "%s" does not contain any misfit result collection.' % path) 

272 

273 return obj 

274 

275 

276__all__ = ''' 

277 TargetGroup 

278 MisfitTarget 

279 MisfitResult 

280 MisfitResultError 

281 dump_misfit_result_collection 

282 load_misfit_result_collection 

283 MisfitResultCollection 

284'''.split()