Fit Multiple Files with Shared Parameters#

This notebook fits multiple datasets simultaneously, sharing some parameters across all files while fitting others independently per file.

Example System:

  • 6 datasets with identical spectral model (GLP + linear background)

  • Different shift amplitudes across files (varying pump fluence)

  • Shared time dependence: the peak position shifts at t = 0 and relaxes back exponentially (blurred by the IRF)

Key Concept: Parameters are classified by vary level in the YAML model:

  • "project": shared across all files (fitted once)

  • "file": independent per file (fitted separately for each)

  • "static": fixed (not fitted); energy-model parameters are set from each file’s baseline fit

Workflow:

  1. Load all datasets into a single Project

  2. Fit baseline for each file

  3. Load 2D models with project/file/static vary levels and
    run project.fit_2d() for one joint fit over all files

  4. Compare fit results to known truth used to create synthetic data

from pathlib import Path

import numpy as np
import pandas as pd
from ruamel.yaml import YAML

import trspecfit

1. Load Data#

Create a Project and load all 6 datasets. Each file shares the same energy and time axes but has a different shift amplitude (expFun A).

project = trspecfit.Project(path=Path.cwd())

data_folder = "data"
energy = np.loadtxt(project.path / data_folder / "energy.csv")
time = np.loadtxt(project.path / data_folder / "time.csv")

shift_amplitudes = [5, 2, 1, 0.5, 0.2, 0.1]
files = []

for i, amp in enumerate(shift_amplitudes, start=1):
    file_data = np.loadtxt(project.path / data_folder / f'data_{i}.csv', delimiter=",")
    f = trspecfit.File(
        parent_project=project,
        path=f'data_{i}',
        data=file_data,
        energy=energy,
        time=time,
    )
    files.append(f)
    print(f"{f.name}: expFun A = {amp}, shape {file_data.shape}")

# detail=1 also lists the attached files and plots each 2D data grid
project.describe(detail=1)
data_1: expFun A = 5, shape (98, 200)
data_2: expFun A = 2, shape (98, 200)
data_3: expFun A = 1, shape (98, 200)
data_4: expFun A = 0.5, shape (98, 200)
data_5: expFun A = 0.2, shape (98, 200)
data_6: expFun A = 0.1, shape (98, 200)
Project
  path:         <repo>/examples/fitting_workflows/21_multi_file_shared_fit
  name:         my_project
  config:       <repo>/examples/fitting_workflows/21_multi_file_shared_fit/project.yaml

  Files (6):
    data_1: 2D (98, 200), energy [0.00, 19.90], time [-20.00, 99.00], z [0, 19.62]
    data_2: 2D (98, 200), energy [0.00, 19.90], time [-20.00, 99.00], z [0, 18.18]
    data_3: 2D (98, 200), energy [0.00, 19.90], time [-20.00, 99.00], z [0, 18.18]
    data_4: 2D (98, 200), energy [0.00, 19.90], time [-20.00, 99.00], z [0, 18.36]
    data_5: 2D (98, 200), energy [0.00, 19.90], time [-20.00, 99.00], z [0, 18]
    data_6: 2D (98, 200), energy [0.00, 19.90], time [-20.00, 99.00], z [0, 19.44]
../../../_images/9b5e3159677b55b28fbcfe2be1e0cb20e1975d5853d9462c35d4eac95c874576.png

2. Fit Baseline Spectra#

2.1. Define Fitting Region and Baseline#

Set energy and time limits, then define the baseline (ground state) spectrum for all files at once.

# Set fitting limits (same for all files)
project.set_fit_limits(
    energy_limits=[5, 18],
    time_limits=[-20, 99],
)

# time_type='ind': indices, not times. Slices 0-22 are t = -20 ... -9, which ends
# three IRF widths (SD = 3) before t = 0 so the onset cannot bleed into the baseline
project.define_baselines(time_start=0, time_stop=22, time_type="ind")
Fit limits set for 6 files: energy [5, 18], time [-20, 99]
Baseline defined for 6 files: time (index) [0, 22]

