Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# 137. CLI `fit` Command Never Saves Results to Disk

Closed on re-verification — **not a defect**. `Analysis.fit()`
auto-saves via `self.project.save()` in `_run_single` / `_run_joint` /
`_run_sequential` (`analysis.py:2661,2688,2727`) whenever the project
path is set. The CLI `fit` command's non-`--dry` path leaves
`project.info.path` set, so results persist; `--dry` nulls the path to
suppress saving (asserted by `test_cli_fit_dry_clears_path`). The
original audit finding read the CLI in isolation and missed `fit()`'s
internal save; no code change is needed.
Closed after fixing the CLI persistence boundary. `Analysis.fit()`
updates fit state without saving the project. The CLI `fit` command now
calls `project.save()` explicitly after a successful non-`--dry` fit, so
updated results still persist. With `--dry`, the project path is cleared
for the fit and the explicit save is skipped, preventing project files
and fit sidecars from being overwritten.
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,16 @@ gap. Two ways to fix it:
(the loader only needs Plotly once it renders), then calls the same
`renderSpec` entry point. `google.colab.output.pauseOutputUntil` holds
the output frame until the figure — or a visible error message — is on
screen. Concurrent outputs in one cell share frame-global loading
screen. Colab pauses outputframe auto-resizing (and the cell's later
outputs) while that promise is pending, so the bootstrap asks for a
remeasure just after it resolves; otherwise the height taken before
the plot replaced its placeholder stays as blank space below the
chart. That request is scheduled on the next paint with a timer
backstop, because a hidden browser tab never paints. Where that API is
absent, the fallback measures Colab's output area — every output of
the cell, so siblings are not clipped — rather than the root scroll
height, which is clamped to the frame viewport and so can only grow
it. Concurrent outputs in one cell share frame-global loading
promises, so each asset is fetched and parsed only once. An immutable,
exact-release-tag jsDelivr URL is the fallback while a new
documentation version is deploying or if that deployment fails. Dev
Expand Down
20 changes: 20 additions & 0 deletions docs/docs/tutorials/bayesian-dream-lbco-hrpt.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,26 @@
"source": [
"project.display.posterior.predictive(expt_name='hrpt', x_min=92, x_max=93)"
]
},
{
"cell_type": "markdown",
"id": "63",
"metadata": {},
"source": [
"## 💾 Save Project\n",
"\n",
"Save the fitted parameters and analysis results."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "64",
"metadata": {},
"outputs": [],
"source": [
"project.save()"
]
}
],
"metadata": {
Expand Down
8 changes: 8 additions & 0 deletions docs/docs/tutorials/bayesian-dream-lbco-hrpt.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,3 +362,11 @@

# %%
project.display.posterior.predictive(expt_name='hrpt', x_min=92, x_max=93)

# %% [markdown]
# ## 💾 Save Project
#
# Save the fitted parameters and analysis results.

# %%
project.save()
20 changes: 20 additions & 0 deletions docs/docs/tutorials/bayesian-emcee-lbco-hrpt.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,26 @@
"source": [
"project.display.posterior.predictive(expt_name='hrpt', x_min=92, x_max=93)"
]
},
{
"cell_type": "markdown",
"id": "62",
"metadata": {},
"source": [
"## 💾 Save Project\n",
"\n",
"Save the fitted parameters and analysis results."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "63",
"metadata": {},
"outputs": [],
"source": [
"project.save()"
]
}
],
"metadata": {
Expand Down
8 changes: 8 additions & 0 deletions docs/docs/tutorials/bayesian-emcee-lbco-hrpt.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,3 +355,11 @@

# %%
project.display.posterior.predictive(expt_name='hrpt', x_min=92, x_max=93)

# %% [markdown]
# ## 💾 Save Project
#
# Save the fitted parameters and analysis results.

# %%
project.save()
20 changes: 20 additions & 0 deletions docs/docs/tutorials/bayesian-emcee-tbti-heidi.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,26 @@
"source": [
"project.display.posterior.predictive(expt_name='heidi')"
]
},
{
"cell_type": "markdown",
"id": "53",
"metadata": {},
"source": [
"## 💾 Save Project\n",
"\n",
"Save the fitted parameters and analysis results."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "54",
"metadata": {},
"outputs": [],
"source": [
"project.save()"
]
}
],
"metadata": {
Expand Down
8 changes: 8 additions & 0 deletions docs/docs/tutorials/bayesian-emcee-tbti-heidi.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,11 @@

# %%
project.display.posterior.predictive(expt_name='heidi')

# %% [markdown]
# ## 💾 Save Project
#
# Save the fitted parameters and analysis results.

# %%
project.save()
20 changes: 20 additions & 0 deletions docs/docs/tutorials/calibrate-beer-ess.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,26 @@
" x_max=2.13,\n",
")"
]
},
{
"cell_type": "markdown",
"id": "67",
"metadata": {},
"source": [
"## 💾 Save Project\n",
"\n",
"Save the fitted parameters and analysis results."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "68",
"metadata": {},
"outputs": [],
"source": [
"project.save()"
]
}
],
"metadata": {
Expand Down
8 changes: 8 additions & 0 deletions docs/docs/tutorials/calibrate-beer-ess.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,3 +350,11 @@
x_min=2.08,
x_max=2.13,
)

