Structured scientific logging using Logbook
Project description
notata
notata is a minimal library for structured filesystem logging of scientific runs.
Each Logbook creates a run directory with parameters, arrays, plots, artifacts, metadata, and a timestamped log. Explicit. Reproducible. Grep-friendly.
Unlike ML logging tools (e.g. W&B, TensorBoard, MLflow), notata is built for scientific workflows such as simulations, solvers, and numerical experiments, where file-based logging matters more than dashboards. No servers, no setup, no reinventing the wheel for each new project.
Installation
pip install notata
Quick Start
Context Manager
Logs a single simulation run to log_<run_id>/, including arrays, metadata, and messages.
from notata import Logbook
import numpy as np
with Logbook("oscillator_dt1e-3", params={"omega": 2.0, "dt": 1e-3, "steps": 10_000}) as log:
omega = 2.0
dt = 1e-3
steps = 10_000
x, v = 1.0, 0.0
xs = np.empty(steps)
vs = np.empty(steps)
E = np.empty(steps)
for n in range(steps):
a = -omega**2 * x
x += v*dt + 0.5*a*dt*dt
a_new = -omega**2 * x
v += 0.5*(a + a_new)*dt
xs[n], vs[n] = x, v
E[n] = 0.5*(v**2 + (omega*x)**2)
if (n+1) % 2000 == 0:
log.info(f"step={n+1} x={x:.4f} v={v:.4f} E={E[n]:.6f}")
log.arrays("trajectory", x=xs, v=vs)
log.array("energy", E)
log.json("final_state", {"x": float(x), "v": float(v), "E": float(E[-1])})
This creates a structured folder with logs, parameters, and output arrays for reproducibility.
Manual Lifecycle
For full control, create a Logbook manually and mark it complete when you're done.
from notata import Logbook
import numpy as np
Nx = Ny = 64
kappa = 0.01
dx = 1.0
dt = 0.2 * dx*dx / kappa
X, Y = np.meshgrid(np.linspace(-1,1,Nx), np.linspace(-1,1,Ny), indexing="ij")
T = np.exp(-6*(X**2 + Y**2))
log = Logbook("heat_eq")
log.params(Nx=Nx, Ny=Ny, kappa=kappa, steps=steps)
for step in range(500):
lap = (np.roll(T,1,0)+np.roll(T,-1,0)+np.roll(T,1,1)+np.roll(T,-1,1)-4*T)
T += kappa * dt * lap
if (step+1) % 100 == 0:
log.array(f"states/T_step{step+1}", T)
log.info(f"step={step+1} maxT={T.max():.4f}")
log.json("final_stats", {"max": float(T.max()), "mean": float(T.mean())})
log.mark_complete()
Parameter sweeps with Experiment
Automatically log multiple runs, each in its own directory, with structured metadata and failure tracking:
from notata import Experiment
import numpy as np
exp = Experiment("falling_ball")
for dt in [0.01, 0.5]: # stable vs unstable
log = exp.add(dt=dt, skip_existing=True)
if log is None:
continue
with log:
v, h = 0.0, 100.0
for _ in range(100):
v += 9.81 * dt
h -= v * dt
if h < 0:
raise RuntimeError(f"Object hit the ground (h={h:.2f})")
log.json("metrics", {"final_height": h, "final_speed": v})
Each run creates a log_<run_id>/ folder and appends a row to index.csv with parameters, status, and final metrics:
| run_id | dt | status | final_height | final_speed |
|---|---|---|---|---|
| falling_ball_dt_0.01 | 0.01 | complete | 95.04595 | 9.81 |
| falling_ball_dt_0.5 | 0.5 | missing |
Using ExperimentReader
Since notata log dirs are somewhat verbose, we also provided a utility wrapper to load and read the data.
This makes it more intuitive when you need to compare runs (e.g. in experiments), or you just don't want to deal with the files paths.
For single runs you can directly use notata.LogReader.
from notata import ExperimentReader
import matplotlib.pyplot as plt
# Load the experiment
exp = ExperimentReader("outputs/oscillator_sweep")
# Plot energy vs time for each run
fig, ax = plt.subplots()
for run in exp:
omega, dt = run.params['omega'], run.params['dt']
energy = run.load_array("energy")
label = f"omega={omega}, dt={dt}"
ax.plot(energy, label=label)
ax.set(xlabel="Time step", ylabel="Energy", title="Energy vs Time")
ax.legend()
plt.show()
# Print summary of runs
for run in exp:
status = run.meta.get('status', 'unkown')
duration = run.meta.get('runtime_sec', 'unknown')
print(f"Run ID: {run.run_id}, {status=}, {duration=}")
Output format
Data is stored as following in order to be intuitive to explore:
log_<run_id>/
log.txt
metadata.json
params.yaml
data/
plots/
artifacts/
where the files follow:
| Path / Pattern | Purpose / Format |
|---|---|
log.txt |
Plain text log; lines: [YYYY-MM-DDTHH:MM:SS] LEVEL message |
metadata.json |
Run metadata: status, start_time, optional end_time, runtime_sec, optional failure_reason, run_id |
params.yaml / params.json |
Parameter snapshot (latest saved form) |
data/*.npy / data/*.npz |
.npy for single arrays (array()); .npz for multi-array bundles (arrays(...)) |
plots/*.(png|pdf|svg) |
Saved figures (save_plot) |
artifacts/*.txt |
Text artifacts (save_text) |
artifacts/*.json |
JSON artifacts (save_json) |
artifacts/*.pkl |
Pickled objects (save_pickle) |
artifacts/* (other) |
Raw bytes (save_bytes) |
artifacts/**/ |
Nested artifact categories |
Documentation
Full documentation, tutorials, and examples are available at: https://notata.readthedocs.io/en/latest/
Citation
You don't have to, but if you use notata in your research and need to reference it, please cite it as follows:
@software{notata_2025,
author = {Albert Alonso},
title = {notata: Structured Filesystem Logging for Scientific Runs},
url = {https://github.com/alonfnt/notata},
version = {0.2.0},
year = {2025}
}
License
MIT License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file notata-0.2.0.tar.gz.
File metadata
- Download URL: notata-0.2.0.tar.gz
- Upload date:
- Size: 18.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
04037a1f5493dd0ad669086603832038394e30d711c45fd499b4c47220fd4668
|
|
| MD5 |
1d1e8f3c3779e81e1effdd1902c400b3
|
|
| BLAKE2b-256 |
460533729fd552ff76530d6da0c71fb19bda0501c503d79826c0de84496095bf
|
Provenance
The following attestation bundles were made for notata-0.2.0.tar.gz:
Publisher:
publish-pypi.yml on alonfnt/notata
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
notata-0.2.0.tar.gz -
Subject digest:
04037a1f5493dd0ad669086603832038394e30d711c45fd499b4c47220fd4668 - Sigstore transparency entry: 319115892
- Sigstore integration time:
-
Permalink:
alonfnt/notata@3d3384a9548b53a5542bbda4c4ffd86d22954465 -
Branch / Tag:
refs/tags/0.2.0 - Owner: https://github.com/alonfnt
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@3d3384a9548b53a5542bbda4c4ffd86d22954465 -
Trigger Event:
release
-
Statement type:
File details
Details for the file notata-0.2.0-py3-none-any.whl.
File metadata
- Download URL: notata-0.2.0-py3-none-any.whl
- Upload date:
- Size: 14.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a306e4956aa1e410c819ca8fe2d58ac1db3be25115121129e940347e23f5dc6
|
|
| MD5 |
3cca166e6df42f2306fa39a5d3fae970
|
|
| BLAKE2b-256 |
4e382677d4184787b4e85737fb49ef8d5e33c62e782bf4da4b00bdd94de87857
|
Provenance
The following attestation bundles were made for notata-0.2.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on alonfnt/notata
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
notata-0.2.0-py3-none-any.whl -
Subject digest:
1a306e4956aa1e410c819ca8fe2d58ac1db3be25115121129e940347e23f5dc6 - Sigstore transparency entry: 319115900
- Sigstore integration time:
-
Permalink:
alonfnt/notata@3d3384a9548b53a5542bbda4c4ffd86d22954465 -
Branch / Tag:
refs/tags/0.2.0 - Owner: https://github.com/alonfnt
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@3d3384a9548b53a5542bbda4c4ffd86d22954465 -
Trigger Event:
release
-
Statement type: