diff --git a/src/qseek/magnitudes/local_magnitude_model.py b/src/qseek/magnitudes/local_magnitude_model.py index db30b10b..ed368dda 100644 --- a/src/qseek/magnitudes/local_magnitude_model.py +++ b/src/qseek/magnitudes/local_magnitude_model.py @@ -378,4 +378,15 @@ def get_amp_attenuation(dist_hypo_km: float, dist_epi_km: float) -> float: return 0.89 * np.log10(dist_epi_km / 100) + 0.00256 * (dist_epi_km - 100) + 3 +class ArgentinaVolcanoes(WoodAnderson, LocalMagnitudeModel): + author = "Montenegro et al. (2021)" + + epicentral_range = Range(0.0 * KM, 100.0 * KM) # Bounds are not clear + component = "north-east-separate" + + @staticmethod + def get_amp_attenuation(dist_hypo_km: float, dist_epi_km: float) -> float: + return 2.76 * np.log10(dist_epi_km) - 2.48 + + ModelName = Literal[LocalMagnitudeModel.model_names()] diff --git a/src/qseek/octree.py b/src/qseek/octree.py index 7a55c97b..2347bcc0 100644 --- a/src/qseek/octree.py +++ b/src/qseek/octree.py @@ -306,7 +306,7 @@ class Octree(BaseModel): description="The reference location of the octree.", ) root_node_size: PositiveFloat = Field( - default=2 * KM, + default=1 * KM, description="Initial size of the root octree node at level 0 in meters.", ) n_levels: int = Field( diff --git a/src/qseek/pre_processing/downsample.py b/src/qseek/pre_processing/downsample.py index b9454dec..14ced59b 100644 --- a/src/qseek/pre_processing/downsample.py +++ b/src/qseek/pre_processing/downsample.py @@ -16,7 +16,7 @@ class Downsample(BatchPreProcessing): process: Literal["downsample"] = "downsample" sampling_frequency: PositiveFloat = Field( - 100.0, + default=100.0, description="The new sampling frequency in Hz.", ) diff --git a/src/qseek/search.py b/src/qseek/search.py index 4381d23d..1cef0d03 100644 --- a/src/qseek/search.py +++ b/src/qseek/search.py @@ -31,6 +31,7 @@ from qseek.models.detection_uncertainty import DetectionUncertainty from qseek.models.semblance import Semblance, SemblanceCache from qseek.octree import NodeSplitError, Octree +from qseek.pre_processing.frequency_filters import Bandpass from qseek.pre_processing.module import Downsample, PreProcessing from qseek.signals import Signal from qseek.station_weights import StationWeights @@ -215,11 +216,11 @@ class Search(BaseModel): description="Station inventory from StationXML or Pyrocko Station YAML.", ) data_provider: WaveformProviderType = Field( - default=PyrockoSquirrel(), + default_factory=PyrockoSquirrel.model_construct, description="Data provider for waveform data.", ) pre_processing: PreProcessing = Field( - default=PreProcessing(root=[Downsample(sampling_frequency=100.0)]), + default=PreProcessing(root=[Downsample(), Bandpass()]), description="Pre-processing steps for waveform data.", ) @@ -379,7 +380,7 @@ def write_config(self, path: Path | None = None) -> None: logger.debug("writing search config to %s", path) path.write_text(self.model_dump_json(indent=2, exclude_unset=True)) - logger.debug("dumping stations...") + logger.debug("dumping stations") self.stations.export_pyrocko_stations(rundir / "pyrocko_stations.yaml") csv_dir = rundir / "csv" @@ -458,7 +459,7 @@ async def prepare(self) -> None: Returns: None """ - logger.info("preparing search...") + logger.info("preparing search components") self.data_provider.prepare(self.stations) await self.pre_processing.prepare() @@ -489,7 +490,7 @@ async def start(self, force_rundir: bool = False) -> None: await self.prepare() - logger.info("starting search...") + logger.info("starting search") stats = self._stats stats.reset_start_time() diff --git a/src/qseek/waveforms/squirrel.py b/src/qseek/waveforms/squirrel.py index ebda8a7b..73ff248a 100644 --- a/src/qseek/waveforms/squirrel.py +++ b/src/qseek/waveforms/squirrel.py @@ -14,7 +14,6 @@ PositiveInt, PrivateAttr, computed_field, - field_validator, model_validator, ) from pyrocko.squirrel import Squirrel @@ -113,10 +112,14 @@ class PyrockoSquirrel(WaveformProvider): provider: Literal["PyrockoSquirrel"] = "PyrockoSquirrel" - environment: DirectoryPath = Field( - default=Path("."), + environment: DirectoryPath | None = Field( + default=None, description="Path to a Squirrel environment.", ) + persistent: str | None = Field( + default=None, + description="Name of the persistent collection for faster loading.", + ) waveform_dirs: list[Path] = Field( default=[], description="List of directories holding the waveform files.", @@ -151,18 +154,20 @@ class PyrockoSquirrel(WaveformProvider): def _validate_model(self) -> Self: if self.start_time and self.end_time and self.start_time > self.end_time: raise ValueError("start_time must be before end_time") + if not self.waveform_dirs and not self.persistent: + raise ValueError("no waveform directories or persistent selection provided") return self - @field_validator("waveform_dirs") - def check_dirs(cls, dirs: list[Path]) -> list[Path]: # noqa: N805 - if not dirs: - raise ValueError("no waveform directories provided!") - return dirs - def get_squirrel(self) -> Squirrel: if not self._squirrel: - logger.info("initializing squirrel waveform provider") - squirrel = Squirrel(str(self.environment.expanduser())) + logger.info( + "initializing squirrel waveform provider in environment %s", + self.environment, + ) + squirrel = Squirrel( + env=str(self.environment.expanduser()) if self.environment else None, + persistent=self.persistent, + ) paths = [] for path in self.waveform_dirs: if "**" in str(path): @@ -173,7 +178,7 @@ def get_squirrel(self) -> Squirrel: squirrel.add(paths, check=False) if self._stations: for path in self._stations.station_xmls: - logger.info("loading responses from %s", path) + logger.info("loading StationXML responses from %s", path) squirrel.add(str(path), check=False) self._squirrel = squirrel return self._squirrel