Browse Source

si inverter: bugfixes and docs

pull/6/head
mmetz 5 days ago
parent
commit
80a80c6881
  1. 21
      src/si/config.py
  2. 167
      src/si/invert.py

21
src/si/config.py

@ -3,6 +3,7 @@
# The Developers, 21st Century
import logging
import numpy as num
import os.path as op
from pyrocko.guts import Int
@ -11,7 +12,7 @@ from grond import Path, read_config, Config
from ewrica import util
from ewrica.module import ModuleConfig
from .invert import SourceInjectionConfig, GrondInverter
from .invert import SourceInjectionConfig, GrondInverter, NEAMTHM18Limit
logger = logging.getLogger('ewrica.si.config')
@ -99,7 +100,23 @@ class SourceInversionConfig(ModuleConfig):
help='Configurations for point source inversion')
dynamic_rupture_config = InversionConfig.T(
default=InversionConfig(grond_config_fn=ds_config_fn),
default=InversionConfig(
grond_config_fn=ds_config_fn,
injection_config=SourceInjectionConfig(
asperity=['total', 'left', 'center', 'right'],
nucleation_x=num.array([-.66, 0., .66]),
nucleation_y=num.array([0., 0., 0.]),
tractions=num.array([0.5, 1., 2.5]) * 1e6,
neamthm18_limit=NEAMTHM18Limit(
quantity='nmodels_max',
target_value=7),
fault_database='EDSF',
length_factor=1.,
width_factor=1.,
include_dimension_uncertainty=False,
f_max=None,
v_min=1200.,
correct_event_duration=False)),
help='Configurations for dynamic rupture inversion')
# @property

167
src/si/invert.py

