60 分钟真实数据快照已执行的网页阅读版

检查真实 GW150914 探测器应变

审计冻结的 Hanford 记录,估计频谱,并检查指定零相位带通,不将其作为新的探测结论。

物理 · 第 05 课🔋 离线核心分析⏱ 60 分钟真实数据快照📦 numpy · pandas · scipy · matplotlib · IPython

目标与运行方法

本课适合具备 Python 基础的本科生。先阅读每一步的方法与图注,再修改参数。AI 练习为可选部分,不需要 AI 账号即可完成核心课程。数据写在 notebook metadata 中,不需要另下载数据文件。请保持 notebook 已保存到当前工作文件夹。

学习任务是理解本课的研究问题、执行透明的计算、校验结果,并说明结论的边界。

环境与参数

In [1] · Python
from pathlib import Path
import base64, gzip, hashlib, json, platform
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import display, HTML
from scipy.signal import welch, butter, sosfiltfilt
from IPython.display import display as _display
def display(value):
    if isinstance(value,pd.DataFrame):
        table=value.to_html(max_rows=12,escape=True)
        style="<style>.vibeit-readable-table{max-width:100%;overflow-x:auto}.vibeit-readable-table table{width:max-content;max-width:none;border-collapse:collapse}.vibeit-readable-table td,.vibeit-readable-table th{white-space:nowrap!important;word-break:normal!important;overflow-wrap:normal!important}</style>"
        return _display(HTML(style+'<div class="vibeit-readable-table">'+table+'</div>'))
    return _display(value)
RECIPE_ID='ph05-gw150914'
OUTPUT_DIR=Path.cwd()/"results"/RECIPE_ID
plt.rcParams.update({"figure.dpi":120,"font.size":10,"axes.spines.top":False,"axes.spines.right":False,
                      "axes.prop_cycle":plt.cycler(color=["#18785f","#416aa6","#bc6541","#9974a5"])})
print("Python:",platform.python_version(),"; NumPy:",np.__version__,"; Pandas:",pd.__version__)
print("Working folder:",Path.cwd())
print("Core data mode: embedded snapshot; no network requests")
Python: 3.13.13 ; NumPy: 2.5.3 ; Pandas: 3.0.6
Working folder: /Users/zhiluo/Developer/vibeit-cookbook/cookbook/downloads
Core data mode: embedded snapshot; no network requests

读取内嵌快照

In [2] · Python
def load_snapshot(recipe_id, expected_sha256, snapshot_key):
    """Read embedded data from this notebook, including a renamed copy."""
    for path in sorted(Path.cwd().glob("*.ipynb")):
        try:
            notebook = json.loads(path.read_text(encoding="utf-8"))
            metadata = notebook.get("metadata", {}).get("vibeit_cookbook", {})
            blob = metadata.get("snapshots", {}).get(snapshot_key)
            if metadata.get("recipe_id") != recipe_id or not blob:
                continue
            if blob.get("sha256") != expected_sha256:
                continue
            raw = gzip.decompress(base64.b64decode(blob["gzip_base64"], validate=True))
            if hashlib.sha256(raw).hexdigest() != expected_sha256:
                raise ValueError("Embedded snapshot checksum mismatch: " + snapshot_key)
            return json.loads(raw)
        except (OSError, json.JSONDecodeError):
            continue
    raise FileNotFoundError(
        "Save/open the complete .ipynb in the active working folder. "
        "The embedded snapshot could not be located for " + recipe_id)

SNAPSHOT_HASHES={'gw150914': '4068109f074163cc9a5b99edc94606ede7862f9b5907e7903c307fe7fd713edf'}
def snapshot(key):
    return load_snapshot(RECIPE_ID,SNAPSHOT_HASHES[key],key)
print("Embedded snapshots:",list(SNAPSHOT_HASHES))
Embedded snapshots: ['gw150914']

方法函数

数据与模型边界

单摆、振子、抛体和温度轨迹是明确声明的数值模型,不是实验室测量。使用的 g0 为约定标准重力加速度。GW150914 使用真实冻结应变和已发表事件时间;滤波曲线不是独立的显著性或源参数分析。黑体曲线假定理想辐射体。

分步分析

1. 审计真实应变样本及事件时间

内嵌 GWOSC 快照为 H1、4096 样本/秒、32 秒,应变无量纲。时间相对于已发表事件 GPS;事件标签来自目录,不由此图阈值判断。

In [3] · Python
gw=snapshot("gw150914");strain=np.asarray(gw["strain"],float);fs=gw["sample_rate"]
assert len(strain)==fs*gw["duration"] and np.isfinite(strain).all()
relative_time=gw["gps_start"]+np.arange(len(strain))/fs-gw["event_gps"]
centered=strain-strain.mean()
print("Release:",gw["version"],"; detector:",gw["detector"],"; samples:",len(strain),"; rate:",fs,"Hz")
fig,ax=plt.subplots(figsize=(9,4));ax.plot(relative_time,centered,lw=.5)
ax.axvline(0,color="red",linestyle="--",label="Catalogued event time")
ax.set(xlabel="Time relative to catalogued event (s)",ylabel="H1 strain (dimensionless)",title="Frozen detector measurements");ax.legend();plt.tight_layout();plt.show()
Release: GW150914 v3 / 4KHZ_R1 ; detector: H1 ; samples: 131072 ; rate: 4096 Hz
Computed figure from this code cell

2. 估计振幅谱密度

Welch 对重叠加窗片段进行平均。sqrt(PSD) 的单位为应变/sqrt(Hz),不同于原始 FFT 振幅。来源已降采样,不能将近 Nyquist 细节当成独立校准的高频结果。