2.2. Fit the Baseline Model on Every File#

Load a baseline model and fit it on every file. The spectral shape is identical across files (only the shift amplitude differs), so the fitted line-shape parameters should come out alike, and the table below checks them against the truth the data was generated with. Most do; the Lorentzian fraction GLP_01_m is the exception. It only shows in the peak’s tails and a single spectrum barely constrains it, so it scatters widely around its truth.

# Load baseline model onto all files
project.load_models(model_yaml="models_energy.yaml", model_info="base")

# Fit baselines: one independent fit per file
project.fit_baselines(
    model_name="base",
    stages=2,  # simplex pass, then leastsq refinement
    try_ci=0,  # skip confidence intervals: see 12_uncertainty_mcmc
)
Model 'base' loaded for 6 files
Baseline fit complete for 6 files: 01.109(ss.ms)
../../../_images/44d60fdf6a42c5c050c844fac67b91872a2982dc124dd90cbff5034b7b95f9e3.png ../../../_images/ad733f45f8cb8142922c8d6de0eb5ab48c6c5eb5b4f7c94eea4fc8e8254b198f.png ../../../_images/7e37ad0ca461f8c81bc5f372895d434bfdfedc4b60f906b8b5fb4596b3d48cc7.png ../../../_images/11213c0269cce07041703f401772eea76f959e08a0b312247c11fe5dd90cc9e9.png ../../../_images/f6d08c48e5282a4efca208719e913fef341a83390ccb7cbffe4c23084bad7c10.png ../../../_images/56e28e853835e7d639162fc3f717ed3dfba9d209b7026b4e6b3f2f81343694da.png
# per-file baseline line shape next to the truth the data was generated with
truth_yaml = YAML(typ="safe").load(project.path / data_folder / "models_energy_truth.yaml")["single_peak"]
truth_base = {
    "LinBack_m": truth_yaml["LinBack"]["m"][0],
    "LinBack_b": truth_yaml["LinBack"]["b"][0],
    "GLP_01_A": truth_yaml["GLP"]["A"][0],
    "GLP_01_x0": truth_yaml["GLP"]["x0"][0],
    "GLP_01_F": truth_yaml["GLP"]["F"][0],
    "GLP_01_m": truth_yaml["GLP"]["m"][0],
}
rows = []
for f in files:
    vals = f.get_parameters(fit_type="baseline").set_index("name")["value"]
    rows.append({"file": f.name, **{k: vals[k] for k in truth_base}})
rows.append({"file": "truth", **truth_base})
pd.DataFrame(rows).set_index("file").round(3)
LinBack_m LinBack_b GLP_01_A GLP_01_x0 GLP_01_F GLP_01_m
file
data_1 0.400 0.009 9.794 10.005 1.000 0.056
data_2 0.401 -0.019 10.007 9.991 0.995 0.286
data_3 0.403 -0.039 9.606 10.004 1.046 0.207
data_4 0.404 -0.078 9.800 9.998 1.016 0.317
data_5 0.400 -0.010 9.768 10.007 1.016 0.305
data_6 0.399 0.039 10.353 10.000 0.967 0.434
truth 0.400 0.000 10.000 10.000 1.000 0.300

Amplitude, position, width and background slope agree across files and with the truth to within a few percent, and the offset LinBack_b stays within a hundredth of the peak height of its truth, zero. GLP_01_m scatters from 0.06 to 0.43 around its truth of 0.3, with data_1 fit as an almost pure Gaussian. That is the one line-shape parameter a single spectrum cannot pin, and the 2D fit below inherits each file’s value as a static parameter: “static” means “as fit in the baseline”, not “known”.

3. Joint 2D Fit Across Files#

Load the 2D model, add the time dependence, and fit all files jointly: one residual over every file, minimized once.

The vary levels control parameter sharing:

Energy model (models_energy.yaml):

  • every line-shape parameter is "static", pinned to its baseline value

Time model (models_time.yaml):

  • "project": tau, gaussCONV SD — shared dynamics across all files

  • "file": expFun A — independent shift amplitude per file

  • "static": expFun t0 — fixed at 0

