gw-nomad 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gw_nomad-0.1.0/.gitignore +16 -0
- gw_nomad-0.1.0/.gitlab-ci.yml +78 -0
- gw_nomad-0.1.0/CHANGELOG.md +34 -0
- gw_nomad-0.1.0/Dockerfile +39 -0
- gw_nomad-0.1.0/PKG-INFO +187 -0
- gw_nomad-0.1.0/README.md +119 -0
- gw_nomad-0.1.0/docs/gen_ref_nav.py +58 -0
- gw_nomad-0.1.0/docs/index.md +1 -0
- gw_nomad-0.1.0/mkdocs.yml +48 -0
- gw_nomad-0.1.0/pyproject.toml +226 -0
- gw_nomad-0.1.0/src/nomad/__init__.py +6 -0
- gw_nomad-0.1.0/src/nomad/_version.py +24 -0
- gw_nomad-0.1.0/src/nomad/cli/__init__.py +0 -0
- gw_nomad-0.1.0/src/nomad/cli/convert.py +61 -0
- gw_nomad-0.1.0/src/nomad/cli/sources.py +300 -0
- gw_nomad-0.1.0/src/nomad/cli/subtract.py +227 -0
- gw_nomad-0.1.0/src/nomad/cli/train.py +109 -0
- gw_nomad-0.1.0/src/nomad/core/__init__.py +0 -0
- gw_nomad-0.1.0/src/nomad/core/filters.py +46 -0
- gw_nomad-0.1.0/src/nomad/core/firhp.py +61 -0
- gw_nomad-0.1.0/src/nomad/core/modulate.py +79 -0
- gw_nomad-0.1.0/src/nomad/core/parametrization.py +162 -0
- gw_nomad-0.1.0/src/nomad/core/resample.py +127 -0
- gw_nomad-0.1.0/src/nomad/core/shaping.py +54 -0
- gw_nomad-0.1.0/src/nomad/core/sosbank.py +35 -0
- gw_nomad-0.1.0/src/nomad/core/subtract.py +306 -0
- gw_nomad-0.1.0/src/nomad/model/__init__.py +1 -0
- gw_nomad-0.1.0/src/nomad/model/convert.py +233 -0
- gw_nomad-0.1.0/src/nomad/model/format.py +312 -0
- gw_nomad-0.1.0/src/nomad/tests/__init__.py +0 -0
- gw_nomad-0.1.0/src/nomad/tests/test_cli.py +242 -0
- gw_nomad-0.1.0/src/nomad/tests/test_convert.py +225 -0
- gw_nomad-0.1.0/src/nomad/tests/test_core_dsp.py +299 -0
- gw_nomad-0.1.0/src/nomad/tests/test_end_to_end.py +145 -0
- gw_nomad-0.1.0/src/nomad/tests/test_engine.py +380 -0
- gw_nomad-0.1.0/src/nomad/tests/test_model_format.py +120 -0
- gw_nomad-0.1.0/src/nomad/tests/test_nomad_subtract_element.py +218 -0
- gw_nomad-0.1.0/src/nomad/tests/test_parametrization.py +166 -0
- gw_nomad-0.1.0/src/nomad/tests/test_report.py +108 -0
- gw_nomad-0.1.0/src/nomad/tests/test_sources.py +155 -0
- gw_nomad-0.1.0/src/nomad/tests/test_train_math.py +288 -0
- gw_nomad-0.1.0/src/nomad/tests/test_train_pipeline.py +189 -0
- gw_nomad-0.1.0/src/nomad/tests/utils.py +106 -0
- gw_nomad-0.1.0/src/nomad/train/__init__.py +0 -0
- gw_nomad-0.1.0/src/nomad/train/collect.py +68 -0
- gw_nomad-0.1.0/src/nomad/train/config.py +109 -0
- gw_nomad-0.1.0/src/nomad/train/cost.py +170 -0
- gw_nomad-0.1.0/src/nomad/train/fdomain.py +51 -0
- gw_nomad-0.1.0/src/nomad/train/optimize.py +148 -0
- gw_nomad-0.1.0/src/nomad/train/preprocess.py +300 -0
- gw_nomad-0.1.0/src/nomad/train/report.py +239 -0
- gw_nomad-0.1.0/src/nomad/transforms/__init__.py +1 -0
- gw_nomad-0.1.0/src/nomad/transforms/nomad_subtract.py +217 -0
- gw_nomad-0.1.0/uv.lock +3131 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
include:
|
|
2
|
+
# -- python
|
|
3
|
+
- component: git.ligo.org/computing/gitlab/components/python/sdist@1
|
|
4
|
+
- component: git.ligo.org/computing/gitlab/components/python/wheel@1
|
|
5
|
+
- component: git.ligo.org/computing/gitlab/components/python/code-quality@1
|
|
6
|
+
inputs:
|
|
7
|
+
analyzer: "ruff"
|
|
8
|
+
requirements: "-r requirements-lint.txt"
|
|
9
|
+
- component: git.ligo.org/computing/gitlab/components/python/dependency-scanning@1
|
|
10
|
+
- component: git.ligo.org/computing/gitlab/components/python/type-checking@1
|
|
11
|
+
inputs:
|
|
12
|
+
project_dir: "src"
|
|
13
|
+
- component: git.ligo.org/computing/gitlab/components/python/test@1
|
|
14
|
+
inputs:
|
|
15
|
+
install_extra: test
|
|
16
|
+
python_versions:
|
|
17
|
+
- "3.11"
|
|
18
|
+
- "3.12"
|
|
19
|
+
- "3.13"
|
|
20
|
+
# -- docker
|
|
21
|
+
- component: git.ligo.org/computing/gitlab/components/docker/build@1
|
|
22
|
+
inputs:
|
|
23
|
+
image_tag: $CI_COMMIT_SHA
|
|
24
|
+
- component: git.ligo.org/computing/gitlab/components/docker/push@1
|
|
25
|
+
inputs:
|
|
26
|
+
pull_image_tag: $CI_COMMIT_SHA
|
|
27
|
+
push_image_tag: $CI_COMMIT_REF_NAME
|
|
28
|
+
push_when: "all"
|
|
29
|
+
tag_latest: false
|
|
30
|
+
rules:
|
|
31
|
+
- if: $CI_COMMIT_BRANCH
|
|
32
|
+
- if: $CI_COMMIT_TAG !~ /^(\d+\.)?(\d+\.)?(\*|\d+)$/
|
|
33
|
+
- component: git.ligo.org/computing/gitlab/components/docker/push@1
|
|
34
|
+
inputs:
|
|
35
|
+
pull_image_tag: $CI_COMMIT_SHA
|
|
36
|
+
push_image_tag: $CI_COMMIT_TAG
|
|
37
|
+
push_when: "tags"
|
|
38
|
+
tag_latest: true
|
|
39
|
+
rules:
|
|
40
|
+
# tag latest only on new versions
|
|
41
|
+
- if: $CI_COMMIT_TAG =~ /^(\d+\.)?(\d+\.)?(\*|\d+)$/
|
|
42
|
+
# -- docs
|
|
43
|
+
- component: git.ligo.org/computing/gitlab/components/mkdocs/build@1
|
|
44
|
+
inputs:
|
|
45
|
+
requirements: "-r requirements-docs.txt"
|
|
46
|
+
- component: git.ligo.org/computing/gitlab/components/mkdocs/pages@1
|
|
47
|
+
inputs:
|
|
48
|
+
pages_when: "default"
|
|
49
|
+
|
|
50
|
+
# -- customizations
|
|
51
|
+
docker_push_gitlab:
|
|
52
|
+
rules:
|
|
53
|
+
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
54
|
+
when: never
|
|
55
|
+
- when: always
|
|
56
|
+
|
|
57
|
+
ruff:
|
|
58
|
+
needs:
|
|
59
|
+
- requirements
|
|
60
|
+
|
|
61
|
+
mkdocs:
|
|
62
|
+
needs:
|
|
63
|
+
- requirements
|
|
64
|
+
|
|
65
|
+
# -- requirements
|
|
66
|
+
requirements:
|
|
67
|
+
stage: build
|
|
68
|
+
image: python:3.12
|
|
69
|
+
script:
|
|
70
|
+
- python -m pip install pipx
|
|
71
|
+
- pipx ensurepath
|
|
72
|
+
- source ~/.bashrc
|
|
73
|
+
- pipx install hatch
|
|
74
|
+
- hatch dep show requirements --feature docs > requirements-docs.txt
|
|
75
|
+
- hatch dep show requirements --feature lint > requirements-lint.txt
|
|
76
|
+
artifacts:
|
|
77
|
+
paths:
|
|
78
|
+
- requirements-*.txt
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- Shaping filter support (model format version 2): nonsens models trained
|
|
6
|
+
with a frequency-domain `shaping_filter` now convert instead of being
|
|
7
|
+
rejected, and the engine applies the tabulated response to the noise
|
|
8
|
+
estimate as a streaming FIR (`--shaping-fir-duration`, default 1 s, half
|
|
9
|
+
of which becomes latency). Omitting it would leave `|T-1|` of the noise
|
|
10
|
+
in the target — ~10% at 100 Hz and worse-than-nothing above 1 kHz for the
|
|
11
|
+
O4 H1 jitter model.
|
|
12
|
+
|
|
13
|
+
- Converter: models trained at the target rate (empty real resampling zpk
|
|
14
|
+
arrays in the pickle) no longer fail to write with an h5py dtype
|
|
15
|
+
conversion error.
|
|
16
|
+
|
|
17
|
+
- Frame source: channels split across frame types (HOFT strain vs raw
|
|
18
|
+
witnesses) are located automatically. `--auxiliary-cache` (repeatable)
|
|
19
|
+
adds caches; entries are grouped by frame type, one file per type is
|
|
20
|
+
probed for its channel list, and one FrameReader is built per frame type.
|
|
21
|
+
|
|
22
|
+
- Initial implementation: sgn reimplementation of NonSENS streaming noise
|
|
23
|
+
subtraction.
|
|
24
|
+
- HDF5 model format and `nomad-convert-model` converter for original
|
|
25
|
+
nonsens pickles.
|
|
26
|
+
- `SubtractionEngine` / `NomadSubtract` streaming element with
|
|
27
|
+
noise-estimate (`noise`) and subtracted (`hsub`) outputs, gap handling,
|
|
28
|
+
and a `--measure-only` mode for feeding the calibration pipeline's
|
|
29
|
+
NOLINES -> CLEAN subtractor.
|
|
30
|
+
- `nomad-subtract` CLI: arrakis/frames/fake sources, arrakis publishing,
|
|
31
|
+
frame writing.
|
|
32
|
+
- `nomad-train` CLI: native training (scipy L-BFGS over the analytic
|
|
33
|
+
cost/gradient, no TensorFlow), YAML config, target defaulting to the
|
|
34
|
+
NOLINES channel.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# build stage: export locked dependencies
|
|
2
|
+
FROM ghcr.io/astral-sh/uv:0.8-python3.13-bookworm AS build
|
|
3
|
+
|
|
4
|
+
WORKDIR /app
|
|
5
|
+
RUN apt-get update && apt-get install -y --no-install-recommends git && \
|
|
6
|
+
rm -rf /var/lib/apt/lists/*
|
|
7
|
+
|
|
8
|
+
COPY . .
|
|
9
|
+
|
|
10
|
+
RUN uv export --no-editable --no-emit-project --locked \
|
|
11
|
+
--format requirements.txt -o requirements.txt
|
|
12
|
+
RUN uv build --wheel
|
|
13
|
+
|
|
14
|
+
# install stage: install dependencies into virtualenv
|
|
15
|
+
FROM python:3.13 AS install
|
|
16
|
+
|
|
17
|
+
WORKDIR /app
|
|
18
|
+
COPY --from=build /app/requirements.txt /app
|
|
19
|
+
COPY --from=build /app/dist/*.whl /app
|
|
20
|
+
|
|
21
|
+
RUN python -m venv /opt/venv
|
|
22
|
+
ENV PATH="/opt/venv/bin:$PATH"
|
|
23
|
+
|
|
24
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
25
|
+
RUN pip install --no-cache-dir --no-deps *.whl
|
|
26
|
+
|
|
27
|
+
# final stage: minimal runtime image
|
|
28
|
+
FROM python:3.13-slim
|
|
29
|
+
|
|
30
|
+
COPY --from=install /opt/venv /opt/venv
|
|
31
|
+
|
|
32
|
+
RUN groupadd --gid 1000 nomad && \
|
|
33
|
+
useradd --uid 1000 --gid 1000 -m nomad
|
|
34
|
+
|
|
35
|
+
USER nomad
|
|
36
|
+
|
|
37
|
+
ENV PATH="/opt/venv/bin:$PATH"
|
|
38
|
+
|
|
39
|
+
ENTRYPOINT ["nomad-subtract"]
|
gw_nomad-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gw-nomad
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: NOMAD: NOn-stationary Modulation-Aware Denoiser for sgn
|
|
5
|
+
Author-email: Zach Yarbrough <zach.yarbrough@ligo.org>
|
|
6
|
+
Maintainer-email: Zach Yarbrough <zach.yarbrough@ligo.org>
|
|
7
|
+
License-Expression: GPL-3.0-or-later
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
14
|
+
Requires-Python: >=3.11
|
|
15
|
+
Requires-Dist: h5py
|
|
16
|
+
Requires-Dist: numpy
|
|
17
|
+
Requires-Dist: pyyaml
|
|
18
|
+
Requires-Dist: scipy>=1.10
|
|
19
|
+
Requires-Dist: sgn-arrakis
|
|
20
|
+
Requires-Dist: sgn-ligo
|
|
21
|
+
Requires-Dist: sgn-ts
|
|
22
|
+
Requires-Dist: sgn>=0.6
|
|
23
|
+
Provides-Extra: convert
|
|
24
|
+
Requires-Dist: dill; extra == 'convert'
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: dill; extra == 'dev'
|
|
27
|
+
Requires-Dist: markdown-callouts>=0.2; extra == 'dev'
|
|
28
|
+
Requires-Dist: markdown-exec>=0.5; extra == 'dev'
|
|
29
|
+
Requires-Dist: matplotlib; extra == 'dev'
|
|
30
|
+
Requires-Dist: mkdocs-coverage>=0.2; extra == 'dev'
|
|
31
|
+
Requires-Dist: mkdocs-gen-files>=0.3; extra == 'dev'
|
|
32
|
+
Requires-Dist: mkdocs-literate-nav>=0.4; extra == 'dev'
|
|
33
|
+
Requires-Dist: mkdocs-material-igwn; extra == 'dev'
|
|
34
|
+
Requires-Dist: mkdocs-section-index>=0.3; extra == 'dev'
|
|
35
|
+
Requires-Dist: mkdocs>=1.3; extra == 'dev'
|
|
36
|
+
Requires-Dist: mkdocstrings[python]; extra == 'dev'
|
|
37
|
+
Requires-Dist: mypy; extra == 'dev'
|
|
38
|
+
Requires-Dist: mypy-extensions; extra == 'dev'
|
|
39
|
+
Requires-Dist: pip; extra == 'dev'
|
|
40
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
41
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
42
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
43
|
+
Requires-Dist: toml>=0.10; extra == 'dev'
|
|
44
|
+
Provides-Extra: docs
|
|
45
|
+
Requires-Dist: markdown-callouts>=0.2; extra == 'docs'
|
|
46
|
+
Requires-Dist: markdown-exec>=0.5; extra == 'docs'
|
|
47
|
+
Requires-Dist: mkdocs-coverage>=0.2; extra == 'docs'
|
|
48
|
+
Requires-Dist: mkdocs-gen-files>=0.3; extra == 'docs'
|
|
49
|
+
Requires-Dist: mkdocs-literate-nav>=0.4; extra == 'docs'
|
|
50
|
+
Requires-Dist: mkdocs-material-igwn; extra == 'docs'
|
|
51
|
+
Requires-Dist: mkdocs-section-index>=0.3; extra == 'docs'
|
|
52
|
+
Requires-Dist: mkdocs>=1.3; extra == 'docs'
|
|
53
|
+
Requires-Dist: mkdocstrings[python]; extra == 'docs'
|
|
54
|
+
Requires-Dist: toml>=0.10; extra == 'docs'
|
|
55
|
+
Provides-Extra: lint
|
|
56
|
+
Requires-Dist: mypy; extra == 'lint'
|
|
57
|
+
Requires-Dist: mypy-extensions; extra == 'lint'
|
|
58
|
+
Requires-Dist: pip; extra == 'lint'
|
|
59
|
+
Requires-Dist: ruff; extra == 'lint'
|
|
60
|
+
Provides-Extra: plot
|
|
61
|
+
Requires-Dist: matplotlib; extra == 'plot'
|
|
62
|
+
Provides-Extra: test
|
|
63
|
+
Requires-Dist: dill; extra == 'test'
|
|
64
|
+
Requires-Dist: matplotlib; extra == 'test'
|
|
65
|
+
Requires-Dist: pytest; extra == 'test'
|
|
66
|
+
Requires-Dist: pytest-cov; extra == 'test'
|
|
67
|
+
Description-Content-Type: text/markdown
|
|
68
|
+
|
|
69
|
+
# NOMAD
|
|
70
|
+
|
|
71
|
+
**NOn-stationary Modulation-Aware Denoiser** — an [sgn](https://git.ligo.org/greg/sgn)-based
|
|
72
|
+
reimplementation of [NonSENS](https://git.ligo.org/gabriele-vajente/nonsens)
|
|
73
|
+
(Vajente et al., PRD 101, 042003) streaming noise subtraction for
|
|
74
|
+
gravitational-wave strain data.
|
|
75
|
+
|
|
76
|
+
NOMAD measures excess noise in a target strain channel from auxiliary "noise
|
|
77
|
+
witness" channels that couple into strain through transfer functions whose
|
|
78
|
+
amplitude is modulated in time by slow "modulation witness" channels. The
|
|
79
|
+
noise estimate is a sum of terms `alpha_i(f) * s_i(t)`, where each `s_i` is a
|
|
80
|
+
modulated signal (noise witness × products of modulation witnesses) and each
|
|
81
|
+
`alpha_i` is an optimal filter learned offline. The stationary case
|
|
82
|
+
(`modulation_order = 0`) is plain multi-channel Wiener filtering.
|
|
83
|
+
|
|
84
|
+
NOMAD's primary production role is the noise **measurement**: the SGN
|
|
85
|
+
calibration pipeline's CAL-NOLINES → CAL-CLEAN stage is a plain subtractor
|
|
86
|
+
that needs some other process to compute what to subtract (in O4 this was the
|
|
87
|
+
NonSENS front-end installation). `nomad-subtract --measure-only
|
|
88
|
+
--publish-noise` is that process, publishing the noise-estimate channel to
|
|
89
|
+
arrakis on witnesses alone. It can also do the subtraction itself and publish
|
|
90
|
+
or frame the cleaned channel directly.
|
|
91
|
+
|
|
92
|
+
## Status
|
|
93
|
+
|
|
94
|
+
- **v0.1**: streaming application of trained models as an sgn pipeline,
|
|
95
|
+
reading witness and target channels from
|
|
96
|
+
[arrakis](https://docs.ligo.org/ngdd/arrakis-python/) (or frame files) and
|
|
97
|
+
publishing the noise estimate and/or subtracted strain back to arrakis
|
|
98
|
+
(or frames).
|
|
99
|
+
- **v0.2**: native model training (`nomad-train`, scipy L-BFGS over the
|
|
100
|
+
analytic cost/gradient — no TensorFlow), plus conversion of original
|
|
101
|
+
nonsens pickles (`nomad-convert-model`).
|
|
102
|
+
|
|
103
|
+
## Installation
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
pip install . # runtime
|
|
107
|
+
pip install .[convert] # + dill, for converting original nonsens pickles
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Usage
|
|
111
|
+
|
|
112
|
+
Train a model (or convert an existing NonSENS pickle):
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
nomad-train --config lho_isc.yaml --data-source frames \
|
|
116
|
+
--frame-cache O4.cache --start 1400000000 --end 1400000600 \
|
|
117
|
+
--output lho_isc.h5
|
|
118
|
+
|
|
119
|
+
nomad-convert-model lho_isc.pickle lho_isc.h5 # legacy pickles
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The training config's `target` defaults to `{ifo}:GDS-CALIB_STRAIN_NOLINES`:
|
|
123
|
+
the noise estimate is consumed downstream of the line subtraction, so
|
|
124
|
+
training against NOLINES keeps already-removed lines out of the CSD
|
|
125
|
+
estimates. Set `target` in the config (or pass `--target`) to train against
|
|
126
|
+
any other channel, e.g. raw `GDS-CALIB_STRAIN` — as in the original nonsens,
|
|
127
|
+
the target is simply whatever channel you name. A minimal config:
|
|
128
|
+
|
|
129
|
+
```yaml
|
|
130
|
+
ifo: H1
|
|
131
|
+
noise_witnesses: [ASC-DHARD_P_OUT_DQ, ASC-DHARD_Y_OUT_DQ, 60.0] # numbers = synthesized lines
|
|
132
|
+
modulation_witnesses: []
|
|
133
|
+
fs: 1024
|
|
134
|
+
t_fft: 10
|
|
135
|
+
fband: [8, 256]
|
|
136
|
+
n_sos: 10
|
|
137
|
+
glitches: {band: [15, 20], threshold: 3.0e-20}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Production: measure the excess noise and publish the estimate for the
|
|
141
|
+
calibration pipeline's CLEAN subtractor (no target channel needed):
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
export ARRAKIS_SERVER=grpc://...
|
|
145
|
+
nomad-subtract --model lho_isc.h5 --data-source arrakis --queue-timeout 30 \
|
|
146
|
+
--measure-only --publish-noise --publisher-id nomad-h1 \
|
|
147
|
+
--noise-channel-name H1:CAL-NOMAD_NOISE_ESTIMATE
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Standalone cleaning — arrakis in, subtracted strain out:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
nomad-subtract --model lho_isc.h5 --data-source arrakis --queue-timeout 30 \
|
|
154
|
+
--publish-arrakis --publisher-id nomad-h1 \
|
|
155
|
+
--output-channel-name H1:GDS-CALIB_STRAIN_NOMAD
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Published channels must be pre-registered with the arrakis server under the
|
|
159
|
+
given `--publisher-id`, at the target sample rate (float64). Note the noise
|
|
160
|
+
estimate carries the model's FIR high-pass group delay (`length/2` seconds)
|
|
161
|
+
as stream latency; timestamps are true GPS times, so a timestamp-aligned
|
|
162
|
+
downstream subtractor handles it naturally.
|
|
163
|
+
|
|
164
|
+
Run offline — frames in, frames out:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
nomad-subtract --model lho_isc.h5 --data-source frames \
|
|
168
|
+
--frame-cache O3.cache --start 1242441180 --end 1242443180 \
|
|
169
|
+
--frame-output-path '{instruments}-{description}-{gps_start_time}-{duration}.gwf'
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Channels rarely share a frame type (strain lives in HOFT frames, witnesses
|
|
173
|
+
in raw R frames); pass each extra cache with `--auxiliary-cache`
|
|
174
|
+
(repeatable). Cache entries are grouped by frame type, one file per type is
|
|
175
|
+
probed for its channels, and each channel is read from whichever frame type
|
|
176
|
+
provides it — a single concatenated mixed cache works too. This applies to
|
|
177
|
+
`nomad-train` as well.
|
|
178
|
+
|
|
179
|
+
## Development
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
hatch test # run the test suite
|
|
183
|
+
hatch run check # mypy
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Golden-comparison tests against the original nonsens implementation are
|
|
187
|
+
skipped unless `NOMAD_GOLDEN=1` is set and `nonsens` is importable.
|
gw_nomad-0.1.0/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# NOMAD
|
|
2
|
+
|
|
3
|
+
**NOn-stationary Modulation-Aware Denoiser** — an [sgn](https://git.ligo.org/greg/sgn)-based
|
|
4
|
+
reimplementation of [NonSENS](https://git.ligo.org/gabriele-vajente/nonsens)
|
|
5
|
+
(Vajente et al., PRD 101, 042003) streaming noise subtraction for
|
|
6
|
+
gravitational-wave strain data.
|
|
7
|
+
|
|
8
|
+
NOMAD measures excess noise in a target strain channel from auxiliary "noise
|
|
9
|
+
witness" channels that couple into strain through transfer functions whose
|
|
10
|
+
amplitude is modulated in time by slow "modulation witness" channels. The
|
|
11
|
+
noise estimate is a sum of terms `alpha_i(f) * s_i(t)`, where each `s_i` is a
|
|
12
|
+
modulated signal (noise witness × products of modulation witnesses) and each
|
|
13
|
+
`alpha_i` is an optimal filter learned offline. The stationary case
|
|
14
|
+
(`modulation_order = 0`) is plain multi-channel Wiener filtering.
|
|
15
|
+
|
|
16
|
+
NOMAD's primary production role is the noise **measurement**: the SGN
|
|
17
|
+
calibration pipeline's CAL-NOLINES → CAL-CLEAN stage is a plain subtractor
|
|
18
|
+
that needs some other process to compute what to subtract (in O4 this was the
|
|
19
|
+
NonSENS front-end installation). `nomad-subtract --measure-only
|
|
20
|
+
--publish-noise` is that process, publishing the noise-estimate channel to
|
|
21
|
+
arrakis on witnesses alone. It can also do the subtraction itself and publish
|
|
22
|
+
or frame the cleaned channel directly.
|
|
23
|
+
|
|
24
|
+
## Status
|
|
25
|
+
|
|
26
|
+
- **v0.1**: streaming application of trained models as an sgn pipeline,
|
|
27
|
+
reading witness and target channels from
|
|
28
|
+
[arrakis](https://docs.ligo.org/ngdd/arrakis-python/) (or frame files) and
|
|
29
|
+
publishing the noise estimate and/or subtracted strain back to arrakis
|
|
30
|
+
(or frames).
|
|
31
|
+
- **v0.2**: native model training (`nomad-train`, scipy L-BFGS over the
|
|
32
|
+
analytic cost/gradient — no TensorFlow), plus conversion of original
|
|
33
|
+
nonsens pickles (`nomad-convert-model`).
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install . # runtime
|
|
39
|
+
pip install .[convert] # + dill, for converting original nonsens pickles
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
Train a model (or convert an existing NonSENS pickle):
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
nomad-train --config lho_isc.yaml --data-source frames \
|
|
48
|
+
--frame-cache O4.cache --start 1400000000 --end 1400000600 \
|
|
49
|
+
--output lho_isc.h5
|
|
50
|
+
|
|
51
|
+
nomad-convert-model lho_isc.pickle lho_isc.h5 # legacy pickles
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The training config's `target` defaults to `{ifo}:GDS-CALIB_STRAIN_NOLINES`:
|
|
55
|
+
the noise estimate is consumed downstream of the line subtraction, so
|
|
56
|
+
training against NOLINES keeps already-removed lines out of the CSD
|
|
57
|
+
estimates. Set `target` in the config (or pass `--target`) to train against
|
|
58
|
+
any other channel, e.g. raw `GDS-CALIB_STRAIN` — as in the original nonsens,
|
|
59
|
+
the target is simply whatever channel you name. A minimal config:
|
|
60
|
+
|
|
61
|
+
```yaml
|
|
62
|
+
ifo: H1
|
|
63
|
+
noise_witnesses: [ASC-DHARD_P_OUT_DQ, ASC-DHARD_Y_OUT_DQ, 60.0] # numbers = synthesized lines
|
|
64
|
+
modulation_witnesses: []
|
|
65
|
+
fs: 1024
|
|
66
|
+
t_fft: 10
|
|
67
|
+
fband: [8, 256]
|
|
68
|
+
n_sos: 10
|
|
69
|
+
glitches: {band: [15, 20], threshold: 3.0e-20}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Production: measure the excess noise and publish the estimate for the
|
|
73
|
+
calibration pipeline's CLEAN subtractor (no target channel needed):
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
export ARRAKIS_SERVER=grpc://...
|
|
77
|
+
nomad-subtract --model lho_isc.h5 --data-source arrakis --queue-timeout 30 \
|
|
78
|
+
--measure-only --publish-noise --publisher-id nomad-h1 \
|
|
79
|
+
--noise-channel-name H1:CAL-NOMAD_NOISE_ESTIMATE
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Standalone cleaning — arrakis in, subtracted strain out:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
nomad-subtract --model lho_isc.h5 --data-source arrakis --queue-timeout 30 \
|
|
86
|
+
--publish-arrakis --publisher-id nomad-h1 \
|
|
87
|
+
--output-channel-name H1:GDS-CALIB_STRAIN_NOMAD
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Published channels must be pre-registered with the arrakis server under the
|
|
91
|
+
given `--publisher-id`, at the target sample rate (float64). Note the noise
|
|
92
|
+
estimate carries the model's FIR high-pass group delay (`length/2` seconds)
|
|
93
|
+
as stream latency; timestamps are true GPS times, so a timestamp-aligned
|
|
94
|
+
downstream subtractor handles it naturally.
|
|
95
|
+
|
|
96
|
+
Run offline — frames in, frames out:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
nomad-subtract --model lho_isc.h5 --data-source frames \
|
|
100
|
+
--frame-cache O3.cache --start 1242441180 --end 1242443180 \
|
|
101
|
+
--frame-output-path '{instruments}-{description}-{gps_start_time}-{duration}.gwf'
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Channels rarely share a frame type (strain lives in HOFT frames, witnesses
|
|
105
|
+
in raw R frames); pass each extra cache with `--auxiliary-cache`
|
|
106
|
+
(repeatable). Cache entries are grouped by frame type, one file per type is
|
|
107
|
+
probed for its channels, and each channel is read from whichever frame type
|
|
108
|
+
provides it — a single concatenated mixed cache works too. This applies to
|
|
109
|
+
`nomad-train` as well.
|
|
110
|
+
|
|
111
|
+
## Development
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
hatch test # run the test suite
|
|
115
|
+
hatch run check # mypy
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Golden-comparison tests against the original nonsens implementation are
|
|
119
|
+
skipped unless `NOMAD_GOLDEN=1` is set and `nonsens` is importable.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Copyright (c) 2021, Timothee Mazzucotelli
|
|
2
|
+
#
|
|
3
|
+
# Permission to use, copy, modify, and/or distribute this software for any
|
|
4
|
+
# purpose with or without fee is hereby granted, provided that the above
|
|
5
|
+
# copyright notice and this permission notice appear in all copies.
|
|
6
|
+
#
|
|
7
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
8
|
+
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
9
|
+
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
10
|
+
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
11
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
12
|
+
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
13
|
+
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
14
|
+
|
|
15
|
+
"""Generate the code reference pages and navigation."""
|
|
16
|
+
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
import mkdocs_gen_files
|
|
20
|
+
|
|
21
|
+
nav = mkdocs_gen_files.Nav()
|
|
22
|
+
|
|
23
|
+
src = Path("src")
|
|
24
|
+
|
|
25
|
+
for path in sorted(src.joinpath("nomad").rglob("*.py")):
|
|
26
|
+
if path.name[0] == "_" or "tests" in path.parts:
|
|
27
|
+
continue
|
|
28
|
+
|
|
29
|
+
# Module path relative to src/ (e.g., nomad/core/subtract)
|
|
30
|
+
module_path = path.relative_to(src).with_suffix("")
|
|
31
|
+
# Doc path mirrors module structure
|
|
32
|
+
doc_path = path.relative_to(src).with_suffix(".md")
|
|
33
|
+
full_doc_path = Path("reference", doc_path)
|
|
34
|
+
|
|
35
|
+
parts = tuple(module_path.parts)
|
|
36
|
+
|
|
37
|
+
if parts[-1] == "__init__":
|
|
38
|
+
parts = parts[:-1]
|
|
39
|
+
doc_path = doc_path.with_name("index.md")
|
|
40
|
+
full_doc_path = full_doc_path.with_name("index.md")
|
|
41
|
+
elif parts[-1] == "__main__":
|
|
42
|
+
continue
|
|
43
|
+
|
|
44
|
+
nav[parts] = doc_path.as_posix()
|
|
45
|
+
|
|
46
|
+
with mkdocs_gen_files.open(full_doc_path, "w") as fd:
|
|
47
|
+
ident = ".".join(parts)
|
|
48
|
+
fd.write(f"::: {ident}")
|
|
49
|
+
|
|
50
|
+
mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(src))
|
|
51
|
+
|
|
52
|
+
with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file:
|
|
53
|
+
nav_file.writelines(nav.build_literate_nav())
|
|
54
|
+
|
|
55
|
+
changelog = Path("CHANGELOG.md")
|
|
56
|
+
if changelog.exists():
|
|
57
|
+
with mkdocs_gen_files.open("changelog.md", "w") as fd:
|
|
58
|
+
fd.write(changelog.read_text())
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--8<-- "README.md"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
site_name: NOMAD
|
|
2
|
+
site_description: "NOMAD: NOn-stationary Modulation-Aware Denoiser for sgn"
|
|
3
|
+
site_url: https://detchar.docs.ligo.org/sgn-dq/nomad
|
|
4
|
+
repo_url: https://git.ligo.org/detchar/sgn-dq/nomad
|
|
5
|
+
repo_name: detchar/sgn-dq/nomad
|
|
6
|
+
site_dir: site
|
|
7
|
+
watch: [README.md, src/nomad]
|
|
8
|
+
|
|
9
|
+
nav:
|
|
10
|
+
- Home:
|
|
11
|
+
- Overview: index.md
|
|
12
|
+
# defer to gen-files + literate-nav
|
|
13
|
+
- API Reference: reference/
|
|
14
|
+
- Changelog: changelog.md
|
|
15
|
+
|
|
16
|
+
theme:
|
|
17
|
+
name: material_igwn
|
|
18
|
+
features:
|
|
19
|
+
- content.code.copy
|
|
20
|
+
- content.code.annotate
|
|
21
|
+
|
|
22
|
+
markdown_extensions:
|
|
23
|
+
- admonition
|
|
24
|
+
- callouts:
|
|
25
|
+
strip_period: no
|
|
26
|
+
- pymdownx.highlight
|
|
27
|
+
- pymdownx.inlinehilite
|
|
28
|
+
- pymdownx.magiclink
|
|
29
|
+
- pymdownx.snippets:
|
|
30
|
+
check_paths: true
|
|
31
|
+
- pymdownx.superfences
|
|
32
|
+
- toc:
|
|
33
|
+
permalink: true
|
|
34
|
+
toc_depth: 4
|
|
35
|
+
|
|
36
|
+
plugins:
|
|
37
|
+
- search
|
|
38
|
+
- markdown-exec
|
|
39
|
+
- gen-files:
|
|
40
|
+
scripts:
|
|
41
|
+
- docs/gen_ref_nav.py
|
|
42
|
+
- literate-nav:
|
|
43
|
+
nav_file: SUMMARY.md
|
|
44
|
+
- section-index
|
|
45
|
+
- mkdocstrings:
|
|
46
|
+
handlers:
|
|
47
|
+
python:
|
|
48
|
+
paths: [src]
|