Project persistence

The asymmetry.core.project module provides versioned JSON project files (.asymp) that capture the full GUI session — loaded datasets, panel settings, fit configuration, and Fourier settings — so you can close the application and resume exactly where you left off.

Raw data arrays are not embedded in the project file; instead, source file paths are stored and the files are reloaded on open. This keeps project files small and avoids duplicating data.

The schema version embedded in every .asymp file is independent of the package version, so project files survive package upgrades as long as the schema version is supported.

Public API

asymmetry.core.project.load_project(path: str | Path) dict[source]

Load, migrate, and validate a project file.

Parameters:

path (str or Path) – Path to the .asymp project file.

Returns:

Validated project state dict at the current schema version.

Return type:

dict

Raises:
asymmetry.core.project.save_project(state: dict, path: str | Path) None[source]

Write a project state dict to a JSON file.

Parameters:
  • state (dict) – Project state as returned by MainWindow.collect_project_state().

  • path (str or Path) – Destination .asymp file path.

asymmetry.core.project.CURRENT_SCHEMA_VERSION = 17

int([x]) -> integer int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.__int__(). For floating-point numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by ‘+’ or ‘-’ and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal. >>> int(‘0b100’, base=0) 4

The integer schema version written into every new project file. Currently 7.

exception asymmetry.core.project.UnsupportedSchemaVersion[source]

Raised when a project file uses an unsupported schema version.

Schema internals

These functions are used internally by load_project() and save_project() but may be useful for testing or migration scripts.

asymmetry.core.project.schema.validate(data: dict) None[source]

Validate that required top-level keys are present.

Parameters:

data (dict) – Project dict at the current schema version.

Raises:

ValueError – If required keys are missing.

asymmetry.core.project.schema.migrate_to_current(data: dict) dict[source]

Migrate a raw project dict to the current schema version.

Future schemas will chain _migrate_v1_to_v2, etc.

Parameters:

data (dict) – Raw JSON dict loaded from a project file.

Returns:

Data at the current schema version.

Return type:

dict

Raises:

UnsupportedSchemaVersion – If the schema version is not in the supported window.

Project file format

A project file is a UTF-8 JSON document with the following top-level structure:

{
    "schema_version": 7,
    "created_with_app_version": "0.1.0",
    "datasets": [
        {
            "source_file": "/absolute/path/to/file.nxs",
            "run_number": 1234,
            "metadata_overrides": { "field": 50.0 },
            "representations": {
                "time_fb_asymmetry": {
                    "recipe": {},
                    "fit": {
                        "model": { "component_names": ["Exponential"], "operators": [] },
                        "parameters": [...],
                        "result": { "success": true, "reduced_chi_squared": 0.97, ... },
                        "provenance": "single",
                        "batch_id": null,
                        "diverged": false,
                        "include_in_trend": true
                    },
                    "trend_state": {}
                },
                "freq_fft": {
                    "recipe": { "fourier_config": { "window": "gaussian", ... } },
                    "fit": { "model": null, "provenance": "none", ... },
                    "trend_state": {}
                }
            }
        }
    ],
    "combined_datasets": [
        { "source_run_numbers": [1234, 1235] }
    ],
    "batches": [
        {
            "batch_id": "batch-1",
            "rep_type": "time_fb_asymmetry",
            "member_kind": "runs",
            "member_run_numbers": [1234, 1235, 1236],
            "member_source_run": {},
            "order_key": "field",
            "canonical_model": { "component_names": ["Exponential"], "operators": [] },
            "param_roles": { "A": "local", "Lambda": "global" },
            "nuisance_params": [],
            "results_by_run": {
                "1234": { "success": true, "parameters": { "Lambda": 0.31 }, ... }
            },
            "diverged_runs": []
        }
    ],
    "browser_state": {
        "sort_column": 0,
        "sort_order": "ascending",
        "filters": {},
        "selected_run_numbers": [1234],
        "selected_group_ids": [],
        "data_groups": [],
        "extra_columns": []
    },
    "plot_state": { ... },
    "single_fit_state": {
        "model_name": "ExponentialRelaxation",
        "parameters": [...],
        "result_html": "<b>χ² = ...</b>"
    },
    "global_fit_state": {
        "model_name": "ExponentialRelaxation",
        "parameters": [...],
        "result_html": "<b>χ² = ...</b>"
    },
    "fit_ui_state": { "active_tab_index": 0 },
    "fit_parameters_state": { "rows": [...], "x_axis": "Auto", ... },
    "fourier_state": {
        "window": "gaussian",
        "filter_start_us": 0.0,
        "filter_time_constant_us": 1.5,
        "padding": 4,
        "display": "Power"
    }
}

Schema versioning history:

  • v1–v4 — initial releases

  • v5 — grouping overrides, wizard cache

  • v6 — per-dataset representations map (recipe-only FFT, per-run FitSlot, trend state); top-level batches list

  • v7 — FitSeries gains member_kind, nuisance_params, member_source_run; trend_state normalised to structured shape; group series recorded in batches

Unknown top-level keys are silently preserved during migration, ensuring forward compatibility when a newer version of the application adds new state.

Representation

Each dataset entry’s representations map holds one entry per analysis view the user has exercised. Keys are RepresentationType values: "time_fb_asymmetry", "time_groups", "freq_fft", "freq_maxent".

Each representation stores:

recipe

Generation parameters (for FFT spectra: the GroupSpectrumConfig serialisation; for time-domain views: empty). On project load the transient arrays are recomputed from the recipe rather than stored.

fit

A single FitSlot — the most recent fit for this (dataset, representation) pair. Includes the model dict, fitted-parameter list, result summary, provenance marker ("none", "single", "batch", "global"), the batch_id of the owning FitSeries when the fit was part of a series, and divergence flags used by the trending panel.

trend_state

Opaque dict persisting the user’s x-axis and y-parameter selections in the Fit Parameters panel for this representation.

FitSeries (batches)

The top-level batches list stores FitSeries objects that accumulate results across multiple members.

Key fields:

member_kind

"runs" for F-B asymmetry / FFT batch fits; "groups" for multi-run grouped time-domain fits.

member_run_numbers

Ordered list of member keys. For "runs" series these are real run numbers; for "groups" series they are synthetic negative keys of the form -(source_run * 1000 + group_index).

member_source_run

Map from synthetic group key → source run number ("groups" series only).

param_roles

Per-parameter classification: "global", "local", or "fixed".

nuisance_params

List of per-(run, group) nuisance parameter names excluded from trending ("groups" series only).

results_by_run

Per-member fit result summaries (parameter values, uncertainties, χ²) used to drive the Fit Parameters trending panel.