@ -32,7 +32,8 @@ d2r = num.pi / 180.
class NEAMTHM18Limit(Object):
'''Sets limiting factor and value for model extraction from NEAMTHM18 db
'''
Limiting factor and value for model extraction from NEAMTHM18 database.
'''
quantity = StringChoice.T(
choices=(
@ -123,7 +124,6 @@ class SourceInjectionConfig(Object):
optional=True)
f_max = Float.T(
default=1.,
help='Maximum wave frequency of interest to be used for source model '
'patch size scaling.',
optional=True)
@ -142,10 +142,13 @@ class SourceInjectionConfig(Object):
@property
def nucleation(self):
'''Nucleation points as 2D array
'''
Nucleation points as 2D array.
:returns: nucleation points to be injected
:rtype: :py:class:`numpy.ndarray`, shape: ``(N, 2)``
:returns:
Nucleation points to be injected.
:rtype:
:py:class:`numpy.ndarray`, shape: ``(N, 2)``
'''
if self.nucleation_x.shape != self.nucleation_y.shape:
raise IndexError(
@ -159,20 +162,30 @@ class SourceInjectionConfig(Object):
class SourceInjector(Object):
'''Extract sources to be injected in Grond
'''
Extract sources to be injected in Grond.
'''
config = SourceInjectionConfig.T(
default=SourceInjectionConfig())
def _extract_from_tsumaps(self, lat, lon):
'''
Get faults from tsumaps (NEAMTHM18) database for given location.
'''
limit = self.config.neamthm18_limit
kwargs = {} if limit is None else {
limit.quantity: limit.get_target_value()}
fms, probs = tsumaps.get_probs(lat, lon, **kwargs)
logger.info('Extracted %d models from NEAMTHM18' % len(fms))
return list(fms[:, 0]), list(fms[:, 1]), list(fms[:, 2])
def _extract_from_faults(self, lat, lon, magnitude=None, mt=None):
'''
Get faults from chosen fault database for given location.
'''
conf = self.config
if conf.fault_radius_max is not None:
radius = conf.fault_radius_max
@ -236,6 +249,9 @@ class SourceInjector(Object):
lengths,
widths,
nparallel):
'''
Generate dynamic sources based on prior data and injection config.
'''
from pyrocko import parimap
@ -393,6 +409,9 @@ class SourceInjector(Object):
for src in sources for nx, ny in conf.nucleation]
def _generate_point_sources(self, event, strikes, dips, rakes):
'''
Generate point sources based on prior data and injection config.
'''
from pyrocko import moment_tensor as pmt
base_source = gf.MTSource.from_pyrocko_event(event)
@ -419,21 +438,41 @@ class SourceInjector(Object):
return sources
def get_sources(self, event, store, source_type='dynamic', nparallel=1):
'''Extract source models to be tested for the event location
:param event: Event to extract likely source models for. Both location,
magnitude and moment tensor information (if available) are used.
:type event: :py:class:`pyrocko.model.event.Event`
:param store: green's function store needed for source generation
:type store: :py:class:`pyrocko.gf.store.Store`
:param nparallel: Number of processes which run in parallel.
:type nparallel: int
:returns: List of source models to be injected.
:rtype: list of
:py:class:`~pyrocko.gf.seismosizer.PseudoDynamicRupture`
'''
Extract source models to be tested for the event location.
:param event:
Event to extract likely source models for. Both location, magnitude
and moment tensor information (if available) are used.
:type event:
:py:class:`pyrocko.model.event.Event`
:param store:
Green's function store needed for source generation.
:type store:
:py:class:`pyrocko.gf.store.Store`
:param source_type:
If ``dynamic``, pseudo dynamic rupture models are extracted. If
``point``, moment tensor point sources are returned instead.
:type source_type:
optional, str
:param nparallel:
Number of processes which run in parallel.
:type nparallel:
optional, int
:returns:
Source models to be injected.
:rtype:
list of
:py:class:`~pyrocko.gf.seismosizer.PseudoDynamicRupture` or
:py:class:`~pyrocko.gf.seismosizer.MTSource`
'''
logger.info('Extract sources based on injection configuration ...')
conf = self.config
lat, lon = event.lat, event.lon
@ -469,6 +508,9 @@ class SourceInjector(Object):
rakes %= 360.
rakes[rakes > 180.] -= 360.
logger.info('Generate %s sources for %d inputs ...' %
(source_type, len(strikes)))
if source_type == 'dynamic':
return self._generate_dynamic_sources(
event,
@ -493,7 +535,9 @@ def expand(base_path, path):
class Inverter(Object):
'''
Inverter base class.
'''
def run(self):
pass
@ -540,10 +584,13 @@ class GrondInverter(Inverter):
@property
def store(self):
'''Pyrocko Green's Function store
'''
Pyrocko Green's Function store.
:returns: green's function store
:rtype: :py:class:`pyrocko.gf.store.Store`
:returns:
Green's function store.
:rtype:
:py:class:`pyrocko.gf.store.Store`
'''
if self._store is not None:
return self._store
@ -560,10 +607,13 @@ class GrondInverter(Inverter):
@property
def event(self):
'''Pyrocko event
'''
Pyrocko event.
:returns: event
:rtype: :py:class:`pyrocko.model.event.Event`
:returns:
Event.
:rtype:
:py:class:`pyrocko.model.event.Event`
'''
if self._event is not None:
return self._event
@ -582,10 +632,10 @@ class GrondInverter(Inverter):
if isinstance(p, list):
for path in p:
if not op.exists(path):
logger.warn('Path %s does not exist.' % path)
logger.warning('Path %s does not exist.' % path)
else:
if not op.exists(p):
logger.warn('Path %s does not exist.' % p)
logger.warning('Path %s does not exist.' % p)
return p
@ -620,10 +670,13 @@ class GrondInverter(Inverter):
@property
def reportconf_path(self):
'''Path to the Grond report configuration file
'''
Path to the Grond report configuration file.
:returns: path to Grond report configuration file
:rtype: str
:returns:
Path to Grond report configuration file.
:rtype:
str
'''
if self.report_path is None:
raise ValueError('report_path needed')
@ -634,10 +687,13 @@ class GrondInverter(Inverter):
@property
def sources_inject(self):
'''Injected models from :py:class:`ewrica.si.invert.SourceInjector`
'''
Injected models from :py:class:`ewrica.si.invert.SourceInjector`.
:returns: sources to be injected within the run
:rtype: list of :py:class:`pyrocko.gf.seismosizer.Source`
:returns:
Sources to be injected within the run.
:rtype:
list of :py:class:`pyrocko.gf.seismosizer.Source`
'''
from grond.problems.cmt.problem import CMTProblemConfig
from grond.problems.dynamic_rupture.problem import \
@ -671,10 +727,13 @@ class GrondInverter(Inverter):
@property
def fn_sources_inject(self):
'''Path to the Grond source injection file
'''
Path to the Grond source injection file.
:returns: path to Grond source injection file
:rtype: str
:returns:
Path to Grond source injection file.
:rtype:
str
'''
if self.report_path is None:
raise ValueError('report_path needed')
@ -685,14 +744,16 @@ class GrondInverter(Inverter):
'sources_inject.yaml'])
def sources_to_injector_config(self):
'''Update Grond InjectionConfig for injected sources
'''
Update Grond InjectionConfig for injected sources.
'''
if self.grond_config is not None:
conf = self.grond_config.optimiser_config
sp = conf.sampler_phases
conf.sampler_phases = [InjectionSamplerPhase(
niterations=len(self.sources_inject),
sources_path=self.fn_sources_inject)]
sources_path=self.fn_sources_inject)] + sp
self.grond_config.optimiser_config = conf
@ -702,7 +763,8 @@ class GrondInverter(Inverter):
logger.warning('Could not update config - No config given')
def save_sources_inject(self):
'''Save inject sources in file
'''
Save inject sources in file.
'''
if self.fn_sources_inject is not None:
dump_all(self.sources_inject, filename=self.fn_sources_inject)
@ -712,7 +774,8 @@ class GrondInverter(Inverter):
logger.warning('Could not save sources - No filename given')
def generate_reportconf(self):
'''Generate and save Grond report config file
'''
Generate and save Grond report config file.
'''
if self.report_path is not None:
report_conf = report.ReportConfig(
@ -725,10 +788,13 @@ class GrondInverter(Inverter):
'Dumped specific grond report config %s', self.reportconf_path)
def run(self, force=False):
'''Run Grond optimization
'''
Run Grond optimization.
:param force: If True, possible existing older runs are overwritten
:type force: bool
:param force:
If ``True``, possible existing older runs are overwritten
:type force:
bool
'''
if self.grond_config is None:
raise ValueError('Config file needed to run Grond Inversion')
@ -755,7 +821,8 @@ class GrondInverter(Inverter):
os.chdir(self._cwd)
def report(self):
'''Generate Grond report for run for easier result validation
'''
Generate Grond report for run for easier result validation.
'''
os.chdir(self.base_path)
@ -781,16 +848,18 @@ class GrondInverter(Inverter):
os.chdir(self._cwd)
def run_full(self, create_report=True, *args, **kwargs):
'''Run full optimization loop
'''
Run full optimization loop.
Different steps as source injection, report config generation, the
Grond run and reporting (if wished) are performed.
Arguments and keyword arguments for
:py:meth:`ewrica.si.invert.GrondInverter.run` can be given.
:param create_report: If True, a Grond report is created after
completing the run
:type create_report: optional, bool
:param create_report:
If ``True``, a Grond report is created after completing the run.
:type create_report:
optional, bool
'''
self.sources_to_injector_config()
self.save_sources_inject()

Loading…
Cancel
Save