Skip to content

Commit

Permalink
docs
Browse files Browse the repository at this point in the history
  • Loading branch information
miili committed Oct 30, 2023
1 parent d384ca9 commit a083c2a
Show file tree
Hide file tree
Showing 17 changed files with 314 additions and 80 deletions.
7 changes: 7 additions & 0 deletions docs/components/image_function.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,10 @@ For image functions this version of Lassie relies heavily on machine learning pi

!!! abstract "Citation PhaseNet"
*Zhu, Weiqiang, and Gregory C. Beroza. "PhaseNet: A Deep-Neural-Network-Based Seismic Arrival Time Picking Method." arXiv preprint arXiv:1803.03211 (2018).*

```python exec='on'
from lassie.utils import generate_docs
from lassie.images.phase_net import PhaseNet

print(generate_docs(PhaseNet()))
```
7 changes: 7 additions & 0 deletions docs/components/octree.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# Octree

A 3D space is searched for sources of seismic energy. Lassie created an octree structure which is iteratively refined when energy is detected, to focus on the source' location. This speeds up the search and improves the resolution of the localisations.

```python exec='on'
from lassie.utils import generate_docs
from lassie.octree import Octree

print(generate_docs(Octree()))
```
25 changes: 23 additions & 2 deletions docs/components/ray_tracer.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,42 @@ $$

This module is used for simple use cases and cross-referencing testing.

```python exec='on'
from lassie.utils import generate_docs
from lassie.tracers.constant_velocity import ConstantVelocityTracer

print(generate_docs(ConstantVelocityTracer()))
```

## 1D Layered Model