Why share tau and SD? The IRF width is an instrument property and the decay time a sample property, common to every fluence, while the shift amplitude is what the fluence changes. Sharing them lets the strong files pin what the weak files cannot: fit on its own, a file with a 0.1 shift barely constrains tau or SD (see 20_multi_file_independent_fit).

# Load 2D model onto all files
project.load_models(model_yaml="models_energy.yaml", model_info="2D")

# Add time dependence to peak position
project.add_time_dependences(
    target_model="2D",
    target_parameter="GLP_01_x0",
    dynamics_yaml="models_time.yaml",
    dynamics_model="MonoExpPosIRF",
)

# Fit all files jointly; the returned JointFitResult carries the combined table
joint = project.fit_2d(
    model_name="2D",
    stages=2,  # simplex pass, then leastsq refinement
    try_ci=0,  # skip confidence intervals: see 12_uncertainty_mcmc
)
Model '2D' loaded for 6 files
Time dependence 'GLP_01_x0' added for 6 files
Project fit: 6 files, 2 project-vary, 6 file-vary, 54 static params (interpreter backend)

Time initialize: 1.1920928955078125e-05 s

Results global minumum fit (method=Nelder): 
[[Variables]]
    file00_LinBack_m:               0.3998635 (fixed)
    file00_LinBack_b:               0.008688754 (fixed)
    file00_LinBack_xStart:          0 (fixed)
    file00_LinBack_xStop:           20 (fixed)
    file00_GLP_01_A:                9.794339 (fixed)
    file00_GLP_01_x0:               10.00546 (fixed)
    GLP_01_x0_gaussCONV_SD:         5.00000174 +/- 0.05454086 (1.09%) (init = 5)
    file00_GLP_01_x0_expFun_01_A:   5.30659754 +/- 0.01166111 (0.22%) (init = 5)
    GLP_01_x0_expFun_01_tau:        46.7309669 +/- 0.14297245 (0.31%) (init = 10)
    file00_GLP_01_x0_expFun_01_t0:  0 (fixed)
    file00_GLP_01_F:                0.9999502 (fixed)
    file00_GLP_01_m:                0.0563767 (fixed)
    file01_LinBack_m:               0.4005746 (fixed)
    file01_LinBack_b:              -0.01868768 (fixed)
    file01_LinBack_xStart:          0 (fixed)
    file01_LinBack_xStop:           20 (fixed)
    file01_GLP_01_A:                10.00687 (fixed)
    file01_GLP_01_x0:               9.990536 (fixed)
    file01_GLP_01_x0_expFun_01_A:   2.15309596 +/- 0.00718476 (0.33%) (init = 5)
    file01_GLP_01_x0_expFun_01_t0:  0 (fixed)
    file01_GLP_01_F:                0.9946391 (fixed)
    file01_GLP_01_m:                0.2860204 (fixed)
    file02_LinBack_m:               0.4033922 (fixed)
    file02_LinBack_b:              -0.03938399 (fixed)
    file02_LinBack_xStart:          0 (fixed)
    file02_LinBack_xStop:           20 (fixed)
    file02_GLP_01_A:                9.606165 (fixed)
    file02_GLP_01_x0:               10.004 (fixed)
    file02_GLP_01_x0_expFun_01_A:   1.04905610 +/- 0.00657774 (0.63%) (init = 5)
    file02_GLP_01_x0_expFun_01_t0:  0 (fixed)
    file02_GLP_01_F:                1.045554 (fixed)
    file02_GLP_01_m:                0.2074394 (fixed)
    file03_LinBack_m:               0.4044803 (fixed)
    file03_LinBack_b:              -0.07764829 (fixed)
    file03_LinBack_xStart:          0 (fixed)
    file03_LinBack_xStop:           20 (fixed)
    file03_GLP_01_A:                9.799693 (fixed)
    file03_GLP_01_x0:               9.998345 (fixed)
    file03_GLP_01_x0_expFun_01_A:   0.53656212 +/- 0.00624687 (1.16%) (init = 5)
    file03_GLP_01_x0_expFun_01_t0:  0 (fixed)
    file03_GLP_01_F:                1.016047 (fixed)
    file03_GLP_01_m:                0.3172321 (fixed)
    file04_LinBack_m:               0.3996662 (fixed)
    file04_LinBack_b:              -0.01004322 (fixed)
    file04_LinBack_xStart:          0 (fixed)
    file04_LinBack_xStop:           20 (fixed)
    file04_GLP_01_A:                9.767538 (fixed)
    file04_GLP_01_x0:               10.00745 (fixed)
    file04_GLP_01_x0_expFun_01_A:   0.19673032 +/- 0.00623976 (3.17%) (init = 5)
    file04_GLP_01_x0_expFun_01_t0:  0 (fixed)
    file04_GLP_01_F:                1.016252 (fixed)
    file04_GLP_01_m:                0.3045525 (fixed)
    file05_LinBack_m:               0.3992315 (fixed)
    file05_LinBack_b:               0.03945658 (fixed)
    file05_LinBack_xStart:          0 (fixed)
    file05_LinBack_xStop:           20 (fixed)
    file05_GLP_01_A:                10.35277 (fixed)
    file05_GLP_01_x0:               10.00032 (fixed)
    file05_GLP_01_x0_expFun_01_A:   0.10997345 +/- 0.00604275 (5.49%) (init = 5)
    file05_GLP_01_x0_expFun_01_t0:  0 (fixed)
    file05_GLP_01_F:                0.9665539 (fixed)
    file05_GLP_01_m:                0.4341475 (fixed)
