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.
Files changed (54) hide show
  1. gw_nomad-0.1.0/.gitignore +16 -0
  2. gw_nomad-0.1.0/.gitlab-ci.yml +78 -0
  3. gw_nomad-0.1.0/CHANGELOG.md +34 -0
  4. gw_nomad-0.1.0/Dockerfile +39 -0
  5. gw_nomad-0.1.0/PKG-INFO +187 -0
  6. gw_nomad-0.1.0/README.md +119 -0
  7. gw_nomad-0.1.0/docs/gen_ref_nav.py +58 -0
  8. gw_nomad-0.1.0/docs/index.md +1 -0
  9. gw_nomad-0.1.0/mkdocs.yml +48 -0
  10. gw_nomad-0.1.0/pyproject.toml +226 -0
  11. gw_nomad-0.1.0/src/nomad/__init__.py +6 -0
  12. gw_nomad-0.1.0/src/nomad/_version.py +24 -0
  13. gw_nomad-0.1.0/src/nomad/cli/__init__.py +0 -0
  14. gw_nomad-0.1.0/src/nomad/cli/convert.py +61 -0
  15. gw_nomad-0.1.0/src/nomad/cli/sources.py +300 -0
  16. gw_nomad-0.1.0/src/nomad/cli/subtract.py +227 -0
  17. gw_nomad-0.1.0/src/nomad/cli/train.py +109 -0
  18. gw_nomad-0.1.0/src/nomad/core/__init__.py +0 -0
  19. gw_nomad-0.1.0/src/nomad/core/filters.py +46 -0
  20. gw_nomad-0.1.0/src/nomad/core/firhp.py +61 -0
  21. gw_nomad-0.1.0/src/nomad/core/modulate.py +79 -0
  22. gw_nomad-0.1.0/src/nomad/core/parametrization.py +162 -0
  23. gw_nomad-0.1.0/src/nomad/core/resample.py +127 -0
  24. gw_nomad-0.1.0/src/nomad/core/shaping.py +54 -0
  25. gw_nomad-0.1.0/src/nomad/core/sosbank.py +35 -0
  26. gw_nomad-0.1.0/src/nomad/core/subtract.py +306 -0
  27. gw_nomad-0.1.0/src/nomad/model/__init__.py +1 -0
  28. gw_nomad-0.1.0/src/nomad/model/convert.py +233 -0
  29. gw_nomad-0.1.0/src/nomad/model/format.py +312 -0
  30. gw_nomad-0.1.0/src/nomad/tests/__init__.py +0 -0
  31. gw_nomad-0.1.0/src/nomad/tests/test_cli.py +242 -0
  32. gw_nomad-0.1.0/src/nomad/tests/test_convert.py +225 -0
  33. gw_nomad-0.1.0/src/nomad/tests/test_core_dsp.py +299 -0
  34. gw_nomad-0.1.0/src/nomad/tests/test_end_to_end.py +145 -0
  35. gw_nomad-0.1.0/src/nomad/tests/test_engine.py +380 -0
  36. gw_nomad-0.1.0/src/nomad/tests/test_model_format.py +120 -0
  37. gw_nomad-0.1.0/src/nomad/tests/test_nomad_subtract_element.py +218 -0
  38. gw_nomad-0.1.0/src/nomad/tests/test_parametrization.py +166 -0
  39. gw_nomad-0.1.0/src/nomad/tests/test_report.py +108 -0
  40. gw_nomad-0.1.0/src/nomad/tests/test_sources.py +155 -0
  41. gw_nomad-0.1.0/src/nomad/tests/test_train_math.py +288 -0
  42. gw_nomad-0.1.0/src/nomad/tests/test_train_pipeline.py +189 -0
  43. gw_nomad-0.1.0/src/nomad/tests/utils.py +106 -0
  44. gw_nomad-0.1.0/src/nomad/train/__init__.py +0 -0
  45. gw_nomad-0.1.0/src/nomad/train/collect.py +68 -0
  46. gw_nomad-0.1.0/src/nomad/train/config.py +109 -0
  47. gw_nomad-0.1.0/src/nomad/train/cost.py +170 -0
  48. gw_nomad-0.1.0/src/nomad/train/fdomain.py +51 -0
  49. gw_nomad-0.1.0/src/nomad/train/optimize.py +148 -0
  50. gw_nomad-0.1.0/src/nomad/train/preprocess.py +300 -0
  51. gw_nomad-0.1.0/src/nomad/train/report.py +239 -0
  52. gw_nomad-0.1.0/src/nomad/transforms/__init__.py +1 -0
  53. gw_nomad-0.1.0/src/nomad/transforms/nomad_subtract.py +217 -0
  54. gw_nomad-0.1.0/uv.lock +3131 -0
@@ -0,0 +1,16 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ build/
5
+ dist/
6
+ site/
7
+ .coverage*
8
+ coverage.xml
9
+ .pytest_cache/
10
+ .mypy_cache/
11
+ .ruff_cache/
12
+ .venv/
13
+ src/nomad/_version.py
14
+ *.h5
15
+ *.gwf
16
+ *.pickle
@@ -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"]
@@ -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.
@@ -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]