Calculation of travel times in 1D layered media is based on the [Pyrocko Cake](https://pyrocko.org/docs/current/apps/cake/manual.html#command-line-examples) ray tracer.

![Pyrocko Cake Ray Tracer](https://pyrocko.org/docs/current/_images/cake_plot_example_2.png)
*Pyrocko Cake 1D ray tracer for travel time calculation in 1D layered media*

## 3D Velocity Model
```python exec='on'
from lassie.utils import generate_docs
from lassie.tracers.cake import CakeTracer

print(generate_docs(CakeTracer(), exclude={'earthmodel': {'raw_file_data'}}))
```

We implement the fast marching method for calculating first arrivals of waves in 3D volumes.
## 3D Fast Marching

We implement the fast marching method for calculating first arrivals of waves in 3D volumes. Currently three different 3D velocity models are supported:

* [x] Import [NonLinLoc](http://alomax.free.fr/nlloc/) 3D velocity model
* [x] 1D layered model 🥞
* [x] Constant velocity, mainly for testing purposes 🥼

```python exec='on'
from lassie.utils import generate_docs
from lassie.tracers.fast_marching import FastMarchingTracer

print(generate_docs(FastMarchingTracer()))
```

### Visualizing 3D Models

For quality check, all 3D velocity models are exported to `vtk/` folder as `.vti` files. Use [ParaView](https://www.paraview.org/) to inspect and explore the velocity models.
Expand Down
16 changes: 15 additions & 1 deletion docs/components/seismic_data.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@

The seismic can be delivered in MiniSeed or any other format compatible with Pyrocko.

Organize your data in an [SDS structure](https://www.seiscomp.de/doc/base/concepts/waveformarchives.html) or just a blob if MiniSeed.
Organize your data in an [SDS structure](https://www.seiscomp.de/doc/base/concepts/waveformarchives.html) or just a single MiniSeed file.

```python exec='on'
from lassie.utils import generate_docs
from lassie.waveforms import PyrockoSquirrel

print(generate_docs(PyrockoSquirrel()))
```

## Meta Data

Expand All @@ -16,3 +23,10 @@ Supported data formats are:
* [x] [Pyrocko Station YAML](https://pyrocko.org/docs/current/formats/yaml.html)

Metadata does not need to include response information for pure detection and localisation. If local magnitudes $M_L$ are extracted response information is required.

```python exec='on'
from lassie.utils import generate_docs
from lassie.models.station import Stations

print(generate_docs(Stations()))
```
9 changes: 9 additions & 0 deletions docs/components/station_corrections.md
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
# Station Corrections

Station corrections can be extract from previous runs to refine the localisation accuracy. The corrections can also help to improve the semblance find more events in a dataset.

```python exec='on'
from lassie.utils import generate_docs
from lassie.models.station import Stations

print(generate_docs(Stations()))
```
10 changes: 8 additions & 2 deletions docs/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ The installation is straight-forward:
pip install git+https://github.com/pyrocko/lassie-v2
```

## Running Lassie

The main entry point in the executeable is the `lassie` command. The provided command line interface (CLI) and a JSON config file is all what is needed to run the program.

```bash exec='on' result='ansi' source='above'
lassie -h
```

## Initializing a New Project

Once installed you can run the lassie executeable to initialize a new project.
Expand Down Expand Up @@ -96,8 +104,6 @@ Check out the `search.json` config file and add your waveform data and velocity
"window_length": "PT300S",
"n_threads_parstack": 0,
"n_threads_argmax": 4,
"plot_octree_surface": false,
"created": "2023-10-29T19:17:17.676279Z"
}
```

Expand Down
17 changes: 12 additions & 5 deletions lassie/apps/lassie.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,14 @@
from pkg_resources import get_distribution

from lassie.console import console
from lassie.models import Stations
from lassie.search import Search
from lassie.server import WebServer
from lassie.station_corrections import StationCorrections
from lassie.utils import CACHE_DIR, setup_rich_logging

nest_asyncio.apply()

logger = logging.getLogger(__name__)


def main() -> None:
def get_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
prog="lassie",
description="Lassie - The friendly earthquake detector 🐕",
Expand Down Expand Up @@ -122,7 +118,18 @@ def main() -> None:
)
dump_schemas.add_argument("folder", type=Path, help="folder to dump schemas to")

return parser


def main() -> None:
parser = get_parser()
args = parser.parse_args()

from lassie.models import Stations
from lassie.search import Search
from lassie.server import WebServer
from lassie.station_corrections import StationCorrections

setup_rich_logging(level=logging.INFO - args.verbose * 10)

if args.command == "init":
Expand Down
68 changes: 53 additions & 15 deletions lassie/images/phase_net.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,22 +82,60 @@ def search_phase_arrival(


class PhaseNet(ImageFunction):
"""PhaseNet image function. For more details see SeisBench documentation."""

image: Literal["PhaseNet"] = "PhaseNet"
model: ModelName = "ethz"
window_overlap_samples: int = Field(default=2000, ge=1000, le=3000)
torch_use_cuda: bool = False
torch_cpu_threads: PositiveInt = 4
batch_size: int = Field(default=64, ge=64)
stack_method: StackMethod = "avg"
upscale_input: PositiveInt = 1
phase_map: dict[PhaseName, str] = {
"P": "constant:P",
"S": "constant:S",
}
weights: dict[PhaseName, PositiveFloat] = {
"P": 1.0,
"S": 1.0,
}
model: ModelName = Field(
default="ethz",
description="SeisBench pre-trained PhaseNet model to use. "
"Choose from `ethz`, `geofon`, `instance`, `iquique`, `lendb`, `neic`, `obs`,"
" `original`, `scedc`, `stead`."
" For more details see SeisBench documentation",
)
window_overlap_samples: int = Field(
default=2000,
ge=1000,
le=3000,
description="Window overlap in samples.",
)
torch_use_cuda: bool = Field(
default=False,
description="Use CUDA for inference.",
)
torch_cpu_threads: PositiveInt = Field(
default=4,
description="Number of CPU threads to use if only CPU is used.",
)
batch_size: int = Field(
default=64,
ge=64,
description="Batch size for inference, larger values can improve performance.",
)
stack_method: StackMethod = Field(
default="avg",
description="Method to stack the overlaping blocks internally. "
"Choose from `avg` and `max`.",
)
upscale_input: PositiveInt = Field(
default=1,
description="Upscale input by factor. "
"This augments the input data from e.g. 100 Hz to 50 Hz (factor: `2`). Can be"
" useful for high-frequency earthquake signals.",
)
phase_map: dict[PhaseName, str] = Field(
default={
"P": "constant:P",
"S": "constant:S",
},
description="Phase mapping from SeisBench PhaseNet to Lassie phases.",
)
weights: dict[PhaseName, PositiveFloat] = Field(
default={
"P": 1.0,
"S": 1.0,
},
description="Weights for each phase.",
)

_phase_net: PhaseNetSeisBench = PrivateAttr(None)

Expand Down
17 changes: 13 additions & 4 deletions lassie/models/station.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,20 @@ def __hash__(self) -> int:


class Stations(BaseModel):
station_xmls: list[Path] = []
pyrocko_station_yamls: list[Path] = []

pyrocko_station_yamls: list[Path] = Field(
default=[],
description="List of Pyrocko station yaml files.",
)
station_xmls: list[Path] = Field(
default=[],
description="List of StationXML files.",
)

blacklist: set[constr(pattern=NSL_RE)] = Field(
default=set(),
description="Blacklist as `['NET.STA.LOC', ...]`",
)
stations: list[Station] = []
blacklist: set[constr(pattern=NSL_RE)] = set()

def model_post_init(self, __context: Any) -> None:
loaded_stations = []
Expand Down
36 changes: 29 additions & 7 deletions lassie/octree.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,35 @@ def __hash__(self) -> int:


class Octree(BaseModel):
location: Location = Location(lat=0.0, lon=0.0)
size_initial: PositiveFloat = 2 * KM
size_limit: PositiveFloat = 500
east_bounds: tuple[float, float] = (-10 * KM, 10 * KM)
north_bounds: tuple[float, float] = (-10 * KM, 10 * KM)
depth_bounds: tuple[float, float] = (0 * KM, 20 * KM)
absorbing_boundary: float = Field(default=1 * KM, ge=0.0)
location: Location = Field(
default=Location(lat=0.0, lon=0.0),
description="The reference location of the octree.",
)
size_initial: PositiveFloat = Field(
default=2 * KM,
description="Initial size of a cubic octree node in meters.",
)
size_limit: PositiveFloat = Field(
default=500.0,
description="Smallest possible size of an octree node in meters.",
)
east_bounds: tuple[float, float] = Field(
default=(-10 * KM, 10 * KM),
description="East bounds of the octree in meters.",
)
north_bounds: tuple[float, float] = Field(
default=(-10 * KM, 10 * KM),
description="North bounds of the octree in meters.",
)
depth_bounds: tuple[float, float] = Field(
default=(0 * KM, 20 * KM),
description="Depth bounds of the octree in meters.",
)
absorbing_boundary: float = Field(
default=1 * KM,
ge=0.0,
description="Absorbing boundary in meters. Detections inside the boundary will be tagged.",
)

_root_nodes: list[Node] = PrivateAttr([])
_cached_coordinates: dict[CoordSystem, np.ndarray] = PrivateAttr({})
Expand Down
30 changes: 25 additions & 5 deletions lassie/station_corrections.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,12 +315,32 @@ class StationCorrections(BaseModel):
default=None,
description="Lassie rundir to calculate the corrections from.",
)
measure: Literal["median", "average"] = "median"
weighting: ArrivalWeighting = "mul-PhaseNet-semblance"
measure: Literal["median", "average"] = Field(
default="median",
description="Arithmetic measure for the traveltime delays. "
"Choose from `median` and `average`.",
)
weighting: ArrivalWeighting = Field(
default="mul-PhaseNet-semblance",
description="Weighting of the traveltime delays. Choose from `none`, "
"`PhaseNet`, `semblance`, `add-PhaseNet-semblance`"
" and `mul-PhaseNet-semblance`.",
)

minimum_num_picks: PositiveInt = 5
minimum_distance_border: PositiveFloat = 2000.0
minimum_depth: PositiveFloat = 3000.0
minimum_num_picks: PositiveInt = Field(
default=5,
description="Minimum number of picks at a station required"
" to calculate station corrections.",
)
minimum_distance_border: PositiveFloat = Field(
default=2000.0,
description="Minimum distance to the octree border "
"to be considered for correction.",
)
minimum_depth: PositiveFloat = Field(
default=3000.0,
description="Minimum depth of the detection to be considered for correction.",
)

_station_corrections: dict[str, StationCorrection] = PrivateAttr({})
_traveltime_delay_cache: dict[tuple[NSL, PhaseDescription], float] = PrivateAttr({})
Expand Down
22 changes: 14 additions & 8 deletions lassie/tracers/cake.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,11 @@ class EarthModel(BaseModel):
)
format: Literal["nd", "hyposat"] = Field(
default="nd",
description="Format of the velocity model. nd or hyposat is supported.",
description="Format of the velocity model. `nd` or `hyposat` is supported.",
)
crust2_profile: constr(to_upper=True) | tuple[float, float] = Field(
default="",
description="Crust2 profile name or a tuple of (lat, lon) coordinates.",
description="Crust2 profile name or a tuple of `(lat, lon)` coordinates.",
)

raw_file_data: str | None = Field(
Expand Down Expand Up @@ -495,18 +495,24 @@ def get_travel_time(self, source: Location, receiver: Location) -> float:

class CakeTracer(RayTracer):
tracer: Literal["CakeTracer"] = "CakeTracer"
phases: dict[PhaseDescription, Timing] = {
"cake:P": Timing(definition="P,p"),
"cake:S": Timing(definition="S,s"),
}
earthmodel: EarthModel = Field(default_factory=EarthModel)
phases: dict[PhaseDescription, Timing] = Field(
default={
"cake:P": Timing(definition="P,p"),
"cake:S": Timing(definition="S,s"),
},
description="Dictionary of phases and timings to calculate.",
)
earthmodel: EarthModel = Field(
default_factory=EarthModel,
description="Earth model to calculate travel times for.",
)
trim_earth_model_depth: bool = Field(
default=True,
description="Trim earth model to max depth of the octree.",
)
lut_cache_size: ByteSize = Field(
default=2 * GiB,
description="Size of the LUT cache.",
description="Size of the LUT cache. Default is `2G`.",
)

_traveltime_trees: dict[PhaseDescription, TravelTimeTree] = PrivateAttr({})
Expand Down
Loading

0 comments on commit a083c2a

Please sign in to comment.