[[Correlations]] (unreported correlations are < 0.100)
    C(file00_GLP_01_x0_expFun_01_A, GLP_01_x0_expFun_01_tau)      = -0.7631
    C(GLP_01_x0_gaussCONV_SD, file00_GLP_01_x0_expFun_01_A)       = +0.5039
    C(GLP_01_x0_expFun_01_tau, file01_GLP_01_x0_expFun_01_A)      = -0.4472
    C(file00_GLP_01_x0_expFun_01_A, file01_GLP_01_x0_expFun_01_A) = +0.3856
    C(GLP_01_x0_gaussCONV_SD, GLP_01_x0_expFun_01_tau)            = -0.3761
    C(GLP_01_x0_gaussCONV_SD, file01_GLP_01_x0_expFun_01_A)       = +0.3437
    C(GLP_01_x0_expFun_01_tau, file02_GLP_01_x0_expFun_01_A)      = -0.2356
    C(file00_GLP_01_x0_expFun_01_A, file02_GLP_01_x0_expFun_01_A) = +0.1986
    C(GLP_01_x0_gaussCONV_SD, file02_GLP_01_x0_expFun_01_A)       = +0.1633
    C(GLP_01_x0_expFun_01_tau, file03_GLP_01_x0_expFun_01_A)      = -0.1269
    C(file01_GLP_01_x0_expFun_01_A, file02_GLP_01_x0_expFun_01_A) = +0.1206
    C(file00_GLP_01_x0_expFun_01_A, file03_GLP_01_x0_expFun_01_A) = +0.1082
Time fit (global minimum): 57.66439080238342 s