# %% [markdown]
# ## 💾 Save Project
#
# Save the fitted parameters and analysis results.

# %%
project.save()
20 changes: 20 additions & 0 deletions docs/docs/tutorials/refine-cosio-d20-tscan.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,26 @@
"display.fit.series(struct.atom_sites['O2'].fract_z, versus=temperature)\n",
"display.fit.series(struct.atom_sites['O3'].fract_z, versus=temperature)"
]
},
{
"cell_type": "markdown",
"id": "75",
"metadata": {},
"source": [
"## 💾 Save Project\n",
"\n",
"Save the fitted parameters and analysis results."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "76",
"metadata": {},
"outputs": [],
"source": [
"project.save()"
]
}
],
"metadata": {
Expand Down
8 changes: 8 additions & 0 deletions docs/docs/tutorials/refine-cosio-d20-tscan.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,3 +404,11 @@
display.fit.series(struct.atom_sites['O1'].fract_z, versus=temperature)
display.fit.series(struct.atom_sites['O2'].fract_z, versus=temperature)
display.fit.series(struct.atom_sites['O3'].fract_z, versus=temperature)

# %% [markdown]
# ## 💾 Save Project
#
# Save the fitted parameters and analysis results.

# %%
project.save()
20 changes: 20 additions & 0 deletions docs/docs/tutorials/refine-cosio-d20.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,26 @@
"The HTML report is written automatically when the project is saved;\n",
"enable `project.report.pdf` as well for a PDF version."
]
},
{
"cell_type": "markdown",
"id": "57",
"metadata": {},
"source": [
"## 💾 Save Project\n",
"\n",
"Save the fitted parameters and analysis results."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "58",
"metadata": {},
"outputs": [],
"source": [
"project.save()"
]
}
],
"metadata": {
Expand Down
8 changes: 8 additions & 0 deletions docs/docs/tutorials/refine-cosio-d20.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,3 +299,11 @@
#
# The HTML report is written automatically when the project is saved;
# enable `project.report.pdf` as well for a PDF version.

# %% [markdown]
# ## 💾 Save Project
#
# Save the fitted parameters and analysis results.

# %%
project.save()
2 changes: 2 additions & 0 deletions docs/docs/user-guide/analysis-workflow/analysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,12 +312,14 @@ Select a Bayesian minimizer through the same category-owned selector:
project.analysis.minimizer.type = 'emcee'
project.save_as(dir_path='lbco_hrpt') # required before an emcee fit
project.analysis.fit()
project.save()
```

For a saved `emcee` project, resume the chain with extra steps:

```python
project.analysis.fit(resume=True, extra_steps=500)
project.save()
```

After a Bayesian fit, inspect posterior summaries and plots:
Expand Down
2 changes: 2 additions & 0 deletions src/easydiffraction/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,8 @@ def fit(
if dry:
project.metadata._path = None
project.analysis.fit()
if not dry:
project.save()
_display_fit_outputs(project)


Expand Down
9 changes: 0 additions & 9 deletions src/easydiffraction/analysis/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -2860,9 +2860,6 @@ def _run_single(
)
self._stamp_software_provenance()

if self.project.metadata.path is not None:
self.project.save()

def _run_joint(
self,
*,
Expand All @@ -2887,9 +2884,6 @@ def _run_joint(
)
self._stamp_software_provenance()

if self.project.metadata.path is not None:
self.project.save()

def _run_sequential(self) -> None:
"""
Execute sequential fitting from persisted sequential settings.
Expand Down Expand Up @@ -2926,9 +2920,6 @@ def _run_sequential(self) -> None:

self._stamp_software_provenance()

if self.project.metadata.path is not None:
self.project.save()

def _fit_joint(
self,
verb: VerbosityEnum,
Expand Down
3 changes: 2 additions & 1 deletion src/easydiffraction/core/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@


def validate_datablock_name(value: str, *, kind: str) -> str:
"""Return a datablock name that survives backend normalization.
"""
Return a datablock name that survives backend normalization.

Calculator backends such as Cryspy normalize CIF datablock names to
lowercase. Reject uppercase names at the public model boundary so
Expand Down
2 changes: 1 addition & 1 deletion src/easydiffraction/datablocks/experiment/item/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@ def _get_valid_linked_structures(
A list of valid linked structures.
"""
if not self.linked_structures:
log.warning('No linked structures defined. Returning empty pattern.')
# log.warning('No linked structures defined.')
return []

valid_linked_structures = []
Expand Down
Loading