From 8d0688c5bac3cec5a693ef0642cef671cc384fc0 Mon Sep 17 00:00:00 2001 From: Mi! Date: Fri, 1 Jul 2022 16:29:28 +0200 Subject: [PATCH] Adding Examples --- .pre-commit-config.yaml | 20 ++-- README.md | 20 ++-- examples/1-denoise-DAS-data.ipynb | 95 +++++++++++++++++++ lightguide/__init__.py | 5 +- .../{afk_filter.py => afk_filter_python.py} | 34 +------ lightguide/filters.py | 2 + lightguide/utils.py | 95 ++++++++++++++++++- pyproject.toml | 2 +- setup.py | 3 +- {tests => test}/conftest.py | 0 .../data/das-iceland-locations-stations.txt | 80 ++++++++-------- {tests => test}/test_afk_filter.py | 22 ++--- {tests => test}/test_gf.py | 0 {tests => test}/test_restitution.py | 0 test/test_utils.py | 20 ++++ 15 files changed, 292 insertions(+), 106 deletions(-) create mode 100644 examples/1-denoise-DAS-data.ipynb rename lightguide/{afk_filter.py => afk_filter_python.py} (72%) create mode 100644 lightguide/filters.py rename {tests => test}/conftest.py (100%) rename {tests => test}/data/das-iceland-locations-stations.txt (82%) rename {tests => test}/test_afk_filter.py (67%) rename {tests => test}/test_gf.py (100%) rename {tests => test}/test_restitution.py (100%) create mode 100644 test/test_utils.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ca682fa..9393d4f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,15 +1,11 @@ # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.0.1 - hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-yaml - - id: check-added-large-files -- repo: https://github.com/ambv/black - rev: 21.6b0 - hooks: - - id: black - # language_version: python3.6 +- repo: local + hooks: + - id: black + name: black + language: system + types: [python] + entry: black + # language_version: python3.6 diff --git a/README.md b/README.md index acda3e6..2860b71 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@ *Tools for distributed acoustic sensing and modelling.* -[![PyPI version](https://badge.fury.io/py/lightguide.svg)](https://badge.fury.io/py/lightguide) [![DOI](https://zenodo.org/badge/495774991.svg)](https://zenodo.org/badge/latestdoi/495774991) +[![PyPI version](https://badge.fury.io/py/lightguide.svg)](https://badge.fury.io/py/lightguide) +Code style: black +[![DOI](https://zenodo.org/badge/495774991.svg)](https://zenodo.org/badge/latestdoi/495774991) Lightguide is a package for handling, filtering and modelling distributed acoustic sensing (DAS) data. The package interfaces handling and processing routines of DAS data to the [Pyrocko framework](https://pyrocko.org). Through Pyrocko's I/O engine :rocket: lightguide supports handling the following DAS data formats: @@ -29,12 +31,18 @@ pip install lightguide The adaptive frequency filter (AFK) can be used to suppress incoherent noise in DAS data sets. ```python -from lightguide import orafk_filter +from lightguide import filters +from lightguide.utils import download_numpy, ExampleData -filtered_data = orafk_filter.afk_filter( - data, window_size=32, overlap=15, exponent=0.8, normalize_power=False) + +das_data = download_numpy(ExampleData.VSPData) + +filtered_data = filters.afk_filter( + das_data, window_size=32, overlap=15, exponent=0.8, normalize_power=False) ``` +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/pyrocko/lightguide/blob/master/examples/1-denoise-DAS-data.ipynb) + The filtering performance of the AFK filter, applied to an earthquake recording at an [ICDP](https://www.icdp-online.org/home/) borehole observatory in Germany. The data was recorded on a [Silixa](https://silixa.com/) iDAS v2. For more details see . ![AFK Filter Performance](https://user-images.githubusercontent.com/4992805/170084970-9484afe7-9b95-45a0-ac8e-aec56ddfb3ea.png) @@ -45,11 +53,11 @@ The filtering performance of the AFK filter, applied to an earthquake recording Lightguide can be cited as: -> Isken, Marius Paul; Christopher, Wollin; Heimann, Sebastian; Dahm, Torsten (2022): Lightguide - Tools for distributed acoustic sensing. +> Marius Paul Isken, Sebastian Heimann, Christopher Wollin, Hannes Bathke, & Torsten Dahm. (2022). Lightguide - Seismological Tools for DAS data. Zenodo. https://doi.org/10.5281/zenodo.6580579 Details of the adaptive frequency filter are published here: -> Isken, Marius Paul; Vasyura-Bathke, Hannes; Dahm, Torsten; Heimann, Sebastian (2022): De-noising distributed acoustic sensing data using an adaptive frequency-wavenumber filter, Geophysical Journal International. +> Marius Paul Isken, Hannes Vasyura-Bathke, Torsten Dahm, Sebastian Heimann, De-noising distributed acoustic sensing data using an adaptive frequency-wavenumber filter, Geophysical Journal International, 2022;, ggac229, https://doi.org/10.1093/gji/ggac229 ## Packaging diff --git a/examples/1-denoise-DAS-data.ipynb b/examples/1-denoise-DAS-data.ipynb new file mode 100644 index 0000000..6a8fde8 --- /dev/null +++ b/examples/1-denoise-DAS-data.ipynb @@ -0,0 +1,95 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Lightguide Adaptive-Frequency-Filter\n", + "\n", + "In this notebook we de-noise an awefully noisy DAS data set.\n", + "\n", + "The vertical seismic profile shot was recorded in an ICDP borehole in Landwüst, Germany. The fibre is interrogated by a Silixa iDAS v2 and is cemented behind the casing." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install lightguide" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First we import the filter function from the `lightguide` module." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from lightguide import filters\n", + "from lightguide.utils import download_numpy, ExampleData\n", + "\n", + "\n", + "das_data = download_numpy(ExampleData.VSPData)\n", + "\n", + "filtered_data = filters.afk_filter(\n", + " das_data, window_size=32, overlap=15, exponent=0.8, normalize_power=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "fig, axes = plt.subplots(2, 1, figsize=(10, 10))\n", + "\n", + "ax = axes[0]\n", + "ax.set_title(\"Raw Data\")\n", + "ax.imshow(das_data, aspect=\"auto\", interpolation=\"none\")\n", + "\n", + "ax = axes[1]\n", + "ax.set_title(\"Filtered Data\")\n", + "ax.imshow(filtered_data, aspect=\"auto\", interpolation=\"none\")\n", + "\n", + "fig.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.9.2 ('venv': venv)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.2" + }, + "orig_nbformat": 4, + "vscode": { + "interpreter": { + "hash": "d6c2f566028469748ec83127a93d0ab1f5f314b33991e2ad2ee6fe2fd330af93" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/lightguide/__init__.py b/lightguide/__init__.py index 1889994..dc423ce 100644 --- a/lightguide/__init__.py +++ b/lightguide/__init__.py @@ -1 +1,4 @@ -from . import lightguide as orafk_filter # noqa +import pkg_resources + + +__version__ = pkg_resources.get_distribution("lightguide").version diff --git a/lightguide/afk_filter.py b/lightguide/afk_filter_python.py similarity index 72% rename from lightguide/afk_filter.py rename to lightguide/afk_filter_python.py index 39a1e64..b46dc91 100644 --- a/lightguide/afk_filter.py +++ b/lightguide/afk_filter_python.py @@ -5,40 +5,14 @@ import numpy as np import numpy.typing as npt -from scipy.signal import butter, lfilter data = np.load(Path(__file__).parent / "data" / "data-DAS-gfz2020wswf.npy") -def butter_bandpass_filter( - data: npt.NDArray, - lowcut: float, - highcut: float, - sampling_rate: float, - order: int = 4, -) -> npt.NDArray: - """Butterworth bandpass filter for 2D time-spatial array. - - Args: - data (npt.NDArray): Input data as [time, space] - lowcut (float): Low-cut frequency in [Hz]. - highcut (float): High-cut frequency in [Hz]. - sampling_rate (float): Sampling rate along the time dimension. - order (int, optional): Order of the filter. Defaults to 4. - - Returns: - npt.NDArray: _description_ - """ - coeff_b, coeff_a = butter( - order, (lowcut, highcut), btype="bandpass", fs=sampling_rate - ) - y = lfilter(coeff_b, coeff_a, data, axis=0) - return y - - @lru_cache -def triangular_taper(size: int, plateau: int) -> npt.NDArray: +def triangular_taper_python(size: int, plateau: int) -> npt.NDArray: + if plateau > size: raise ValueError("Plateau cannot be larger than size.") if size % 2 or plateau % 2: @@ -52,7 +26,7 @@ def triangular_taper(size: int, plateau: int) -> npt.NDArray: return window * window[:, np.newaxis] -def afk_filter( +def afk_filter_python( data: npt.NDArray, window_size: int = 32, overlap: int = 14, @@ -74,7 +48,7 @@ def afk_filter( raise ValueError("Padding does not match desired data shape") filtered_data = np.zeros_like(data) - taper = triangular_taper(window_size, window_non_overlap) + taper = triangular_taper_python(window_size, window_non_overlap) for iwin_x in range(nwin_x): px_x = iwin_x * window_stride diff --git a/lightguide/filters.py b/lightguide/filters.py new file mode 100644 index 0000000..f9ab200 --- /dev/null +++ b/lightguide/filters.py @@ -0,0 +1,2 @@ +from .lightguide import * # noqa +from .afk_filter_python import * # noqa diff --git a/lightguide/utils.py b/lightguide/utils.py index 3c9c2b8..82e1aaf 100644 --- a/lightguide/utils.py +++ b/lightguide/utils.py @@ -1,11 +1,29 @@ +from __future__ import annotations +from enum import Enum +from tempfile import SpooledTemporaryFile + import time +import logging from functools import wraps +from pathlib import Path from typing import Any, Callable - -from pyrocko.trace import Trace +import requests import numpy as np import numpy.typing as npt +from pyrocko.trace import Trace +from scipy.signal import butter, lfilter + + +# create console handler +ch = logging.StreamHandler() +formatter = logging.Formatter("\x1b[80D\x1b[1A\x1b[K%(message)s") +ch.setFormatter(formatter) + + +class ExampleData: + VSPData = "https://data.pyrocko.org/testing/lightguide/das-data.npy" + EQData = "https://data.pyrocko.org/testing/lightguide/data-DAS-gfz2020wswf.npy" class AttrDict(dict): @@ -15,11 +33,25 @@ def __init__(self, *args, **kwargs) -> None: def traces_to_numpy_and_meta(traces: list[Trace]) -> tuple[npt.NDArray, AttrDict]: + """Geneare a numpy 2-D array from a list of traces + + Args: + traces (list[Trace]): List of input traces + + Raises: + ValueError: Raised when the traces have different lengths / start times. + + Returns: + tuple[npt.NDArray, AttrDict]: The waveform data and meta information as dict. + """ + if not traces: + raise ValueError("No traces given") ntraces = len(traces) nsamples = set(tr.ydata.size for tr in traces) if len(nsamples) != 1: - raise ValueError("Traces nsamples differ") + raise ValueError("Traces number of samples differ.") + nsamples = nsamples.pop() data = np.zeros((ntraces, nsamples)) @@ -31,7 +63,64 @@ def traces_to_numpy_and_meta(traces: list[Trace]) -> tuple[npt.NDArray, AttrDict return data, AttrDict(meta) +def butter_bandpass_filter( + data: npt.NDArray, + lowcut: float, + highcut: float, + sampling_rate: float, + order: int = 4, +) -> npt.NDArray: + """Butterworth bandpass filter for 2D time-spatial array. + + Args: + data (npt.NDArray): Input data as [time, space] + lowcut (float): Low-cut frequency in [Hz]. + highcut (float): High-cut frequency in [Hz]. + sampling_rate (float): Sampling rate along the time dimension. + order (int, optional): Order of the filter. Defaults to 4. + + Returns: + npt.NDArray: Filtered wavefield. + """ + coeff_b, coeff_a = butter( + order, (lowcut, highcut), btype="bandpass", fs=sampling_rate + ) + y = lfilter(coeff_b, coeff_a, data, axis=0) + return y + + +def download_http(url: str, target: Path | SpooledTemporaryFile): + req = requests.get(url) + req.raise_for_status() + total_size = int(req.headers.get("Content-Length", 0)) + nbytes = 0 + + if isinstance(target, Path): + writer = target.write_bytes + elif isinstance(target, SpooledTemporaryFile): + writer = target.write + else: + raise TypeError("Bad target for download") + + for data in req.iter_content(chunk_size=4096): + nbytes += len(data) + print(f"\u001b[2KDownloading {url}: {nbytes}/{total_size} bytes", end="\r") + + writer(data) + print(f"\u001b[2KDownloaded {url}") + + +def download_numpy(url: str) -> np.ndarray: + file = SpooledTemporaryFile() + download_http(url, target=file) + file.flush() + file.seek(0) + return np.load(file) + + def timeit(func: Callable) -> Callable: + """A helper decorator to time function execution.""" + @wraps(func) def wrapper(*args, **kwargs) -> Any: t = time.time() diff --git a/pyproject.toml b/pyproject.toml index 8d664d8..ca62408 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ authors = [ urls = {GitHub = "https://github.com/pyrocko/lightguide"} keywords = ["distributed acoustic sensing", "DAS", "seismology", "earthquake modelling"] license = {file = "LICENSE"} -dependencies = ["pyrocko>=2022.4.28", "numpy>=1.20.0"] +dependencies = ["pyrocko>=2022.4.28", "numpy>=1.20.0", "requests>=2.20.0"] readme = "README.md" classifiers = [ "Programming Language :: Rust", diff --git a/setup.py b/setup.py index 71693d9..97d671a 100644 --- a/setup.py +++ b/setup.py @@ -24,13 +24,12 @@ ], rust_extensions=[ RustExtension( - "lightguide.orafk_filter", + "lightguide.afk_filter", path="Cargo.toml", binding=Binding.PyO3, debug=False, ) ], - package_data={"lightguide": ["data/*.npy"]}, packages=["lightguide"], python_requires=">=3.8", ) diff --git a/tests/conftest.py b/test/conftest.py similarity index 100% rename from tests/conftest.py rename to test/conftest.py diff --git a/tests/data/das-iceland-locations-stations.txt b/test/data/das-iceland-locations-stations.txt similarity index 82% rename from tests/data/das-iceland-locations-stations.txt rename to test/data/das-iceland-locations-stations.txt index 6676367..a136b19 100755 --- a/tests/data/das-iceland-locations-stations.txt +++ b/test/data/das-iceland-locations-stations.txt @@ -1,40 +1,40 @@ -5J.05119. 63.82725 -22.67712 0.0 0.0 -5J.04992. 63.83083 -22.67155 0.0 0.0 -5J.04864. 63.83406 -22.66579 0.0 0.0 -5J.04736. 63.83600 -22.65668 0.0 0.0 -5J.04608. 63.83991 -22.65147 0.0 0.0 -5J.04480. 63.84300 -22.64507 0.0 0.0 -5J.04352. 63.84610 -22.63717 0.0 0.0 -5J.04224. 63.84933 -22.63000 0.0 0.0 -5J.04096. 63.85254 -22.62261 0.0 0.0 -5J.03969. 63.85398 -22.61294 0.0 0.0 -5J.03841. 63.85595 -22.60373 0.0 0.0 -5J.03713. 63.85521 -22.59439 0.0 0.0 -5J.03585. 63.85409 -22.58429 0.0 0.0 -5J.03457. 63.85324 -22.57465 0.0 0.0 -5J.03329. 63.85459 -22.56499 0.0 0.0 -5J.03201. 63.85883 -22.56068 0.0 0.0 -5J.03073. 63.86222 -22.55340 0.0 0.0 -5J.02946. 63.86495 -22.54518 0.0 0.0 -5J.02818. 63.86642 -22.53514 0.0 0.0 -5J.02690. 63.86757 -22.52550 0.0 0.0 -5J.02562. 63.86656 -22.51521 0.0 0.0 -5J.02434. 63.86616 -22.50483 0.0 0.0 -5J.02306. 63.86613 -22.49439 0.0 0.0 -5J.02178. 63.86787 -22.48457 0.0 0.0 -5J.02050. 63.86989 -22.47506 0.0 0.0 -5J.01923. 63.87134 -22.46520 0.0 0.0 -5J.01795. 63.87258 -22.45499 0.0 0.0 -5J.01667. 63.87480 -22.44567 0.0 0.0 -5J.01539. 63.87709 -22.43664 0.0 0.0 -5J.01412. 63.87888 -22.43064 0.0 0.0 -5J.01284. 63.87482 -22.42599 0.0 0.0 -5J.01156. 63.87039 -22.42549 0.0 0.0 -5J.01028. 63.86590 -22.42404 0.0 0.0 -5J.00901. 63.86151 -22.42662 0.0 0.0 -5J.00773. 63.85709 -22.42943 0.0 0.0 -5J.00645. 63.85263 -22.43191 0.0 0.0 -5J.00517. 63.85036 -22.43858 0.0 0.0 -5J.00389. 63.84684 -22.44513 0.0 0.0 -5J.00261. 63.84249 -22.44831 0.0 0.0 -5J.00133. 63.84025 -22.44546 0.0 0.0 +5J.05119. 63.82725 -22.67712 0.0 0.0 +5J.04992. 63.83083 -22.67155 0.0 0.0 +5J.04864. 63.83406 -22.66579 0.0 0.0 +5J.04736. 63.83600 -22.65668 0.0 0.0 +5J.04608. 63.83991 -22.65147 0.0 0.0 +5J.04480. 63.84300 -22.64507 0.0 0.0 +5J.04352. 63.84610 -22.63717 0.0 0.0 +5J.04224. 63.84933 -22.63000 0.0 0.0 +5J.04096. 63.85254 -22.62261 0.0 0.0 +5J.03969. 63.85398 -22.61294 0.0 0.0 +5J.03841. 63.85595 -22.60373 0.0 0.0 +5J.03713. 63.85521 -22.59439 0.0 0.0 +5J.03585. 63.85409 -22.58429 0.0 0.0 +5J.03457. 63.85324 -22.57465 0.0 0.0 +5J.03329. 63.85459 -22.56499 0.0 0.0 +5J.03201. 63.85883 -22.56068 0.0 0.0 +5J.03073. 63.86222 -22.55340 0.0 0.0 +5J.02946. 63.86495 -22.54518 0.0 0.0 +5J.02818. 63.86642 -22.53514 0.0 0.0 +5J.02690. 63.86757 -22.52550 0.0 0.0 +5J.02562. 63.86656 -22.51521 0.0 0.0 +5J.02434. 63.86616 -22.50483 0.0 0.0 +5J.02306. 63.86613 -22.49439 0.0 0.0 +5J.02178. 63.86787 -22.48457 0.0 0.0 +5J.02050. 63.86989 -22.47506 0.0 0.0 +5J.01923. 63.87134 -22.46520 0.0 0.0 +5J.01795. 63.87258 -22.45499 0.0 0.0 +5J.01667. 63.87480 -22.44567 0.0 0.0 +5J.01539. 63.87709 -22.43664 0.0 0.0 +5J.01412. 63.87888 -22.43064 0.0 0.0 +5J.01284. 63.87482 -22.42599 0.0 0.0 +5J.01156. 63.87039 -22.42549 0.0 0.0 +5J.01028. 63.86590 -22.42404 0.0 0.0 +5J.00901. 63.86151 -22.42662 0.0 0.0 +5J.00773. 63.85709 -22.42943 0.0 0.0 +5J.00645. 63.85263 -22.43191 0.0 0.0 +5J.00517. 63.85036 -22.43858 0.0 0.0 +5J.00389. 63.84684 -22.44513 0.0 0.0 +5J.00261. 63.84249 -22.44831 0.0 0.0 +5J.00133. 63.84025 -22.44546 0.0 0.0 diff --git a/tests/test_afk_filter.py b/test/test_afk_filter.py similarity index 67% rename from tests/test_afk_filter.py rename to test/test_afk_filter.py index 71f62ba..0c4b719 100644 --- a/tests/test_afk_filter.py +++ b/test/test_afk_filter.py @@ -2,7 +2,7 @@ import numpy as num import pytest -from lightguide import afk_filter, orafk_filter +from lightguide import filters @pytest.fixture @@ -20,17 +20,17 @@ def data_big(): def test_taper(): - window = afk_filter.triangular_taper(32, 4) + window = filters.triangular_taper_python(32, 4) assert window[32 // 2, 32 // 2] == 1.0 assert window.shape == (32, 32) - taper_rust = orafk_filter.triangular_taper((32, 32), (4, 4)) + taper_rust = filters.triangular_taper((32, 32), (4, 4)) num.testing.assert_almost_equal(window, taper_rust) def test_plot_taper(show_plot): - taper_rust = orafk_filter.triangular_taper((32, 64), (4, 10)) + taper_rust = filters.triangular_taper((32, 64), (4, 10)) if show_plot: import matplotlib.pyplot as plt @@ -43,33 +43,33 @@ def test_plot_taper(show_plot): @pytest.mark.skip def test_benchmark_goldstein(benchmark, data_big): - benchmark(afk_filter.afk_filter, data_big, 32, 14, 0.5) + benchmark(filters.afk_filter_python, data_big, 32, 14, 0.5) @pytest.mark.skip def test_benchmark_goldstein_rust(benchmark, data_big): - benchmark(orafk_filter.afk_filter, data_big, 32, 14, 0.5, False) + benchmark(filters.afk_filter, data_big, 32, 14, 0.5, False) def test_goldstein_rust(data): - filtered_data_rust = orafk_filter.afk_filter( + filtered_data_rust = filters.afk_filter( data, 32, 14, exponent=0.0, normalize_power=False ) - filtered_data_rust_rect = orafk_filter.afk_filter_rectangular( + filtered_data_rust_rect = filters.afk_filter_rectangular( data, (32, 32), (14, 14), exponent=0.0, normalize_power=False ) num.testing.assert_almost_equal(filtered_data_rust_rect, filtered_data_rust) num.testing.assert_allclose(data, filtered_data_rust, rtol=1.0) - filtered_data_rust_rect = orafk_filter.afk_filter_rectangular( + filtered_data_rust_rect = filters.afk_filter_rectangular( data, (32, 16), (14, 7), exponent=0.0, normalize_power=False ) - filtered_data_rust_rect = orafk_filter.afk_filter_rectangular( + filtered_data_rust_rect = filters.afk_filter_rectangular( data, (32, 128), (14, 56), exponent=0.0, normalize_power=False ) - filtered_data_rust_rect = orafk_filter.afk_filter_rectangular( + filtered_data_rust_rect = filters.afk_filter_rectangular( data, (32, 200), (14, 80), exponent=0.0, normalize_power=False ) diff --git a/tests/test_gf.py b/test/test_gf.py similarity index 100% rename from tests/test_gf.py rename to test/test_gf.py diff --git a/tests/test_restitution.py b/test/test_restitution.py similarity index 100% rename from tests/test_restitution.py rename to test/test_restitution.py diff --git a/test/test_utils.py b/test/test_utils.py new file mode 100644 index 0000000..4cadb0d --- /dev/null +++ b/test/test_utils.py @@ -0,0 +1,20 @@ +from pathlib import Path +from lightguide.utils import download_http, download_numpy, ExampleData +import numpy as np + +URLS = [url for k, url in ExampleData.__dict__.items() if not k.startswith("_")] + + +def test_downloads(): + for url in URLS: + download_http(url, Path("/tmp/test")) + + +def test_download_numnpy(): + for url in URLS: + res = download_numpy(url) + assert isinstance(res, np.ndarray) + + +if __name__ == "__main__": + test_download_numnpy()