Results local optimization fit (method=leastsq): 
[[Variables]]
    file00_LinBack_m:               0.3998635 (fixed)
    file00_LinBack_b:               0.008688754 (fixed)
    file00_LinBack_xStart:          0 (fixed)
    file00_LinBack_xStop:           20 (fixed)
    file00_GLP_01_A:                9.794339 (fixed)
    file00_GLP_01_x0:               10.00546 (fixed)
    GLP_01_x0_gaussCONV_SD:         2.98834297 +/- 0.01455372 (0.49%) (init = 5)
    file00_GLP_01_x0_expFun_01_A:   4.97589760 +/- 0.00774839 (0.16%) (init = 5)
    GLP_01_x0_expFun_01_tau:        50.0788670 +/- 0.12928627 (0.26%) (init = 10)
    file00_GLP_01_x0_expFun_01_t0:  0 (fixed)
    file00_GLP_01_F:                0.9999502 (fixed)
    file00_GLP_01_m:                0.0563767 (fixed)
    file01_LinBack_m:               0.4005746 (fixed)
    file01_LinBack_b:              -0.01868768 (fixed)
    file01_LinBack_xStart:          0 (fixed)
    file01_LinBack_xStop:           20 (fixed)
    file01_GLP_01_A:                10.00687 (fixed)
    file01_GLP_01_x0:               9.990536 (fixed)
    file01_GLP_01_x0_expFun_01_A:   2.01684523 +/- 0.00574378 (0.28%) (init = 5)
    file01_GLP_01_x0_expFun_01_t0:  0 (fixed)
    file01_GLP_01_F:                0.9946391 (fixed)
    file01_GLP_01_m:                0.2860204 (fixed)
    file02_LinBack_m:               0.4033922 (fixed)
    file02_LinBack_b:              -0.03938399 (fixed)
    file02_LinBack_xStart:          0 (fixed)
    file02_LinBack_xStop:           20 (fixed)
    file02_GLP_01_A:                9.606165 (fixed)
    file02_GLP_01_x0:               10.004 (fixed)
    file02_GLP_01_x0_expFun_01_A:   0.98749596 +/- 0.00572794 (0.58%) (init = 5)
    file02_GLP_01_x0_expFun_01_t0:  0 (fixed)
    file02_GLP_01_F:                1.045554 (fixed)
    file02_GLP_01_m:                0.2074394 (fixed)
    file03_LinBack_m:               0.4044803 (fixed)
    file03_LinBack_b:              -0.07764829 (fixed)
    file03_LinBack_xStart:          0 (fixed)
    file03_LinBack_xStop:           20 (fixed)
    file03_GLP_01_A:                9.799693 (fixed)
    file03_GLP_01_x0:               9.998345 (fixed)
    file03_GLP_01_x0_expFun_01_A:   0.50410441 +/- 0.00549338 (1.09%) (init = 5)
    file03_GLP_01_x0_expFun_01_t0:  0 (fixed)
    file03_GLP_01_F:                1.016047 (fixed)
    file03_GLP_01_m:                0.3172321 (fixed)
    file04_LinBack_m:               0.3996662 (fixed)
    file04_LinBack_b:              -0.01004322 (fixed)
    file04_LinBack_xStart:          0 (fixed)
    file04_LinBack_xStop:           20 (fixed)
    file04_GLP_01_A:                9.767538 (fixed)
    file04_GLP_01_x0:               10.00745 (fixed)
    file04_GLP_01_x0_expFun_01_A:   0.18568026 +/- 0.00548172 (2.95%) (init = 5)
    file04_GLP_01_x0_expFun_01_t0:  0 (fixed)
    file04_GLP_01_F:                1.016252 (fixed)
    file04_GLP_01_m:                0.3045525 (fixed)
    file05_LinBack_m:               0.3992315 (fixed)
    file05_LinBack_b:               0.03945658 (fixed)
    file05_LinBack_xStart:          0 (fixed)
    file05_LinBack_xStop:           20 (fixed)
    file05_GLP_01_A:                10.35277 (fixed)
    file05_GLP_01_x0:               10.00032 (fixed)
    file05_GLP_01_x0_expFun_01_A:   0.10332184 +/- 0.00510252 (4.94%) (init = 5)
    file05_GLP_01_x0_expFun_01_t0:  0 (fixed)
    file05_GLP_01_F:                0.9665539 (fixed)
    file05_GLP_01_m:                0.4341475 (fixed)