In [4] · Python
frequency,psd=welch(centered,fs=fs,nperseg=fs*4,noverlap=fs*2,scaling="density")
positive=frequency>0
fig,ax=plt.subplots(figsize=(8,5));ax.loglog(frequency[positive],np.sqrt(psd[positive]))
ax.axvspan(35,350,color="orange",alpha=.12,label="Declared display band")
ax.set(xlim=(10,1500),xlabel="Frequency (Hz)",ylabel="ASD (strain / sqrt(Hz))",title="Welch spectrum of the frozen record");ax.legend();plt.tight_layout();plt.show()
Computed figure from this code cell

3. 应用声明过的非因果显示滤波器

四阶 Butterworth 35–350 Hz 带通前向、反向各应用一次。这是离线零相位显示处理,会使用记录内未来样本。单探测器滤波轨迹不提供虚警概率,也不复现正式探测流程。

In [5] · Python
sos=butter(4,[35,350],btype="bandpass",fs=fs,output="sos")
filtered=sosfiltfilt(sos,centered)
window=(relative_time>=-.25)&(relative_time<=.15)
fig,ax=plt.subplots(figsize=(9,4));ax.plot(relative_time[window],1e21*filtered[window],lw=1)
ax.axvline(0,color="gray",linestyle="--")
ax.set(xlabel="Time relative to catalogued event (s)",ylabel="Filtered strain (10⁻²¹)",title="Declared band-pass; interpretation requires the published analysis");plt.tight_layout();plt.show()
Computed figure from this code cell

4. 审计频谱归一化并导出

使用整段实数 FFT 的 Parseval 恒等式做独立归一化检查;它不同于平均 Welch PSD。每个派生序列随同滤波频带及事件 metadata 导出。

In [6] · Python
transform=np.fft.rfft(centered);squared=np.abs(transform)**2
fft_variance=(squared[0]+squared[-1]+2*squared[1:-1].sum())/len(centered)**2
assert np.isclose(fft_variance,np.mean(centered**2),rtol=1e-12,atol=0)
assert np.isfinite(filtered).all() and np.all(psd>=0)
OUTPUT_DIR.mkdir(parents=True,exist_ok=True)
pd.DataFrame({"relative_time_s":relative_time,"centered_strain":centered,"filtered_strain":filtered}).to_csv(OUTPUT_DIR/"strain-display.csv",index=False)
pd.DataFrame({"frequency_Hz":frequency,"PSD_strain_squared_per_Hz":psd}).to_csv(OUTPUT_DIR/"welch-spectrum.csv",index=False)
(OUTPUT_DIR/"processing.json").write_text(json.dumps({"detector":gw["detector"],"release":gw["version"],"event_GPS":gw["event_gps"],"sample_rate_Hz":fs,"band_Hz":[35,350],"filter":"4th-order Butterworth SOS, forward/backward","inference":"display processing; no independent detection significance"},indent=2))
print("Parseval check passed; source credit: GWOSC, LIGO, Virgo and KAGRA. Exports:",OUTPUT_DIR)
Parseval check passed; source credit: GWOSC, LIGO, Virgo and KAGRA. Exports: /Users/zhiluo/Developer/vibeit-cookbook/cookbook/downloads/results/ph05-gw150914

自主练习与参考答案

  1. 区分 PSD、ASD 与 FFT 振幅的单位。
  2. 为何前后向滤波不是实时处理?

参考解释。 ASD 为 PSD 的平方根,单位为应变/sqrt(Hz)。非因果滤波读取未来样本;显示波形本身不是显著性计算。

先保留当前 notebook 副本,再修改参数。把新图与原图一起比较,并记录改变了哪些输入、哪些计算和哪些结论。

可选的 AI 编程练习

提示词 1

仅用 NumPy、SciPy、Matplotlib 比较 30–300 与 35–350 Hz,保留原始应变及明确设置。

提示词 2

使用同样的包增加滤波幅频响应并标频带,保留 FFT Parseval 校验。

人工验收。 保留 131072 个样本、来源时间及署名,输出有限,明确非因果显示性质。

检查修改后的源代码,再手动运行受影响单元及后续校验。要求助手解释修改的目的、使用的包和数据来源,并用实际输出核对解释。

可选联网扩展

In [7] · Python
RUN_ONLINE_UPDATE=False
if RUN_ONLINE_UPDATE:
    import requests
    try:
        response=requests.get('https://gwosc.org/eventapi/json/GWTC-1-confident/GW150914/v3/',timeout=20)
        response.raise_for_status()
        print("Source:",response.url)
        print(response.text[:700])
    except requests.RequestException as error:
        print("Online extension unavailable:",type(error).__name__,str(error)[:160])
else:
    print("Online extension skipped; embedded core data unchanged")
Online extension skipped; embedded core data unchanged

来源、快照与下一步

  • GWOSC GW150914 v3 event metadata — CC BY 4.0; acknowledge GWOSC, LIGO, Virgo and KAGRA; GWTC-1 DOI 10.7935/82H3-HH23; retrieved 2026-10-09. SHA-256 1ce014e3277cc6ef491daa48250465eefda026f2b974d2a15bb8c6ea5596e37e. Preserve event GPS, release and strain download parameters; event catalog values are not recomputed detection significance.

  • GWOSC H1 32 second strain — CC BY 4.0; credit GWOSC and collaborations; DOI 10.7935/82H3-HH23; retrieved 2026-10-09. SHA-256 fefe8717306109460b6c9cff74da6beb9e80ee624824b4b09bdd2c54ce9b4dfc. Decompress original ASCII data and parse all 131072 strain samples; preserve GPS start and sampling rate.

下一步。 在保留本课的数据检查、参数记录和结果校验之后,将相同方法用于自己的公开或已获授权数据。记录研究设计与方法限制,不把示例结果当成新实验的验证。完整数据许可与生成说明见 cookbook 网页。