Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change df_to_inventory to use a local copy of NRL if needed #272

Merged
merged 3 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
master
- obsplus.utils.stations
* Fixed df_to_inventory to use a local copy of the NRL for compatibility
with ObsPy and NRLv2 (Note that NRLv1 is no longer accessible)

obsplus 0.2.5
- obsplus
* Modernized packaging, including src file and pyrpoject.toml (#259)
Expand Down
1 change: 1 addition & 0 deletions src/obsplus/constants.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Constants used throughout obsplus."""

from collections import OrderedDict
from os import cpu_count
from pathlib import Path
Expand Down
1 change: 0 additions & 1 deletion src/obsplus/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""A collection of ObsPlus Exceptions and Warnings."""


# --- Exceptions


Expand Down
1 change: 1 addition & 0 deletions src/obsplus/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Note: These are used instead of the ones in obspy.clients.base so the subclass
hooks can be used.
"""

from abc import abstractmethod
from typing_extensions import Protocol, runtime_checkable

Expand Down
35 changes: 24 additions & 11 deletions src/obsplus/utils/stations.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import warnings
from functools import singledispatch, lru_cache
from pathlib import Path
from typing import Optional
from typing import Optional, Union

import numpy as np
import obspy
Expand Down Expand Up @@ -88,8 +88,9 @@ class _InventoryConstructor:
channel=("channel", "location", "start_date", "end_date"),
)

def __init__(self, station_client=None):
def __init__(self, station_client=None, nrl_path=None):
self._client = station_client
self._nrl_path = nrl_path

def _groupby_if_exists(self, df, level):
"""Groupby columns if they exist on dataframe, else return empty."""
Expand Down Expand Up @@ -136,13 +137,12 @@ def _get_kwargs(self, series, key_mapping):

return out

@property
@lru_cache()
def nrl_client(self):
def nrl_client(self, path):
"""Initiate a nominal response library object."""
from obspy.clients.nrl import NRL

return NRL()
return NRL(str(path))

@property
@lru_cache()
Expand All @@ -159,8 +159,8 @@ def station_client(self):
return client

@lru_cache()
def get_nrl_response(self, datalogger_keys, sensor_keys):
nrl = self.nrl_client
def get_nrl_response(self, path, datalogger_keys, sensor_keys):
nrl = self.nrl_client(path)
kwargs = dict(datalogger_keys=datalogger_keys, sensor_keys=sensor_keys)
return nrl.get_response(**kwargs)

Expand All @@ -182,8 +182,12 @@ def _update_nrl_response(self, response, df):
# df doesn't have needed columns, just exit
logger_keys, sensor_keys = df["datalogger_keys"], df["sensor_keys"]
valid = (~logger_keys.isna()) & (~sensor_keys.isna())
if valid.any() and self._nrl_path is None:
raise AttributeError(
"nrl_path must be specified if using NRL to get instrument responses"
)
response[valid] = [
self.get_nrl_response(tuple(x), tuple(y))
self.get_nrl_response(self._nrl_path, tuple(x), tuple(y))
for x, y in zip(logger_keys[valid], sensor_keys[valid])
]

Expand Down Expand Up @@ -319,7 +323,9 @@ def _make_inventory(self, df: pd.DataFrame):

@compose_docstring(station_columns=station_col_str)
def df_to_inventory(
df: pd.DataFrame, client: Optional[StationClient] = None
df: pd.DataFrame,
client: Optional[StationClient] = None,
nrl_path: Optional[Union[Path, str]] = None,
) -> obspy.Inventory:
"""
Create an inventory from a dataframe.
Expand All @@ -334,6 +340,10 @@ def df_to_inventory(
client
Any client with a `get_stations` method. Only used if the dataframe
contains special columns for retrieving channel responses.
nrl_path
The path to a local copy of the Nominal Response Library. Only used
if the dataframe contains special columns for retrieving channel
responses.

Notes
-----
Expand All @@ -344,13 +354,16 @@ def df_to_inventory(
If the dataframe has columns named "sensor_keys" and "datalogger_keys"
these will indicate the response information should be fetched using
ObsPy's NRL client. Each of these columns should either contain lists
or josn-loadable strings.
or json-loadable strings.
For example, to specify sensor keys:
["Nanometrics", "Trillium 120 Horizon"]
or
'["Nanometrics", "Trillium 120 Horizon"]'
are both valid.

Note that the capability of ObsPy's NRL client to parse the online NRL has
been deprecated and it is now necessary to download and use a local copy.

2. Using a station client:
If the dataframe contains a column get_stations_kwargs it indicates that
either a client was passed as the client argument or the fdsn IRIS client
Expand All @@ -375,7 +388,7 @@ def df_to_inventory(
- If both NRL and client methods are indicated by column contents an
AmbiguousResponseError will be raised.
"""
inv_constructor = _InventoryConstructor(station_client=client)
inv_constructor = _InventoryConstructor(station_client=client, nrl_path=nrl_path)
return inv_constructor(df)


Expand Down
1 change: 1 addition & 0 deletions src/obsplus/version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Module for reporting the package version."""

from contextlib import suppress
from importlib.metadata import PackageNotFoundError, version

Expand Down
8 changes: 8 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ def internet_available():
"""Test if internet resources are available."""
import socket

# TODO: This isn't a consistent way to determine if there's internet, especially
# behind a corporate firewall
address = "8.8.8.8"
port = 53
try:
Expand Down Expand Up @@ -164,6 +166,12 @@ def load_and_update_dataset(name):
has_internet = internet_available()


@pytest.fixture(scope="session")
def data_path():
"""For scenarios when you really just need a path to test data"""
return TEST_DATA_PATH


# -------------------- collection of test cases


Expand Down
21 changes: 21 additions & 0 deletions tests/test_data/mini_nrl/datalogger/Nanometrics/Centaur.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[Main]
question = "What is the full scale voltage?"

[4Vpp]
path = "Centaur_FV4Vpp.txt"

[40Vpp]
path = "Centaur_FV40Vpp.txt"

[2Vpp]
path = "Centaur_FV2Vpp.txt"

[20Vpp]
path = "Centaur_FV20Vpp.txt"

[1Vpp]
path = "Centaur_FV1Vpp.txt"

[10Vpp]
path = "Centaur_FV10Vpp.txt"

66 changes: 66 additions & 0 deletions tests/test_data/mini_nrl/datalogger/Nanometrics/Centaur_FV1Vpp.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
[Main]
question = "What is the final sample rate?"

[80 Hz]
path = "Centaur_FV1Vpp_FR80.txt"


[5 Hz]
path = "Centaur_FV1Vpp_FR5.txt"


[50 Hz]
path = "Centaur_FV1Vpp_FR50.txt"


[500 Hz]
path = "Centaur_FV1Vpp_FR500.txt"


[5000 Hz]
path = "Centaur_FV1Vpp_FR5000.txt"


[40 Hz]
path = "Centaur_FV1Vpp_FR40.txt"


[2 Hz]
path = "Centaur_FV1Vpp_FR2.txt"


[250 Hz]
path = "Centaur_FV1Vpp_FR250.txt"


[20 Hz]
path = "Centaur_FV1Vpp_FR20.txt"


[200 Hz]
path = "Centaur_FV1Vpp_FR200.txt"


[2000 Hz]
path = "Centaur_FV1Vpp_FR2000.txt"


[1 Hz]
path = "Centaur_FV1Vpp_FR1.txt"


[125 Hz]
path = "Centaur_FV1Vpp_FR125.txt"


[10 Hz]
path = "Centaur_FV1Vpp_FR10.txt"


[100 Hz]
path = "Centaur_FV1Vpp_FR100.txt"


[1000 Hz]
path = "Centaur_FV1Vpp_FR1000.txt"

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Main]
question = "What is the DC removal/low pass filter setting?"

[Off]
path = "Centaur_FV1Vpp_FR40_DFOff.txt"


[0.001]
path = "Centaur_FV1Vpp_FR40_DF0.001.txt"

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[Main]
question = "What is the final filter phase?"

[Minimum]
description = "Nanometrics; Centaur; Full-Scale_Voltage 1Vpp; Final_Sample_Rate 40 Hz; DC_Filter Off; Final_Filter_Phase Minimum"
xml = "Centaur_FV1Vpp_FR40_DFOff_FPMinimum.xml"


[Linear]
description = "Nanometrics; Centaur; Full-Scale_Voltage 1Vpp; Final_Sample_Rate 40 Hz; DC_Filter Off; Final_Filter_Phase Linear"
xml = "Centaur_FV1Vpp_FR40_DFOff_FPLinear.xml"

Loading
Loading