Coverage for /usr/local/lib/python3.11/dist-packages/pyrocko/gf/builder.py: 75%
120 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-10-06 15:01 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2023-10-06 15:01 +0000
1# http://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6'''
7Common base for Green's function store builders (:py:mod:`~pyrocko.fomosto`).
8'''
10import os
11import signal
12import errno
13from os.path import join as pjoin
14import numpy as num
16from collections import defaultdict
17from pyrocko.parimap import parimap
18from pyrocko import util
19from . import store
22def int_arr(*args):
23 return num.array(args, dtype=int)
26class Interrupted(store.StoreError):
27 def __str__(self):
28 return 'Interrupted.'
31g_builders = {}
34def work_block(args):
35 # previously this was a implemented as a classmethod __work_block but it
36 # caused problems on Conda Python 3.8 on OSX.
37 try:
38 cls, store_dir, step, iblock, shared, force = args
39 if (store_dir, step) not in g_builders:
40 g_builders[store_dir, step] = cls(
41 store_dir, step, shared, force=force)
43 builder = g_builders[store_dir, step]
44 builder.work_block(iblock)
45 except KeyboardInterrupt:
46 raise Interrupted()
47 except IOError as e:
48 if e.errno == errno.EINTR:
49 raise Interrupted()
50 else:
51 raise
53 return store_dir, step, iblock
56def cleanup():
57 for k in list(g_builders):
58 g_builders[k].cleanup()
59 del g_builders[k]
62class Builder(object):
63 nsteps = 1
65 def __init__(self, gf_config, step, block_size=None, force=False):
66 if block_size is None:
67 if len(gf_config.ns) == 3:
68 block_size = (10, 1, 10)
69 elif len(gf_config.ns) == 2:
70 block_size = (1, 10)
71 else:
72 assert False
74 self.step = step
75 self.force = force
76 self.gf_config = gf_config
77 self.warnings = defaultdict(int)
78 self._block_size = int_arr(*block_size)
80 def cleanup(self):
81 pass
83 @property
84 def nblocks(self):
85 return num.prod(self.block_dims)
87 @property
88 def block_dims(self):
89 return (self.gf_config.ns-1) // self._block_size + 1
91 def warn(self, msg):
92 self.warnings[msg] += 1
94 def log_warnings(self, index, logger):
95 for warning, noccur in self.warnings.items():
96 msg = 'block {}: ' + warning
97 logger.warning(msg.format(index, noccur))
99 self.warnings = defaultdict(int)
101 def all_block_indices(self):
102 return num.arange(self.nblocks)
104 def get_block(self, index):
105 dims = self.block_dims
106 iblock = num.unravel_index(index, dims)
107 ibegins = iblock * self._block_size
108 iends = num.minimum(ibegins + self._block_size, self.gf_config.ns)
109 return ibegins, iends
111 def get_block_extents(self, index):
112 ibegins, iends = self.get_block(index)
113 begins = self.gf_config.mins + ibegins * self.gf_config.deltas
114 ends = self.gf_config.mins + (iends-1) * self.gf_config.deltas
115 return begins, ends, iends - ibegins
117 @classmethod
118 def build(cls, store_dir, force=False, nworkers=None, continue_=False,
119 step=None, iblock=None):
120 if step is None:
121 steps = list(range(cls.nsteps))
122 else:
123 steps = [step]
125 if iblock is not None and step is None and cls.nsteps != 1:
126 raise store.StoreError('--step option must be given')
128 done = set()
129 status_fn = pjoin(store_dir, '.status')
131 if not continue_ and iblock in (None, -1) and step in (None, 0):
132 store.Store.create_dependants(store_dir, force)
134 if iblock is None:
135 if not continue_:
136 with open(status_fn, 'w') as status:
137 pass
138 else:
139 if iblock is None:
140 try:
141 with open(status_fn, 'r') as status:
142 for line in status:
143 done.add(tuple(int(x) for x in line.split()))
144 except IOError:
145 raise store.StoreError('nothing to continue')
147 shared = {}
148 for step in steps:
149 builder = cls(store_dir, step, shared, force=force)
150 if not (0 <= step < builder.nsteps):
151 raise store.StoreError('invalid step: %i' % (step+1))
153 if iblock in (None, -1):
154 iblocks = [x for x in builder.all_block_indices()
155 if (step, x) not in done]
156 else:
157 if not (0 <= iblock < builder.nblocks):
158 raise store.StoreError(
159 'invalid block index %i' % (iblock+1))
161 iblocks = [iblock]
163 if iblock == -1:
164 for i in iblocks:
165 c = ['fomosto', 'build']
166 if not os.path.samefile(store_dir, '.'):
167 c.append("'%s'" % store_dir)
169 if builder.nsteps != 1:
170 c.append('--step=%i' % (step+1))
172 c.append('--block=%i' % (i+1))
174 print(' '.join(c))
176 return
178 builder.cleanup()
179 del builder
181 original = signal.signal(signal.SIGINT, signal.SIG_IGN)
182 try:
183 for x in parimap(
184 work_block,
185 [(cls, store_dir, step, i, shared, force)
186 for i in iblocks],
187 nprocs=nworkers,
188 eprintignore=(Interrupted, store.StoreError),
189 startup=util.setup_logging,
190 startup_args=util.subprocess_setup_logging_args(),
191 cleanup=cleanup):
193 store_dir, step, i = x
194 with open(status_fn, 'a') as status:
195 status.write('%i %i\n' % (step, i))
197 finally:
198 signal.signal(signal.SIGINT, original)
200 os.remove(status_fn)
203__all__ = ['Builder']