Skip to content

fix: write dataset.fits once, always, to image/ (drop fits_dataset knob) #608

Closed

Description

@Jammy2211

Overview

Since the 2026-07-08 aggregator fix (PyAutoGalaxy #479, PyAutoLens #574, issue #478) every imaging and interferometer search writes dataset.fits twice with identical contents: files/dataset.fits unconditionally from save_attributes via paths.save_fits, and image/dataset.fits from the plotter interface gated on visualize/plots.yaml dataset.fits_dataset. Spotted in a Euclid vis_lp output folder; it is generic library behaviour. Decision: one unconditional write, in image/, and drop the fits_dataset config knob.

Safe because autofit.aggregator.SearchOutput._outputs_by_suffix already scans both files/ and image/ for .fits, so fit.value(name="dataset") and every *Agg loader keep working, and autolens_workspace/scripts/guides/results/start_here.py already reads image/dataset.fits.

Plan

  • Move the unconditional dataset.fits write in each save_attributes (Galaxy + Lens imaging, Galaxy + Lens interferometer) from paths.save_fits (→ files/) to a direct write into paths.image_path.
  • Delete the fits_dataset-gated plotter writes in the imaging and interferometer plotters.
  • Remove the fits_dataset key and its comment from the packaged plots.yaml in PyAutoGalaxy and PyAutoLens.
  • Update the save_attributes regression tests to assert the image/ path; drop the plotter tests' dataset.fits assertions.
  • Workspace follow-up (library-first gate): remove fits_dataset from autolens_workspace / autogalaxy_workspace config/visualize/plots.yaml and any prose mentioning it; regenerate affected notebooks.
  • No PyAutoFit change (repo is claimed by two other tasks). Known trade-off: DatabasePaths.save_fits no longer receives dataset — the pre-July behaviour, not a new gap; an image-folder-aware save_fits in PyAutoFit can follow if the DB route ever needs it.
Detailed implementation plan

Affected Repositories

  • PyAutoGalaxy (primary)
  • PyAutoLens
  • autolens_workspace (follow-up)
  • autogalaxy_workspace (follow-up)

Branch Survey

Repository Current Branch Dirty?
./PyAutoGalaxy main clean
./PyAutoLens main clean
./autolens_workspace main clean
./autogalaxy_workspace main clean
./PyAutoFit main clean (claimed by two other tasks; deliberately not touched)

Suggested branch: feature/dataset-fits-image-only

Implementation Steps

  1. PyAutoGalaxy autogalaxy/imaging/model/analysis.py save_attributes: keep building the HDU list via hdu_list_for_output_from, replace paths.save_fits(name="dataset", ...) with hdu_list.writeto(paths.image_path / "dataset.fits", overwrite=True); rewrite the comment (files→image; no mention of a gated plotter write).
  2. PyAutoGalaxy autogalaxy/interferometer/model/analysis.py save_attributes: same.
  3. PyAutoGalaxy autogalaxy/imaging/model/plotter.py imaging(): delete the should_plot("fits_dataset") block; drop the hdu_list_for_output_from import if unused. Same in autogalaxy/interferometer/model/plotter.py. Leave autogalaxy/ellipse/model/plotter.py (already a single unconditional image/ write).
  4. PyAutoGalaxy autogalaxy/config/visualize/plots.yaml: remove dataset.fits_dataset and its comment line; keep fits_adapt_images.
  5. PyAutoGalaxy tests: test_autogalaxy/analysis/analysis/test_analysis.py::test__save_attributes__dataset_fits_output_for_aggregator asserts paths.image_path / "dataset.fits"; test_autogalaxy/imaging/model/test_plotter_imaging.py::test__imaging and test_autogalaxy/interferometer/model/test_plotter_interferometer.py drop the dataset.fits read; add an interferometer save_attributes test mirroring the imaging one if none exists.
  6. PyAutoLens: same rewrite in autolens/imaging/model/analysis.py and autolens/interferometer/model/analysis.py save_attributes; remove fits_dataset from autolens/config/visualize/plots.yaml; test_autolens/analysis/analysis/test_analysis_dataset.py::test__save_attributes__dataset_fits_output_for_aggregator asserts the image/ path; grep autolens/ for any lens-side fits_dataset plotter write or test_plotter_* asserting dataset.fits and treat the same way.
  7. Workspaces (after the library PRs): remove fits_dataset key + comment from autolens_workspace/config/visualize/plots.yaml and autogalaxy_workspace/config/visualize/plots.yaml; grep scripts/ for fits_dataset prose and update; regenerate affected notebooks per each workspace's convention.

Key Files

  • PyAutoGalaxy/autogalaxy/imaging/model/analysis.py — save_attributes writer
  • PyAutoGalaxy/autogalaxy/interferometer/model/analysis.py — save_attributes writer
  • PyAutoGalaxy/autogalaxy/imaging/model/plotter.py, .../interferometer/model/plotter.py — gated plotter writes to delete
  • PyAutoGalaxy/autogalaxy/config/visualize/plots.yaml, PyAutoLens/autolens/config/visualize/plots.yaml — fits_dataset key
  • PyAutoLens/autolens/imaging/model/analysis.py, .../interferometer/model/analysis.py — save_attributes writers
  • PyAutoFit/autofit/aggregator/search_output.py — _outputs_by_suffix (read-only: confirms image/ is scanned)

Verification

  • Unit tests above green in both library worktrees.
  • End-to-end: run autolens_workspace/scripts/imaging/modeling/start_here.py under the smoke profile from the worktree; the search output has exactly one dataset.fits, under image/, none under files/.
  • Aggregator round-trip: af.Aggregator.from_directory(...) on that output, then al.agg.ImagingAgg(agg).dataset_gen_from() yields the dataset.

Original Prompt

Click to expand starting prompt

dataset.fits written twice per search (files/ and image/): write once, always, to image/

Type: bug
Target: autogalaxy
Repos:

  • PyAutoGalaxy
  • PyAutoLens
  • autolens_workspace
  • autogalaxy_workspace
    Themes:
  • output
  • aggregator
  • hygiene
    Difficulty: small
    Autonomy: safe
    Priority: medium
    Status: draft
    Consequence: judge
    Review-minutes: 15
    Unattended: ready
    Filed: 2026-09-08

Since PyAutoGalaxy#479 / PyAutoLens#574 (2026-07-08, fix for #478) every imaging and
interferometer search writes dataset.fits twice: AnalysisImaging.save_attributes /
AnalysisInterferometer.save_attributes write files/dataset.fits unconditionally via
paths.save_fits, and the plotter interface writes an identical image/dataset.fits
gated on visualize/plots.yaml dataset.fits_dataset. Spotted in a
euclid_strong_lens_modeling_pipeline vis_lp output folder; it is generic library
behaviour, not a pipeline one.

User request (verbatim): "I do not want duplication and I want dataset.fits to always be in
image, so I guess the unconditional writing should just put it there? To be honest it sounds
like we pretty much dont allow a user to not output the dataset.fits, which is fine but yeah
avoid duplication."

Facts

  • autofit.aggregator.SearchOutput._outputs_by_suffix already scans both files/ and
    image/ for .fits, so fit.value(name="dataset") resolves image/dataset.fits with
    no aggregator change.
  • autolens_workspace/scripts/guides/results/start_here.py already documents and reads
    image/dataset.fits — image/ is the user-facing location.
  • The ellipse plotter writes image/dataset.fits unconditionally and has no files/
    twin; it is not duplicated today.
  • DatabasePaths.save_fits stores fits in the database. Writing directly to
    paths.image_path (as the plotters already do) means the database route no longer
    receives dataset — which is the pre-July behaviour, not a new gap.

Deliverable

  • Single unconditional image/dataset.fits write in save_attributes (Galaxy + Lens
    imaging; Lens interferometer; Galaxy interferometer if it has the same pair).
  • Remove the fits_dataset-gated plotter writes and the fits_dataset key from the
    packaged plots.yaml (PyAutoGalaxy, PyAutoLens) and the workspace copies.
  • Update the two save_attributes regression tests to assert image/dataset.fits, and
    the plotter tests that assert the gated write.

Activity

  1. Jammy2211 commented on Sep 8, 2026

    @Jammy2211
    CollaboratorAuthor

    Plan addendum (human, 2026-09-08): since dataset.fits is now always written by AnalysisImaging / AnalysisInterferometer, save_attributes should skip the write when image/dataset.fits already exists (e.g. a resumed search), to save the time of rewriting it. Implement as an existence check before writeto; the regression tests should cover both the fresh write and the skip.

  2. Jammy2211 commented on Sep 8, 2026

    @Jammy2211
    CollaboratorAuthor

    Library PR Created

    Both pending-release. Tests: PyAutoGalaxy 1182 passed; PyAutoLens 624 passed, 1 xfailed. E2E: a real fit writes exactly one dataset.fits, under image/, none under files/; af.Aggregator.from_directory + ImagingAgg(...).dataset_gen_from() round-trips it.

    API-change summary: visualize/plots.yaml dataset.fits_dataset is removed (the file is always written, once, to image/); dataset.fits is no longer written to files/. The aggregator needs no change (it already scans image/).

    Workspace impact: option (ii) parity — the key is dead, not breaking. Copies live in 9 downstream repos. This task removes it from autolens_workspace, autogalaxy_workspace, autolens_workspace_test (5 files, 2 CRLF) and autogalaxy_workspace_test (4 files). The other five copies (euclid pipeline, both assistants, HowToLens, HowToGalaxy) are filed as a low-priority Mind hygiene draft. Next: /start_workspace → /ship_workspace.

  3. Jammy2211 commented on Sep 8, 2026

    @Jammy2211
    CollaboratorAuthor

    Smoke test results (workspace ship)

    Run with the task worktree stack active (source $WT/activate.sh) via
    pyauto-heart smoke autogalaxy autolens autolens_test, after the config-only
    workspace commits were pushed on feature/dataset-fits-image-only.

    Workspace Passed Failed Total
    autogalaxy_workspace 16 0 16
    autolens_workspace 39 0 39
    autolens_workspace_test 29 0 29
    Total 84 0 84

    Counts are scripts + notebooks: autogalaxy 14 scripts + 2 notebooks, autolens
    37 scripts + 2 notebooks, autolens_test 29 scripts (no notebook leg).
    autogalaxy_workspace_test has no Heart smoke key, so it is not covered here;
    its change is the same config-only edit.

    No failures. Notably the two scripts named in the current Heart YELLOW
    reasons both passed on this branch:

    • autolens_workspace/scripts/multi_dataset/modeling.py — PASS (16.4s)
    • autolens_workspace_test/scripts/imaging/jax_likelihood/delaunay.py — PASS (41.0s)

    Every script that reads config/visualize/plots.yaml ran clean with the
    dataset.fits_dataset key removed, confirming nothing in the workspaces still
    depends on it.

    Failure tracebacks

    None — the run exited 0 with zero failures across all three workspaces.

  4. Jammy2211 commented on Sep 8, 2026

    @Jammy2211
    CollaboratorAuthor

    /prm: five PRs green on every leg; autogalaxy_workspace_test#120 red on both smoke legs — scripts/imaging/visualization/visualization.py asserts PlotterImaging.imaging() writes dataset.fits (run 34258035876, FileNotFoundError: scripts/imaging/images/visualization/dataset.fits). Workspace CI clones the library chain on the same-named branch, so it saw the new plotter. The autolens_workspace_test imaging + interferometer twins assert the same and are only green because they are disabled in that repo's smoke list. Missed by the impact grep (fits_dataset, not dataset.fits). Fixing all three on the task branches; nothing merged yet.

  5. Jammy2211 commented on Sep 8, 2026

    @Jammy2211
    CollaboratorAuthor

    Shipped

    dataset.fits is now written once per search, always, to the search's image/ folder by AnalysisImaging.save_attributes / AnalysisInterferometer.save_attributes (skipped when it already exists, so a resumed search does not rewrite it). The fits_dataset-gated plotter writes and the fits_dataset key in config/visualize/plots.yaml are removed. The aggregator is unchanged (it already scans image/).

    Merged:

    Verification: PyAutoGalaxy 1182 passed, PyAutoLens 624 passed (1 xfailed); E2E fit → exactly one dataset.fits under image/, aggregator round-trip OK; pyauto-heart smoke autogalaxy autolens autolens_test 84/84; all PR CI legs green.

    Not done here: five more plots.yaml copies (euclid pipeline, both assistants, HowToLens, HowToGalaxy) still carry the dead key — harmless, filed as a Mind hygiene draft. Known trade-off: the database route no longer receives dataset from save_attributes (pre-#479 behaviour).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions