Coverage for /usr/local/lib/python3.11/dist-packages/grond/environment.py: 76%
218 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 time
5import logging
6import shutil
7import os
9from grond.config import read_config, write_config
10from grond import meta, run_info
11from grond.problems.base import load_optimiser_info, load_problem_info, \
12 ModelHistory
14op = os.path
16logger = logging.getLogger('grond.environment')
19class GrondEnvironmentError(meta.GrondError):
20 pass
23class EventSelectionFailed(GrondEnvironmentError):
24 pass
27class NoCurrentEventAvailable(GrondEnvironmentError):
28 def __init__(self, message='no current event available'):
29 GrondEnvironmentError.__init__(self, message)
32class NoEventSelectionAvailable(GrondEnvironmentError):
33 def __init__(self, message='no event selection available'):
34 GrondEnvironmentError.__init__(self, message)
37class NoRundirAvailable(GrondEnvironmentError):
38 def __init__(self, message='no rundir available'):
39 GrondEnvironmentError.__init__(self, message)
42class NoPlotCollectionManagerAvailable(GrondEnvironmentError):
43 def __init__(self, message='no plot collection manager available'):
44 GrondEnvironmentError.__init__(self, message)
47class Environment(object):
49 def __init__(self, args=None, config=None, event_names=None):
51 self._current_event_name = None
52 self._selected_event_names = None
53 self._config = None
54 self._plot_collection_manager = None
55 self._dataset = None
57 if isinstance(args, str):
58 args = [args]
60 if not args and not config:
61 raise GrondEnvironmentError('missing arguments')
63 if config and event_names:
64 self._config_path = None
65 self._rundir_path = None
66 self._config = config
68 if isinstance(event_names, str):
69 event_names = [event_names]
70 self.set_selected_event_names(event_names)
72 elif op.isdir(args[0]):
73 self._rundir_path = args[0]
74 self._config_path = op.join(self._rundir_path, 'config.yaml')
76 else:
77 self._rundir_path = None
78 self._config_path = args[0]
79 self.set_selected_event_names(args[1:])
81 self.reset()
83 @classmethod
84 def discover(cls, rundir):
85 running_fn = op.join(rundir, '.running')
86 while op.exists(running_fn):
87 try:
88 cls.verify_rundir(rundir)
89 return cls([rundir])
90 except GrondEnvironmentError:
91 time.sleep(.25)
92 raise GrondEnvironmentError('could not discover rundir')
94 @staticmethod
95 def verify_rundir(rundir_path):
96 files = [
97 'config.yaml',
98 'problem.yaml',
99 'optimiser.yaml',
100 'misfits'
101 ]
102 for fn in files:
103 if not op.exists(op.join(rundir_path, fn)):
104 raise GrondEnvironmentError('inconsistent rundir')
106 def copy(self, destination, force=False):
107 ''' Copy the environment and return it '''
108 files = [
109 'config.yaml',
110 'problem.yaml',
111 'optimiser.yaml',
112 'misfits',
113 'models',
114 'choices',
115 'chains'
116 ]
118 if op.exists(destination) and not force:
119 raise OSError('Directory %s already exists' % destination)
121 destination = op.abspath(destination)
122 os.makedirs(destination, exist_ok=True)
124 for file in files:
125 src = op.join(self._rundir_path, file)
126 dest = op.join(destination, file)
128 if not op.isfile(src):
129 logger.debug('Cannot find file %s', src)
130 continue
131 logger.debug('Copying %s to %s', src, dest)
133 shutil.copy(src, dest)
135 cls = self.__class__
136 return cls(destination)
138 def reset(self):
139 if self._dataset:
140 self._dataset.close()
141 self._histories = {}
142 self._dataset = None
143 self._optimiser = None
144 self._problem = None
146 def get_config(self):
147 if self._config is None:
148 self._config = read_config(self._config_path)
150 return self._config
152 def write_config(self):
153 write_config(self.get_config(), self.get_config_path())
155 def get_available_event_names(self):
156 return self.get_config().get_event_names()
158 def set_current_event_name(self, event_name):
159 self._current_event_name = event_name
160 self.reset()
162 def get_current_event_name(self):
163 if self._current_event_name is None:
164 try:
165 self.get_rundir_path()
166 self._current_event_name = self.get_problem().base_source.name
167 except NoRundirAvailable:
168 try:
169 event_names = self.get_selected_event_names()
170 if len(event_names) == 1:
171 self._current_event_name = event_names[0]
172 else:
173 raise NoCurrentEventAvailable()
175 except NoEventSelectionAvailable:
176 raise NoCurrentEventAvailable()
178 return self._current_event_name
180 def set_selected_event_names(self, args):
181 event_names = self.get_available_event_names()
182 if len(args) == 0:
183 if len(event_names) == 1:
184 self._selected_event_names = event_names
185 else:
186 if not event_names:
187 raise EventSelectionFailed(
188 'No event file found, check your config!')
189 raise EventSelectionFailed(
190 'Ambiguous event selection. Select from available events:'
191 '\n %s\n or \'all\' to use all available events'
192 % '\n '.join(event_names))
194 elif len(args) == 1 and args[0] == 'all':
195 self._selected_event_names = event_names
197 else:
198 self._selected_event_names = []
199 for event_name in args:
200 if event_name not in event_names:
201 self._selected_event_names = None
202 raise EventSelectionFailed(
203 'No such event: %s' % event_name)
205 self._selected_event_names.append(event_name)
207 @property
208 def nevents_selected(self):
209 return len(self.get_selected_event_names())
211 def get_selected_event_names(self):
212 if self._selected_event_names is None:
213 raise NoEventSelectionAvailable()
215 return self._selected_event_names
217 def get_dataset(self):
218 if self._dataset is None:
219 event_name = self.get_current_event_name()
220 self._dataset = self.get_config().get_dataset(event_name)
222 return self._dataset
224 def set_rundir_path(self, path):
225 self._rundir_path = path
227 def get_rundir_path(self):
228 if self._rundir_path is None:
229 raise NoRundirAvailable()
231 return self._rundir_path
233 def have_rundir(self):
234 return self._rundir_path is not None
236 def get_run_info_path(self):
237 return op.join(self.get_rundir_path(), 'run_info.yaml')
239 def get_run_info(self):
240 run_info_path = self.get_run_info_path()
241 if not op.exists(run_info_path):
242 info = run_info.RunInfo()
243 return info
244 else:
245 return run_info.read_info(run_info_path)
247 def set_run_info(self, info):
248 run_info_path = self.get_run_info_path()
249 run_info.write_info(info, run_info_path)
251 def get_optimiser(self):
252 if self._optimiser is None:
253 try:
254 self._optimiser = load_optimiser_info(self.get_rundir_path())
255 except NoRundirAvailable:
256 self._optimiser = \
257 self.get_config().optimiser_config.get_optimiser()
259 return self._optimiser
261 def get_problem(self):
262 if self._problem is None:
263 try:
264 self._problem = load_problem_info(self.get_rundir_path())
265 except NoRundirAvailable:
266 self._problem = \
267 self.get_config().get_problem(
268 self.get_dataset().get_event())
270 return self._problem
272 def get_history(self, subset=None):
273 if subset not in self._histories:
274 self._histories[subset] = \
275 ModelHistory(
276 self.get_problem(),
277 nchains=self.get_optimiser().nchains,
278 path=meta.xjoin(self.get_rundir_path(), subset))
280 self._histories[subset].ensure_bootstrap_misfits(
281 self.get_optimiser())
283 return self._histories[subset]
285 def set_plot_collection_manager(self, pcm):
286 self._plot_collection_manager = pcm
288 def get_plot_collection_manager(self):
289 if self._plot_collection_manager is None:
290 raise NoPlotCollectionManagerAvailable()
292 return self._plot_collection_manager
294 def setup_modelling(self):
295 '''Must be called before any modelling can be done.'''
296 logger.debug('Setting up modelling...')
297 self.get_config().setup_modelling_environment(self.get_problem())
298 ds = self.get_dataset()
299 for target in self.get_problem().targets:
300 target.set_dataset(ds)
302 def get_plot_classes(self):
303 '''Discover all plot classes relevant for the setup.'''
305 plots = set()
306 try:
307 plots.update(self.get_problem().get_plot_classes())
308 except GrondEnvironmentError:
309 pass
311 try:
312 plots.update(self.get_optimiser().get_plot_classes())
313 except GrondEnvironmentError:
314 pass
316 try:
317 for target in self.get_problem().targets:
318 plots.update(target.get_plot_classes())
319 except GrondEnvironmentError:
320 pass
322 return sorted(list(plots), key=lambda plot: plot.name)
324 def get_plots_path(self):
325 try:
326 return op.join(self.get_rundir_path(), 'plots')
327 except NoRundirAvailable:
328 return 'plots'
330 def get_config_path(self):
331 return self._config_path
333 def is_running(self):
334 return op.exists(self.get_rundir_path, '.running')
337__all__ = [
338 'GrondEnvironmentError',
339 'EventSelectionFailed',
340 'NoCurrentEventAvailable',
341 'NoRundirAvailable',
342 'NoPlotCollectionManagerAvailable',
343 'Environment',
344]