[[Correlations]] (unreported correlations are < 0.100)
    C(file00_GLP_01_x0_expFun_01_A, GLP_01_x0_expFun_01_tau)      = -0.6763
    C(GLP_01_x0_gaussCONV_SD, file00_GLP_01_x0_expFun_01_A)       = +0.4214
    C(GLP_01_x0_expFun_01_tau, file01_GLP_01_x0_expFun_01_A)      = -0.3698
    C(GLP_01_x0_gaussCONV_SD, GLP_01_x0_expFun_01_tau)            = -0.2824
    C(file00_GLP_01_x0_expFun_01_A, file01_GLP_01_x0_expFun_01_A) = +0.2816
    C(GLP_01_x0_gaussCONV_SD, file01_GLP_01_x0_expFun_01_A)       = +0.2304
    C(GLP_01_x0_expFun_01_tau, file02_GLP_01_x0_expFun_01_A)      = -0.1815
    C(file00_GLP_01_x0_expFun_01_A, file02_GLP_01_x0_expFun_01_A) = +0.1383
    C(GLP_01_x0_gaussCONV_SD, file02_GLP_01_x0_expFun_01_A)       = +0.1131
Time fit (local optimization): 1.2711539268493652 s
Time elapsed for project-level 2D fit: 59.068(ss.ms)
../../../_images/b5dc80d1f7bf77b2e03f9ff164b336c0feb164924973e88ed2407d41626044db.png ../../../_images/157b5d9850995a0fe7c8d60b209179dacaa570f7718074272621050ad08724b8.png ../../../_images/04ea9afe38bcfcadbe907782bad49d716b4afbef0e8c70b9bdf93f27311557b2.png ../../../_images/01ffd6b19bd88ed8fc0ce011858ea2d73760176613bbe5b1aa1903e864b4228e.png ../../../_images/5ff66ba7f94db0f5d9d0c615fbc63f0330e9efd9fa9f34bbdeaccdee51f7c403.png ../../../_images/a94ef26f1d8749f7fc20db3c64a6c6c3f5d3d02d201a6c26ea22f0ab6441de2c.png

The correlation block the fit printed is the method’s fingerprint. Per-file parameters carry the optimizer’s file00_…, file01_… prefix, numbered from zero in load order, so file00_ is data_1. The A of the stronger files is anti-correlated with the shared tau (a slower decay and a smaller shift describe the same early trace), and SD and tau trade off weakly.

4. Shared vs. Per-File Parameters#

Confirm the vary levels in the output. file.get_parameters(fit_type="2d") returns one row per parameter; below we collect the three fitted dynamics parameters (t0 is pinned) for every file into one table. Next to them sits the truth: shared SD = 3, tau = 50 (data/models_time_truth.yaml) and per-file A = [5, 2, 1, 0.5, 0.2, 0.1] (amplitudes in data/generate_data.ipynb).

  • SD and tau are identical down every row: fit once, shared.

  • A varies row to row and tracks the shift each file was generated with.

# vary level per parameter: "project" = shared (fit once), "file" = independent
rows = []
for f, A_truth in zip(files, shift_amplitudes):
    vals = f.get_parameters(fit_type="2d").set_index("name")["value"]
    rows.append(
        {
            "file": f.name,
            "SD (project)": vals["GLP_01_x0_gaussCONV_SD"],
            "tau (project)": vals["GLP_01_x0_expFun_01_tau"],
            "A (file)": vals["GLP_01_x0_expFun_01_A"],
            "A truth": A_truth,
        }
    )

pd.DataFrame(rows).set_index("file").round(3)
SD (project) tau (project) A (file) A truth
file
data_1 2.988 50.079 4.976 5.0
data_2 2.988 50.079 2.017 2.0
data_3 2.988 50.079 0.987 1.0
data_4 2.988 50.079 0.504 0.5
data_5 2.988 50.079 0.186 0.2
data_6 2.988 50.079 0.103 0.1

Error bars live on the joint result, not on the per-file tables: after a joint fit, file.get_parameters reports no stderr. The per-file tables are projections of the joint values only; the covariance stays with the joint record, because the shared parameters it couples are constrained by every file’s data at once. The JointFitResult that project.fit_2d returned (also reachable later as project.results.get_joint(model="2D")) carries the combined table with uncertainties, under the optimizer-scoped file00_… names:

# the fitted parameters of the joint fit with their uncertainties
joint.params.loc[joint.params["vary"], ["name", "value", "stderr"]].round(4)
name value stderr
6 GLP_01_x0_gaussCONV_SD 2.9883 0.0146
7 file00_GLP_01_x0_expFun_01_A 4.9759 0.0077
8 GLP_01_x0_expFun_01_tau 50.0789 0.1293
18 file01_GLP_01_x0_expFun_01_A 2.0168 0.0057
28 file02_GLP_01_x0_expFun_01_A 0.9875 0.0057
38 file03_GLP_01_x0_expFun_01_A 0.5041 0.0055
48 file04_GLP_01_x0_expFun_01_A 0.1857 0.0055
58 file05_GLP_01_x0_expFun_01_A 0.1033 0.0051

Every fitted parameter lands within a few stderr of its truth. The shared SD and tau come out to a fraction of a percent, and the smallest shift, 0.1, is recovered at about five percent relative uncertainty. Compare 20_multi_file_independent_fit: fit on its own, the tau of data_6 carried an uncertainty fifty times larger than that of data_1; here one shared tau is pinned by all six files at once.

Tips#

  • vary levels live in the YAML, not the code. project / file / static on each parameter decide what is shared, fit per-file, or frozen from the baseline — reshape the sharing scheme by editing models_time.yaml, no code change.

  • project.fit_2d() is a true joint fit. Every file enters one residual; project parameters resolve to a single shared value, file parameters to one per file. Contrast 20_multi_file_independent_fit, which fits each file separately.

  • Static energy parameters come from the baseline. Every line-shape parameter is static, so the ground-state shape is frozen per file and only the time dynamics are fit in 2D.

Next Steps#

  • 20_multi_file_independent_fit — the independent-fit counterpart, no shared parameters.

  • The stderr column above is the tier-1 error bar. 12_uncertainty_mcmc explains the profiled and MCMC tiers on a single file; project.fit_2d takes the same mc_settings for the joint posterior (project.results.get_joint(model="2D").mcmc, plot_joint_mcmc).

  • 11_save_load_export — project.save_fits() archives the joint result as one bundle with its per-file slots.

Model files#

The YAML files next to this notebook, as shipped with the example.

models_energy.yaml#

# use these models to describe the energy-dependent data
# (available functions info: /src/trspecfit/functions/energy.py)

# ground state (most parameters should be able to vary)
base:
    LinBack:
      m: [1E-2, True, -1, 1]
      b: [0, True, -5, 5]
      xStart: [0, False]
      xStop: [20, False]
    GLP:
      A: [12, True, 5, 15]
      x0: [8, True, 5, 15]
      F: [1.5, True, 0.75, 2.5]
      m: [0.3, True, 0, 1]

# 2D model: every line-shape parameter is "static" (not fitted, set from the
# baseline fit); the "project" / "file" levels live in models_time.yaml
2D:
    LinBack:
      m: [1E-2, static]
      b: [0, static]
      xStart: [0, static]
      xStop: [20, static]
    GLP:
      A: [12, static]
      x0: [8, static]
      F: [1.5, static]
      m: [0.3, static]

models_time.yaml#

# use these models to add time-dependence to a parameter in a 2D model
# (available functions info: /src/trspecfit/functions/time.py)

# vary field uses "project" / "file" / "static" strings
# - "project": shared across all files in project.fit_2d (fitted once)
# - "file": independent per file (fitted separately)
# - "static": fixed (not fitted); energy-model parameters are set from the baseline fit
MonoExpPosIRF:
    gaussCONV:
        SD: [5, project, 1.0E-6, 10]
    expFun:
        A: [5, file, 0, 15]
        tau: [10, project, 1, 100]
        t0: [0, static]

project.yaml#

# Project configuration for project-level fitting example

# Display settings
show_output: 1

# Axis labels
e_label: 'Energy (arb. units)'
t_label: 'Time (arb. units)'
z_label: 'Intensity (arb. units)'

# Plot settings
x_dir: 'def'
x_type: 'lin'
y_dir: 'def'
y_type: 'lin'
z_colormap: 'viridis'
z_type: 'lin'
dpi_plt: 100
dpi_save: 300
res_mult: 5

The data were generated by data/generate_data.ipynb; its page shows the truth model files.