{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "0",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:42.917324Z",
"iopub.status.busy": "2026-06-30T22:39:42.916958Z",
"iopub.status.idle": "2026-06-30T22:39:42.924551Z",
"shell.execute_reply": "2026-06-30T22:39:42.922660Z"
},
"tags": [
"hide-in-docs"
]
},
"outputs": [],
"source": [
"# Check whether easydiffraction is installed; install it if needed.\n",
"# Required for remote environments such as Google Colab.\n",
"import importlib.util\n",
"\n",
"if importlib.util.find_spec('easydiffraction') is None:\n",
" %pip install easydiffraction==0.19.1"
]
},
{
"cell_type": "markdown",
"id": "1",
"metadata": {},
"source": [
"# Bayesian Analysis: Tb2TiO7 (`emcee`), HEiDi\n",
"\n",
"This tutorial demonstrates a practical two-stage workflow for single-crystal\n",
"diffraction analysis with EasyDiffraction.\n",
"\n",
"In the first stage, we run a fast local refinement to obtain a sensible\n",
"point estimate and parameter uncertainties. In the second stage, we use\n",
"these refined values to define fit bounds and then sample the posterior\n",
"distribution with emcee.\n",
"\n",
"The example uses constant-wavelength neutron single-crystal diffraction data\n",
"for Tb2TiO7 measured on HEiDi at FRM II.\n",
"\n",
"The goal is not only to obtain a good fit, but also to answer Bayesian\n",
"questions such as:\n",
"\n",
"- Which parameter values are most probable?\n",
"- How broad are the credible intervals?\n",
"- Which parameters are strongly correlated?\n",
"- How much uncertainty propagates into the calculated reflection\n",
" intensities?"
]
},
{
"cell_type": "markdown",
"id": "2",
"metadata": {},
"source": [
"## 🛠️ Import Library"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "3",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:42.926477Z",
"iopub.status.busy": "2026-06-30T22:39:42.926259Z",
"iopub.status.idle": "2026-06-30T22:39:46.878423Z",
"shell.execute_reply": "2026-06-30T22:39:46.877548Z"
}
},
"outputs": [],
"source": [
"import easydiffraction as edi"
]
},
{
"cell_type": "markdown",
"id": "4",
"metadata": {},
"source": [
"## 📦 Define Project\n",
"\n",
"The project object keeps structures, experiments, fit settings, and\n",
"plotting utilities together in a single place. We will build the full\n",
"workflow inside this object."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "5",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:46.880211Z",
"iopub.status.busy": "2026-06-30T22:39:46.879899Z",
"iopub.status.idle": "2026-06-30T22:39:47.117667Z",
"shell.execute_reply": "2026-06-30T22:39:47.116882Z"
}
},
"outputs": [],
"source": [
"project = edi.Project(name='tbti_heidi_emcee')"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "6",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:47.119809Z",
"iopub.status.busy": "2026-06-30T22:39:47.119637Z",
"iopub.status.idle": "2026-06-30T22:39:47.237461Z",
"shell.execute_reply": "2026-06-30T22:39:47.236725Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1;36mSaving project 📦 \u001b[0m\u001b[32m'tbti_heidi_emcee'\u001b[0m\u001b[1;36m to \u001b[0m\u001b[32m'../../../projects/bayesian-emcee-tbti-heidi'\u001b[0m\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"├── 📄 project.edi\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"├── 📁 structures/\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"├── 📁 experiments/\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"├── 📁 analysis/\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"│ └── 📄 analysis.edi\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"└── 📁 reports/\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
" └── 📄 tbti_heidi_emcee.html\n"
]
}
],
"source": [
"project.save_as(dir_path='projects/bayesian-emcee-tbti-heidi')"
]
},
{
"cell_type": "markdown",
"id": "7",
"metadata": {},
"source": [
"## 🧩 Define Structure\n",
"\n",
"For this example we start from a CIF file describing the Tb2TiO7\n",
"pyrochlore structure. Loading the structure from CIF is convenient\n",
"because it preserves a realistic starting\n",
"model without rebuilding the full structure by hand."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "8",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:47.238919Z",
"iopub.status.busy": "2026-06-30T22:39:47.238774Z",
"iopub.status.idle": "2026-06-30T22:39:47.244577Z",
"shell.execute_reply": "2026-06-30T22:39:47.244171Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1;36mGetting data\u001b[0m\u001b[1;36m...\u001b[0m\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Data \u001b[32m'struct-tbti'\u001b[0m: Tb2Ti2O7 \u001b[1m(\u001b[0mcrystal structure\u001b[1m)\u001b[0m\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"✅ Data \u001b[32m'struct-tbti'\u001b[0m already present at \u001b[32m'../../../data/struct-tbti.cif'\u001b[0m. Keeping existing.\n"
]
}
],
"source": [
"structure_path = edi.download_data('struct-tbti', destination='data')"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "9",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:47.246391Z",
"iopub.status.busy": "2026-06-30T22:39:47.246234Z",
"iopub.status.idle": "2026-06-30T22:39:47.258868Z",
"shell.execute_reply": "2026-06-30T22:39:47.258068Z"
}
},
"outputs": [],
"source": [
"project.structures.add_from_cif_path(structure_path)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "10",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:47.260509Z",
"iopub.status.busy": "2026-06-30T22:39:47.260348Z",
"iopub.status.idle": "2026-06-30T22:39:47.263415Z",
"shell.execute_reply": "2026-06-30T22:39:47.262607Z"
}
},
"outputs": [],
"source": [
"structure = project.structures['tbti']"
]
},
{
"cell_type": "markdown",
"id": "11",
"metadata": {},
"source": [
"Render the structure to confirm the pyrochlore model loaded from CIF as\n",
"expected before configuring the experiment."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "12",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:47.264774Z",
"iopub.status.busy": "2026-06-30T22:39:47.264614Z",
"iopub.status.idle": "2026-06-30T22:39:47.470144Z",
"shell.execute_reply": "2026-06-30T22:39:47.468822Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1;36mStructure 🧩 \u001b[0m\u001b[32m'tbti'\u001b[0m\u001b[1;36m \u001b[0m\u001b[1;36m(\u001b[0m\u001b[1;36mAtom view type: \u001b[0m\u001b[32m'covalent'\u001b[0m\u001b[1;36m)\u001b[0m\n"
]
},
{
"data": {
"text/html": [
"
\n",
"
\n",
"
Loading plot…
\n",
"
\n",
"
\n",
"
\n",
"
drag = rotate
wheel = zoom
right-drag = pan
\n",
"
\n",
"
\n",
"\n",
"\n",
"\n",
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"project.display.structure(struct_name='tbti')"
]
},
{
"cell_type": "markdown",
"id": "13",
"metadata": {},
"source": [
"## 🔬 Define Experiment\n",
"\n",
"Next we download the measured reflection data, create a neutron\n",
"single-crystal experiment, and configure the crystal link,\n",
"wavelength, and extinction model."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "14",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:47.471794Z",
"iopub.status.busy": "2026-06-30T22:39:47.471636Z",
"iopub.status.idle": "2026-06-30T22:39:47.478120Z",
"shell.execute_reply": "2026-06-30T22:39:47.477330Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1;36mGetting data\u001b[0m\u001b[1;36m...\u001b[0m\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Data \u001b[32m'meas-tbti-heidi'\u001b[0m: Tb2Ti2O7, HEiDi \u001b[1m(\u001b[0mMLZ\u001b[1m)\u001b[0m\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"✅ Data \u001b[32m'meas-tbti-heidi'\u001b[0m already present at \u001b[32m'../../../data/meas-tbti-heidi.xye'\u001b[0m. Keeping existing.\n"
]
}
],
"source": [
"data_path = edi.download_data('meas-tbti-heidi', destination='data')"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "15",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:47.479498Z",
"iopub.status.busy": "2026-06-30T22:39:47.479348Z",
"iopub.status.idle": "2026-06-30T22:39:47.605577Z",
"shell.execute_reply": "2026-06-30T22:39:47.604858Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1;36mData loaded successfully\u001b[0m\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Experiment 🔬 \u001b[32m'heidi'\u001b[0m. Number of data points: \u001b[1;36m220\u001b[0m.\n"
]
}
],
"source": [
"project.experiments.add_from_data_path(\n",
" name='heidi',\n",
" data_path=data_path,\n",
" sample_form='single crystal',\n",
" beam_mode='constant wavelength',\n",
" radiation_probe='neutron',\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "16",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:47.613173Z",
"iopub.status.busy": "2026-06-30T22:39:47.612953Z",
"iopub.status.idle": "2026-06-30T22:39:47.616621Z",
"shell.execute_reply": "2026-06-30T22:39:47.615972Z"
}
},
"outputs": [],
"source": [
"experiment = project.experiments['heidi']"
]
},
{
"cell_type": "markdown",
"id": "17",
"metadata": {},
"source": [
"Link the crystal structure to the experiment and set its scale factor."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "18",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:47.619643Z",
"iopub.status.busy": "2026-06-30T22:39:47.619459Z",
"iopub.status.idle": "2026-06-30T22:39:47.628377Z",
"shell.execute_reply": "2026-06-30T22:39:47.624864Z"
}
},
"outputs": [],
"source": [
"experiment.linked_structure.structure_id = 'tbti'\n",
"experiment.linked_structure.scale = 1.0"
]
},
{
"cell_type": "markdown",
"id": "19",
"metadata": {},
"source": [
"Set the instrument wavelength and starting extinction parameters.\n",
"These values provide the initial experiment description for the local\n",
"refinement."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "20",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:47.630307Z",
"iopub.status.busy": "2026-06-30T22:39:47.630115Z",
"iopub.status.idle": "2026-06-30T22:39:47.635502Z",
"shell.execute_reply": "2026-06-30T22:39:47.633083Z"
}
},
"outputs": [],
"source": [
"experiment.instrument.setup_wavelength = 0.793"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "21",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:47.637308Z",
"iopub.status.busy": "2026-06-30T22:39:47.637139Z",
"iopub.status.idle": "2026-06-30T22:39:47.642367Z",
"shell.execute_reply": "2026-06-30T22:39:47.640962Z"
}
},
"outputs": [],
"source": [
"experiment.extinction.mosaicity = 35000\n",
"experiment.extinction.radius = 10"
]
},
{
"cell_type": "markdown",
"id": "22",
"metadata": {},
"source": [
"## 🚀 Initial Refinement\n",
"\n",
"Before Bayesian sampling, it is useful to run a deterministic fit. This\n",
"gives us:\n",
"\n",
"- a good point estimate near the best-fit region,\n",
"- uncertainties from the local optimizer,\n",
"- a quick check that the model and experiment are configured\n",
" sensibly.\n",
"\n",
"In this tutorial we refine a small set of structural and extinction\n",
"parameters while keeping occupancies fixed."
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "23",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:47.644108Z",
"iopub.status.busy": "2026-06-30T22:39:47.643928Z",
"iopub.status.idle": "2026-06-30T22:39:47.652684Z",
"shell.execute_reply": "2026-06-30T22:39:47.651947Z"
}
},
"outputs": [],
"source": [
"structure.atom_sites['O1'].fract_x.free = True\n",
"\n",
"structure.atom_sites['Ti'].occupancy.free = False\n",
"structure.atom_sites['O1'].occupancy.free = False\n",
"structure.atom_sites['O2'].occupancy.free = False\n",
"\n",
"structure.atom_sites['Tb'].adp_iso.free = True\n",
"structure.atom_sites['Ti'].adp_iso.free = True\n",
"structure.atom_sites['O1'].adp_iso.free = True\n",
"structure.atom_sites['O2'].adp_iso.free = True"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "24",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:47.654395Z",
"iopub.status.busy": "2026-06-30T22:39:47.654226Z",
"iopub.status.idle": "2026-06-30T22:39:47.660295Z",
"shell.execute_reply": "2026-06-30T22:39:47.657927Z"
}
},
"outputs": [],
"source": [
"experiment.linked_structure.scale.free = True\n",
"experiment.extinction.radius.free = True"
]
},
{
"cell_type": "markdown",
"id": "25",
"metadata": {},
"source": [
"We keep using the default LMFIT Levenberg-Marquardt minimizer as a fast local\n",
"optimizer. Its main purpose here is to provide a stable starting point\n",
"and uncertainty estimates for the Bayesian run."
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "26",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:47.662191Z",
"iopub.status.busy": "2026-06-30T22:39:47.661875Z",
"iopub.status.idle": "2026-06-30T22:39:47.671511Z",
"shell.execute_reply": "2026-06-30T22:39:47.670463Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1;36mMinimizer types\u001b[0m\n"
]
},
{
"data": {
"text/html": [
" | | Type | Description |
|---|
| 1 | | bumps | BUMPS library using the default Levenberg-Marquardt method |
|---|
| 2 | | bumps (amoeba) | BUMPS library with Nelder-Mead simplex method |
|---|
| 3 | | bumps (de) | BUMPS library with differential evolution method |
|---|
| 4 | | bumps (dream) | BUMPS library with DREAM Bayesian sampling |
|---|
| 5 | | bumps (lm) | BUMPS library with Levenberg-Marquardt method |
|---|
| 6 | | dfols | DFO-LS library for derivative-free least-squares optimization |
|---|
| 7 | | emcee | emcee affine-invariant ensemble Bayesian sampling |
|---|
| 8 | | lmfit | LMFIT library using the default Levenberg-Marquardt method |
|---|
| 9 | | lmfit (least_squares) | LMFIT library with SciPy's trust region reflective algorithm |
|---|
| 10 | * | lmfit (leastsq) | LMFIT library with Levenberg-Marquardt least squares method |
|---|
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"project.analysis.minimizer.show_supported()"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "27",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:47.673041Z",
"iopub.status.busy": "2026-06-30T22:39:47.672867Z",
"iopub.status.idle": "2026-06-30T22:39:49.183367Z",
"shell.execute_reply": "2026-06-30T22:39:49.181111Z"
}
},
"outputs": [
{
"data": {
"text/html": [],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function() {\n",
" const button = document.getElementById('ed-fit-stop-e738ddf516294bf9a37c4fd1a4f7e13a-button');\n",
" const status = document.getElementById('ed-fit-stop-e738ddf516294bf9a37c4fd1a4f7e13a-status');\n",
" const kernelId = '';\n",
" if (!button) {\n",
" return;\n",
" }\n",
"\n",
" function setStatus(text) {\n",
" if (status) {\n",
" status.textContent = text;\n",
" }\n",
" }\n",
"\n",
" function pageConfig() {\n",
" const element = document.getElementById('jupyter-config-data');\n",
" if (!element || !element.textContent) {\n",
" return {};\n",
" }\n",
" try {\n",
" return JSON.parse(element.textContent);\n",
" } catch (error) {\n",
" return {};\n",
" }\n",
" }\n",
"\n",
" function baseUrl(config) {\n",
" const configured = config.baseUrl || config.base_url ||\n",
" (window.Jupyter && Jupyter.notebook && Jupyter.notebook.base_url);\n",
" if (configured) {\n",
" return configured.endsWith('/') ? configured : configured + '/';\n",
" }\n",
" const markers = ['/lab/', '/notebooks/', '/tree/'];\n",
" for (const marker of markers) {\n",
" const index = window.location.pathname.indexOf(marker);\n",
" if (index >= 0) {\n",
" return window.location.pathname.slice(0, index + 1);\n",
" }\n",
" }\n",
" return '/';\n",
" }\n",
"\n",
" function token(config) {\n",
" return config.token || new URLSearchParams(window.location.search).get('token') || '';\n",
" }\n",
"\n",
" function cookie(name) {\n",
" const prefix = name + '=';\n",
" for (const part of document.cookie.split(';')) {\n",
" const trimmed = part.trim();\n",
" if (trimmed.startsWith(prefix)) {\n",
" return decodeURIComponent(trimmed.slice(prefix.length));\n",
" }\n",
" }\n",
" return '';\n",
" }\n",
"\n",
" function notebookPath() {\n",
" const decoded = decodeURIComponent(window.location.pathname);\n",
" const markers = ['/lab/tree/', '/notebooks/', '/tree/'];\n",
" for (const marker of markers) {\n",
" const index = decoded.indexOf(marker);\n",
" if (index >= 0) {\n",
" return decoded.slice(index + marker.length);\n",
" }\n",
" }\n",
" return '';\n",
" }\n",
"\n",
" async function kernelFromSessions(config) {\n",
" const url = new URL(baseUrl(config) + 'api/sessions', window.location.origin);\n",
" const authToken = token(config);\n",
" if (authToken) {\n",
" url.searchParams.set('token', authToken);\n",
" }\n",
" const response = await fetch(url, {credentials: 'same-origin'});\n",
" if (!response.ok) {\n",
" return '';\n",
" }\n",
" const sessions = await response.json();\n",
" const path = notebookPath();\n",
" const session = sessions.find((item) => item.path === path) || sessions[0];\n",
" return session && session.kernel ? session.kernel.id : '';\n",
" }\n",
"\n",
" async function interruptKernel(config, resolvedKernelId) {\n",
" const url = new URL(\n",
" baseUrl(config) + 'api/kernels/' + resolvedKernelId + '/interrupt',\n",
" window.location.origin\n",
" );\n",
" const authToken = token(config);\n",
" if (authToken) {\n",
" url.searchParams.set('token', authToken);\n",
" }\n",
" const xsrfToken = cookie('_xsrf');\n",
" const headers = {};\n",
" if (xsrfToken) {\n",
" headers['X-XSRFToken'] = xsrfToken;\n",
" }\n",
" const response = await fetch(url, {\n",
" method: 'POST',\n",
" credentials: 'same-origin',\n",
" headers: headers\n",
" });\n",
" return response.ok;\n",
" }\n",
"\n",
" button.addEventListener('click', async function() {\n",
" button.disabled = true;\n",
" setStatus('Stopping...');\n",
" const config = pageConfig();\n",
" try {\n",
" const resolvedKernelId = kernelId || await kernelFromSessions(config);\n",
" if (!resolvedKernelId) {\n",
" throw new Error('Could not resolve the current kernel id.');\n",
" }\n",
" const interrupted = await interruptKernel(config, resolvedKernelId);\n",
" if (!interrupted) {\n",
" throw new Error('Jupyter Server rejected the interrupt request.');\n",
" }\n",
" setStatus('Interrupt sent...');\n",
" } catch (error) {\n",
" button.disabled = false;\n",
" setStatus('Use Kernel > Interrupt to stop this fit.');\n",
" }\n",
" });\n",
"})();\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1;36mStandard fitting\u001b[0m\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"📋 Using experiment 🔬 \u001b[32m'heidi'\u001b[0m for \u001b[32m'single'\u001b[0m fitting\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"🚀 Starting fit process with \u001b[32m'lmfit \u001b[0m\u001b[32m(\u001b[0m\u001b[32mleastsq\u001b[0m\u001b[32m)\u001b[0m\u001b[32m'\u001b[0m\u001b[33m...\u001b[0m\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"📈 Goodness-of-fit progress:\n"
]
},
{
"data": {
"text/html": [
" | iteration | time (s) | χ² | change / status |
|---|
| 1 | 1 | 0.38 | 592.02 | |
|---|
| 2 | 11 | 0.47 | 191.19 | 67.7% ↓ |
|---|
| 3 | 19 | 0.57 | 36.84 | 80.7% ↓ |
|---|
| 4 | 29 | 0.68 | 18.99 | 48.4% ↓ |
|---|
| 5 | 37 | 0.77 | 12.74 | 32.9% ↓ |
|---|
| 6 | 62 | 1.07 | 12.71 | |
|---|
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"🏆 Best goodness-of-fit \u001b[1m(\u001b[0mreduced χ²\u001b[1m)\u001b[0m is \u001b[1;36m12.71\u001b[0m at iteration \u001b[1;36m61\u001b[0m\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"✅ Fitting complete.\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1;36mSaving project 📦 \u001b[0m\u001b[32m'tbti_heidi_emcee'\u001b[0m\u001b[1;36m to \u001b[0m\u001b[32m'../../../projects/bayesian-emcee-tbti-heidi'\u001b[0m\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"├── 📄 project.edi\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"├── 📁 structures/\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"│ └── 📄 tbti.edi\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"├── 📁 experiments/\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"│ └── 📄 heidi.edi\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"├── 📁 analysis/\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"│ └── 📄 analysis.edi\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"└── 📁 reports/\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
" └── 📄 tbti_heidi_emcee.html\n"
]
}
],
"source": [
"project.analysis.fit()"
]
},
{
"cell_type": "markdown",
"id": "28",
"metadata": {},
"source": [
"The fit-results display summarizes the locally refined values and their\n",
"estimated uncertainties."
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "29",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:49.187888Z",
"iopub.status.busy": "2026-06-30T22:39:49.187686Z",
"iopub.status.idle": "2026-06-30T22:39:49.544753Z",
"shell.execute_reply": "2026-06-30T22:39:49.542087Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"⚙️ Settings used:\n"
]
},
{
"data": {
"text/html": [
" | Name | Value | Description |
|---|
| 1 | max_iterations | 1000 | Maximum solver iterations. |
|---|
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"📋 Least-squares fit results:\n"
]
},
{
"data": {
"text/html": [
" | Metric | Value |
|---|
| 1 | 🧪 Minimizer | lmfit (leastsq) |
|---|
| 2 | ✅ Overall status | success |
|---|
| 3 | ⏱️ Fitting time (seconds) | 1.07 |
|---|
| 4 | 🔁 Iterations | 59 |
|---|
| 5 | 📏 Goodness-of-fit (reduced χ²) | 12.71 |
|---|
| 6 | 📏 R-factor (Rf, %) | 7.67 |
|---|
| 7 | 📏 R-factor squared (Rf², %) | 8.12 |
|---|
| 8 | 📏 Weighted R-factor (wR, %) | 8.52 |
|---|
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"📈 Refined parameters:\n"
]
},
{
"data": {
"text/html": [
" | datablock | category | entry | parameter | units | start | value | s.u. | change |
|---|
| 1 | tbti | atom_site | Tb | adp_iso | Ų | 0.5300 | 0.5319 | 0.0190 | 0.36 % ↑ |
|---|
| 2 | tbti | atom_site | Ti | adp_iso | Ų | 0.4800 | 0.4776 | 0.0311 | 0.50 % ↓ |
|---|
| 3 | tbti | atom_site | O1 | fract_x | | 0.3280 | 0.3280 | 0.0001 | 0.00 % ↓ |
|---|
| 4 | tbti | atom_site | O1 | adp_iso | Ų | 0.4500 | 0.4504 | 0.0165 | 0.09 % ↑ |
|---|
| 5 | tbti | atom_site | O2 | adp_iso | Ų | 0.2300 | 0.2375 | 0.0259 | 3.25 % ↑ |
|---|
| 6 | heidi | extinction | | radius | μm | 10.0000 | 26.4607 | 1.0003 | 164.61 % ↑ |
|---|
| 7 | heidi | linked_structure | | scale | | 1.0000 | 2.9236 | 0.0488 | 192.36 % ↑ |
|---|
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" • start = parameter value before refinement
• value = refined value from least-squares minimization
• s.u. = standard uncertainty (one sigma), from the covariance matrix
• change = relative change from start, in %; ↑ = increase, ↓ = decrease
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"project.display.fit.results()"
]
},
{
"cell_type": "markdown",
"id": "30",
"metadata": {},
"source": [
"The correlation plot shows how strongly the refined parameters move\n",
"together in the local refinement. The measured-vs-calculated plot shows\n",
"how well the refined crystal model reproduces the measured reflection\n",
"intensities."
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "31",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:49.547363Z",
"iopub.status.busy": "2026-06-30T22:39:49.546630Z",
"iopub.status.idle": "2026-06-30T22:39:49.581333Z",
"shell.execute_reply": "2026-06-30T22:39:49.580276Z"
}
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"project.display.fit.correlations()"
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "32",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:49.582848Z",
"iopub.status.busy": "2026-06-30T22:39:49.582674Z",
"iopub.status.idle": "2026-06-30T22:39:49.598556Z",
"shell.execute_reply": "2026-06-30T22:39:49.597667Z"
}
},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"project.display.pattern(expt_name='heidi')"
]
},
{
"cell_type": "markdown",
"id": "33",
"metadata": {},
"source": [
"## 🎲 Prepare Sampling\n",
"\n",
"Bayesian samplers require finite bounds for the free parameters. Instead of\n",
"setting them manually, we derive them from the uncertainties estimated\n",
"in the local refinement.\n",
"\n",
"The helper method `set_fit_bounds_from_uncertainty` centers the bounds\n",
"on the current parameter value and expands them by a chosen multiple of\n",
"the reported uncertainty.\n",
"\n",
"The default `multiplier` is 4. In this single-crystal tutorial we use\n",
"a tighter value of `1.5` to keep the sampling window closer to the\n",
"locally refined solution.\n",
"\n",
"Show unset fit bounds before setting them from the local refinement\n",
"uncertainties."
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "34",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:49.600042Z",
"iopub.status.busy": "2026-06-30T22:39:49.599874Z",
"iopub.status.idle": "2026-06-30T22:39:49.703147Z",
"shell.execute_reply": "2026-06-30T22:39:49.699621Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1;36mFree parameters for both structures \u001b[0m\u001b[1;36m(\u001b[0m\u001b[1;36m🧩 data blocks\u001b[0m\u001b[1;36m)\u001b[0m\u001b[1;36m and experiments \u001b[0m\u001b[1;36m(\u001b[0m\u001b[1;36m🔬 data blocks\u001b[0m\u001b[1;36m)\u001b[0m\n"
]
},
{
"data": {
"text/html": [
" | datablock | category | entry | parameter | value | uncertainty | min | max | units |
|---|
| 1 | tbti | atom_site | Tb | adp_iso | 0.53188 | 0.01900 | -inf | inf | Ų |
|---|
| 2 | tbti | atom_site | Ti | adp_iso | 0.47759 | 0.03113 | -inf | inf | Ų |
|---|
| 3 | tbti | atom_site | O1 | fract_x | 0.32803 | 0.00009 | -inf | inf | |
|---|
| 4 | tbti | atom_site | O1 | adp_iso | 0.45043 | 0.01652 | -inf | inf | Ų |
|---|
| 5 | tbti | atom_site | O2 | adp_iso | 0.23747 | 0.02588 | -inf | inf | Ų |
|---|
| 6 | heidi | extinction | | radius | 26.46071 | 1.00034 | -inf | inf | μm |
|---|
| 7 | heidi | linked_structure | | scale | 2.92356 | 0.04884 | -inf | inf | |
|---|
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"project.display.parameters.free()"
]
},
{
"cell_type": "markdown",
"id": "35",
"metadata": {},
"source": [
"Set fit bounds for all free parameters using `multiplier=1.5`. In this\n",
"tutorial that means the posterior pair plot will later refer to a\n",
"`±1.5 × uncertainty` region in its title. To widen the sampling window,\n",
"increase the multiplier explicitly."
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "36",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:49.710114Z",
"iopub.status.busy": "2026-06-30T22:39:49.709881Z",
"iopub.status.idle": "2026-06-30T22:39:49.722632Z",
"shell.execute_reply": "2026-06-30T22:39:49.721878Z"
}
},
"outputs": [],
"source": [
"for param in project.free_parameters:\n",
" param.set_fit_bounds_from_uncertainty(multiplier=1.5)"
]
},
{
"cell_type": "markdown",
"id": "37",
"metadata": {},
"source": [
"Displaying the free parameters again is a convenient way to confirm\n",
"that the fit bounds have been assigned as expected before launching the\n",
"sampler."
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "38",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:49.724885Z",
"iopub.status.busy": "2026-06-30T22:39:49.724693Z",
"iopub.status.idle": "2026-06-30T22:39:49.840569Z",
"shell.execute_reply": "2026-06-30T22:39:49.839758Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1;36mFree parameters for both structures \u001b[0m\u001b[1;36m(\u001b[0m\u001b[1;36m🧩 data blocks\u001b[0m\u001b[1;36m)\u001b[0m\u001b[1;36m and experiments \u001b[0m\u001b[1;36m(\u001b[0m\u001b[1;36m🔬 data blocks\u001b[0m\u001b[1;36m)\u001b[0m\n"
]
},
{
"data": {
"text/html": [
" | datablock | category | entry | parameter | value | uncertainty | min | max | units |
|---|
| 1 | tbti | atom_site | Tb | adp_iso | 0.53188 | 0.01900 | 0.50338 | 0.56038 | Ų |
|---|
| 2 | tbti | atom_site | Ti | adp_iso | 0.47759 | 0.03113 | 0.43090 | 0.52428 | Ų |
|---|
| 3 | tbti | atom_site | O1 | fract_x | 0.32803 | 0.00009 | 0.32789 | 0.32817 | |
|---|
| 4 | tbti | atom_site | O1 | adp_iso | 0.45043 | 0.01652 | 0.42565 | 0.47520 | Ų |
|---|
| 5 | tbti | atom_site | O2 | adp_iso | 0.23747 | 0.02588 | 0.19866 | 0.27629 | Ų |
|---|
| 6 | heidi | extinction | | radius | 26.46071 | 1.00034 | 24.96020 | 27.96123 | μm |
|---|
| 7 | heidi | linked_structure | | scale | 2.92356 | 0.04884 | 2.85030 | 2.99682 | |
|---|
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"project.display.parameters.free()"
]
},
{
"cell_type": "markdown",
"id": "39",
"metadata": {},
"source": [
"## 🎲 Run Sampling\n",
"\n",
"We now switch from the local minimizer to the Bayesian emcee sampler.\n",
"\n",
"The settings below are intentionally small so the tutorial runs\n",
"quickly. For production analysis you would usually increase the number\n",
"of steps and often the burn-in as well. emcee also lets you tune how\n",
"walkers are initialized, how many walkers are used, and which proposal\n",
"move drives the ensemble.\n",
"\n",
"The `burn` setting is auto-resolved when left unset. Here we override\n",
"`steps` with a smaller value to keep the tutorial fast, and the\n",
"effective burn-in is recomputed automatically."
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "40",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:49.842141Z",
"iopub.status.busy": "2026-06-30T22:39:49.841967Z",
"iopub.status.idle": "2026-06-30T22:39:49.848846Z",
"shell.execute_reply": "2026-06-30T22:39:49.848040Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1;36mMinimizer types\u001b[0m\n"
]
},
{
"data": {
"text/html": [
" | | Type | Description |
|---|
| 1 | | bumps | BUMPS library using the default Levenberg-Marquardt method |
|---|
| 2 | | bumps (amoeba) | BUMPS library with Nelder-Mead simplex method |
|---|
| 3 | | bumps (de) | BUMPS library with differential evolution method |
|---|
| 4 | | bumps (dream) | BUMPS library with DREAM Bayesian sampling |
|---|
| 5 | | bumps (lm) | BUMPS library with Levenberg-Marquardt method |
|---|
| 6 | | dfols | DFO-LS library for derivative-free least-squares optimization |
|---|
| 7 | | emcee | emcee affine-invariant ensemble Bayesian sampling |
|---|
| 8 | | lmfit | LMFIT library using the default Levenberg-Marquardt method |
|---|
| 9 | | lmfit (least_squares) | LMFIT library with SciPy's trust region reflective algorithm |
|---|
| 10 | * | lmfit (leastsq) | LMFIT library with Levenberg-Marquardt least squares method |
|---|
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"project.analysis.minimizer.show_supported()"
]
},
{
"cell_type": "code",
"execution_count": 26,
"id": "41",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:49.850279Z",
"iopub.status.busy": "2026-06-30T22:39:49.850124Z",
"iopub.status.idle": "2026-06-30T22:39:49.857914Z",
"shell.execute_reply": "2026-06-30T22:39:49.857122Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"⚠️ Switching minimizer type removes these settings: \n",
" • max_iterations \n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"⚠️ Switching minimizer type adds these settings with defaults: \n",
" • burn_in_steps=1000 \n",
" • initialization_method='ball' \n",
" • parallel_workers=0 \n",
" • population_size=32 \n",
" • proposal_moves='de' \n",
" • random_seed=None \n",
" • sampling_steps=5000 \n",
" • thinning_interval=1 \n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1;36mCurrent minimizer changed to\u001b[0m\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"emcee\n"
]
}
],
"source": [
"project.analysis.minimizer.type = 'emcee'"
]
},
{
"cell_type": "code",
"execution_count": 27,
"id": "42",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:49.859464Z",
"iopub.status.busy": "2026-06-30T22:39:49.859329Z",
"iopub.status.idle": "2026-06-30T22:39:49.862663Z",
"shell.execute_reply": "2026-06-30T22:39:49.861766Z"
}
},
"outputs": [],
"source": [
"project.analysis.minimizer.sampling_steps = 500 # lower than the default 3000\n",
"project.analysis.minimizer.burn_in_steps = 100 # lower than the default 600\n",
"project.analysis.minimizer.population_size = 16 # lower than the default 32\n",
"project.analysis.minimizer.random_seed = 42 # fixed seed for reproducible output"
]
},
{
"cell_type": "code",
"execution_count": 28,
"id": "43",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:39:49.864015Z",
"iopub.status.busy": "2026-06-30T22:39:49.863877Z",
"iopub.status.idle": "2026-06-30T22:41:44.860663Z",
"shell.execute_reply": "2026-06-30T22:41:44.860003Z"
}
},
"outputs": [
{
"data": {
"text/html": [],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/javascript": [
"\n",
"(function() {\n",
" const button = document.getElementById('ed-fit-stop-640429646d9446d6b287049c12ca910d-button');\n",
" const status = document.getElementById('ed-fit-stop-640429646d9446d6b287049c12ca910d-status');\n",
" const kernelId = '';\n",
" if (!button) {\n",
" return;\n",
" }\n",
"\n",
" function setStatus(text) {\n",
" if (status) {\n",
" status.textContent = text;\n",
" }\n",
" }\n",
"\n",
" function pageConfig() {\n",
" const element = document.getElementById('jupyter-config-data');\n",
" if (!element || !element.textContent) {\n",
" return {};\n",
" }\n",
" try {\n",
" return JSON.parse(element.textContent);\n",
" } catch (error) {\n",
" return {};\n",
" }\n",
" }\n",
"\n",
" function baseUrl(config) {\n",
" const configured = config.baseUrl || config.base_url ||\n",
" (window.Jupyter && Jupyter.notebook && Jupyter.notebook.base_url);\n",
" if (configured) {\n",
" return configured.endsWith('/') ? configured : configured + '/';\n",
" }\n",
" const markers = ['/lab/', '/notebooks/', '/tree/'];\n",
" for (const marker of markers) {\n",
" const index = window.location.pathname.indexOf(marker);\n",
" if (index >= 0) {\n",
" return window.location.pathname.slice(0, index + 1);\n",
" }\n",
" }\n",
" return '/';\n",
" }\n",
"\n",
" function token(config) {\n",
" return config.token || new URLSearchParams(window.location.search).get('token') || '';\n",
" }\n",
"\n",
" function cookie(name) {\n",
" const prefix = name + '=';\n",
" for (const part of document.cookie.split(';')) {\n",
" const trimmed = part.trim();\n",
" if (trimmed.startsWith(prefix)) {\n",
" return decodeURIComponent(trimmed.slice(prefix.length));\n",
" }\n",
" }\n",
" return '';\n",
" }\n",
"\n",
" function notebookPath() {\n",
" const decoded = decodeURIComponent(window.location.pathname);\n",
" const markers = ['/lab/tree/', '/notebooks/', '/tree/'];\n",
" for (const marker of markers) {\n",
" const index = decoded.indexOf(marker);\n",
" if (index >= 0) {\n",
" return decoded.slice(index + marker.length);\n",
" }\n",
" }\n",
" return '';\n",
" }\n",
"\n",
" async function kernelFromSessions(config) {\n",
" const url = new URL(baseUrl(config) + 'api/sessions', window.location.origin);\n",
" const authToken = token(config);\n",
" if (authToken) {\n",
" url.searchParams.set('token', authToken);\n",
" }\n",
" const response = await fetch(url, {credentials: 'same-origin'});\n",
" if (!response.ok) {\n",
" return '';\n",
" }\n",
" const sessions = await response.json();\n",
" const path = notebookPath();\n",
" const session = sessions.find((item) => item.path === path) || sessions[0];\n",
" return session && session.kernel ? session.kernel.id : '';\n",
" }\n",
"\n",
" async function interruptKernel(config, resolvedKernelId) {\n",
" const url = new URL(\n",
" baseUrl(config) + 'api/kernels/' + resolvedKernelId + '/interrupt',\n",
" window.location.origin\n",
" );\n",
" const authToken = token(config);\n",
" if (authToken) {\n",
" url.searchParams.set('token', authToken);\n",
" }\n",
" const xsrfToken = cookie('_xsrf');\n",
" const headers = {};\n",
" if (xsrfToken) {\n",
" headers['X-XSRFToken'] = xsrfToken;\n",
" }\n",
" const response = await fetch(url, {\n",
" method: 'POST',\n",
" credentials: 'same-origin',\n",
" headers: headers\n",
" });\n",
" return response.ok;\n",
" }\n",
"\n",
" button.addEventListener('click', async function() {\n",
" button.disabled = true;\n",
" setStatus('Stopping...');\n",
" const config = pageConfig();\n",
" try {\n",
" const resolvedKernelId = kernelId || await kernelFromSessions(config);\n",
" if (!resolvedKernelId) {\n",
" throw new Error('Could not resolve the current kernel id.');\n",
" }\n",
" const interrupted = await interruptKernel(config, resolvedKernelId);\n",
" if (!interrupted) {\n",
" throw new Error('Jupyter Server rejected the interrupt request.');\n",
" }\n",
" setStatus('Interrupt sent...');\n",
" } catch (error) {\n",
" button.disabled = false;\n",
" setStatus('Use Kernel > Interrupt to stop this fit.');\n",
" }\n",
" });\n",
"})();\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1;36mStandard fitting\u001b[0m\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"📋 Using experiment 🔬 \u001b[32m'heidi'\u001b[0m for \u001b[32m'single'\u001b[0m fitting\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"🚀 Starting fit process with \u001b[32m'emcee'\u001b[0m\u001b[33m...\u001b[0m\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"📈 Bayesian sampling progress:\n"
]
},
{
"data": {
"text/html": [
" | step | progress | time (s) | log posterior | phase |
|---|
| 1 | | | 0.34 | -1354.04 | pre-processing |
|---|
| 2 | 34/600 | 5.7% | 4.09 | -1354.48 | burn-in |
|---|
| 3 | 67/600 | 11.2% | 9.05 | -1354.44 | burn-in |
|---|
| 4 | 100/600 | 16.7% | 12.75 | -1354.78 | burn-in |
|---|
| 5 | 101/600 | 16.8% | 12.88 | -1354.78 | sampling |
|---|
| 6 | 126/600 | 21.0% | 15.78 | -1355.34 | sampling |
|---|
| 7 | 151/600 | 25.2% | 18.55 | -1354.79 | sampling |
|---|
| 8 | 176/600 | 29.3% | 21.34 | -1354.79 | sampling |
|---|
| 9 | 201/600 | 33.5% | 23.90 | -1355.65 | sampling |
|---|
| 10 | 226/600 | 37.7% | 26.42 | -1354.92 | sampling |
|---|
| 11 | 251/600 | 41.8% | 28.95 | -1356.35 | sampling |
|---|
| 12 | 276/600 | 46.0% | 31.49 | -1354.44 | sampling |
|---|
| 13 | 301/600 | 50.2% | 34.03 | -1354.57 | sampling |
|---|
| 14 | 326/600 | 54.3% | 36.50 | -1354.68 | sampling |
|---|
| 15 | 350/600 | 58.3% | 38.93 | -1355.26 | sampling |
|---|
| 16 | 375/600 | 62.5% | 41.40 | -1355.31 | sampling |
|---|
| 17 | 400/600 | 66.7% | 43.89 | -1355.14 | sampling |
|---|
| 18 | 425/600 | 70.8% | 46.34 | -1355.43 | sampling |
|---|
| 19 | 450/600 | 75.0% | 48.85 | -1355.05 | sampling |
|---|
| 20 | 475/600 | 79.2% | 51.39 | -1355.61 | sampling |
|---|
| 21 | 500/600 | 83.3% | 53.91 | -1354.88 | sampling |
|---|
| 22 | 525/600 | 87.5% | 56.26 | -1354.86 | sampling |
|---|
| 23 | 550/600 | 91.7% | 58.54 | -1355.59 | sampling |
|---|
| 24 | 575/600 | 95.8% | 61.06 | -1354.60 | sampling |
|---|
| 25 | 600/600 | 100.0% | 63.61 | -1354.48 | sampling |
|---|
| 26 | | | 114.69 | | post-processing |
|---|
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"✅ Bayesian sampling complete.\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"⚠️ Convergence diagnostics indicate the posterior may be poorly mixed. \n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[1;36mSaving project 📦 \u001b[0m\u001b[32m'tbti_heidi_emcee'\u001b[0m\u001b[1;36m to \u001b[0m\u001b[32m'../../../projects/bayesian-emcee-tbti-heidi'\u001b[0m\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"├── 📄 project.edi\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"├── 📁 structures/\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"│ └── 📄 tbti.edi\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"├── 📁 experiments/\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"│ └── 📄 heidi.edi\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"├── 📁 analysis/\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"│ ├── 📄 analysis.edi\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"│ └── 📄 mcmc.h5\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"└── 📁 reports/\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
" └── 📄 tbti_heidi_emcee.html\n"
]
}
],
"source": [
"project.analysis.fit()"
]
},
{
"cell_type": "markdown",
"id": "44",
"metadata": {},
"source": [
"## 📊 Inspect Results\n",
"\n",
"The fit-results display now includes sampler settings, convergence\n",
"diagnostics, committed parameter values, and posterior summary\n",
"statistics."
]
},
{
"cell_type": "code",
"execution_count": 29,
"id": "45",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:41:44.862617Z",
"iopub.status.busy": "2026-06-30T22:41:44.862429Z",
"iopub.status.idle": "2026-06-30T22:41:44.892422Z",
"shell.execute_reply": "2026-06-30T22:41:44.891539Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"⚙️ Settings used:\n"
]
},
{
"data": {
"text/html": [
" | Name | Value | Description |
|---|
| 1 | sampling_steps | 500 | Total sampler iterations per chain. |
|---|
| 2 | burn_in_steps | 100 | Sampler iterations discarded as warm-up. |
|---|
| 3 | thinning_interval | 1 | Sampler thinning interval. |
|---|
| 4 | population_size | 16 | Number of chains or walkers. |
|---|
| 5 | parallel_workers | 0 | Worker count; 0 uses all available CPUs. |
|---|
| 6 | initialization_method | ball | emcee walker initialization method. |
|---|
| 7 | random_seed | 42 | Random seed; None uses a system-derived seed. |
|---|
| 8 | proposal_moves | de | Single emcee proposal move; move mixtures are not persisted in v1. |
|---|
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"📋 Bayesian fit results:\n"
]
},
{
"data": {
"text/html": [
" | Metric | Value |
|---|
| 1 | 🧪 Sampler | emcee |
|---|
| 2 | ❌ Overall status | failed |
|---|
| 3 | 💬 Engine message | emcee sampling completed |
|---|
| 4 | ⏱️ Fitting time (seconds) | 114.69 |
|---|
| 5 | 📏 Goodness-of-fit (reduced χ²) | 12.71 |
|---|
| 6 | 📏 R-factor (Rf, %) | 7.67 |
|---|
| 7 | 📏 R-factor squared (Rf², %) | 8.11 |
|---|
| 8 | 📏 Weighted R-factor (wR, %) | 8.57 |
|---|
| 9 | 📉 Best log-posterior | -1354.14 |
|---|
| 10 | 📊 Convergence status | failed |
|---|
| 11 | 📊 Max r-hat | 1.072 |
|---|
| 12 | 📊 Min ess bulk | 376.618 |
|---|
| 13 | 📊 Draws per chain | 501 |
|---|
| 14 | 📊 Chains | 16 |
|---|
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"📈 Committed parameters:\n"
]
},
{
"data": {
"text/html": [
" | datablock | category | entry | parameter | units | start | value | s.u. | change |
|---|
| 1 | tbti | atom_site | Tb | adp_iso | Ų | 0.5319 | 0.5309 | 0.0052 | 0.18 % ↓ |
|---|
| 2 | tbti | atom_site | Ti | adp_iso | Ų | 0.4776 | 0.4774 | 0.0084 | 0.03 % ↓ |
|---|
| 3 | tbti | atom_site | O1 | fract_x | | 0.3280 | 0.3280 | 0.0000 | 0.00 % ↓ |
|---|
| 4 | tbti | atom_site | O1 | adp_iso | Ų | 0.4504 | 0.4497 | 0.0044 | 0.15 % ↓ |
|---|
| 5 | tbti | atom_site | O2 | adp_iso | Ų | 0.2375 | 0.2347 | 0.0068 | 1.15 % ↓ |
|---|
| 6 | heidi | extinction | | radius | μm | 26.4607 | 26.4150 | 0.2736 | 0.17 % ↓ |
|---|
| 7 | heidi | linked_structure | | scale | | 2.9236 | 2.9213 | 0.0138 | 0.08 % ↓ |
|---|
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" • start = parameter value before sampling
• value = estimate written back to the project (best posterior sample)
• s.u. = standard uncertainty (one sigma), posterior standard deviation
• change = relative change from start, in %; ↑ = increase, ↓ = decrease
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"📊 Posterior distribution:\n"
]
},
{
"data": {
"text/html": [
" | datablock | category | entry | parameter | units | median | 95% CI | r-hat | ess bulk |
|---|
| 1 | tbti | atom_site | Tb | adp_iso | Ų | 0.5325 | [0.5229, 0.5432] | 1.060 | 471.6 |
|---|
| 2 | tbti | atom_site | Ti | adp_iso | Ų | 0.4783 | [0.4620, 0.4945] | 1.060 | 376.6 |
|---|
| 3 | tbti | atom_site | O1 | fract_x | | 0.3280 | [0.3280, 0.3281] | 1.060 | 410.4 |
|---|
| 4 | tbti | atom_site | O1 | adp_iso | Ų | 0.4509 | [0.4425, 0.4596] | 1.051 | 396.6 |
|---|
| 5 | tbti | atom_site | O2 | adp_iso | Ų | 0.2368 | [0.2241, 0.2508] | 1.044 | 436.8 |
|---|
| 6 | heidi | extinction | | radius | μm | 26.4967 | [25.9381, 27.0226] | 1.072 | 444.2 |
|---|
| 7 | heidi | linked_structure | | scale | | 2.9246 | [2.8984, 2.9529] | 1.056 | 424.7 |
|---|
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
" • median = 50th percentile of the marginal posterior
• 95% CI = 95% credible interval (2.5%-97.5%, asymmetric)
• r-hat = Gelman-Rubin diagnostic (good convergence: r-hat <= 1.01)
• ess bulk = bulk effective sample size (typically >= 400)
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"⚠️ r-hat > 1.01: Consider longer sampling, better initialization, or reparameterization.
⚠️ ess bulk < 400: Consider longer sampling or reparameterization.
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"project.display.fit.results()"
]
},
{
"cell_type": "markdown",
"id": "46",
"metadata": {},
"source": [
"The correlation and posterior-pair plots are complementary:\n",
"\n",
"- `plot_param_correlations` summarizes pairwise structure in a compact\n",
" matrix.\n",
"- `plot_posterior_pairs` shows marginal densities on the diagonal and\n",
" posterior contours off-diagonal. In this tutorial its title also\n",
" reminds you that the display region follows the `±1.5 × uncertainty`\n",
" bounds defined above, while numeric subplot ranges are omitted to\n",
" keep the grid readable."
]
},
{
"cell_type": "code",
"execution_count": 30,
"id": "47",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:41:44.894154Z",
"iopub.status.busy": "2026-06-30T22:41:44.893969Z",
"iopub.status.idle": "2026-06-30T22:41:44.916111Z",
"shell.execute_reply": "2026-06-30T22:41:44.915393Z"
}
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"project.display.fit.correlations()"
]
},
{
"cell_type": "code",
"execution_count": 31,
"id": "48",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:41:44.917521Z",
"iopub.status.busy": "2026-06-30T22:41:44.917371Z",
"iopub.status.idle": "2026-06-30T22:41:45.334718Z",
"shell.execute_reply": "2026-06-30T22:41:45.333742Z"
}
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"project.display.posterior.pairs()"
]
},
{
"cell_type": "markdown",
"id": "49",
"metadata": {},
"source": [
"The one-dimensional posterior distributions below make it easier to\n",
"inspect individual parameters in isolation, including asymmetry or\n",
"multimodality."
]
},
{
"cell_type": "code",
"execution_count": 32,
"id": "50",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:41:45.340163Z",
"iopub.status.busy": "2026-06-30T22:41:45.339964Z",
"iopub.status.idle": "2026-06-30T22:41:45.501339Z",
"shell.execute_reply": "2026-06-30T22:41:45.500546Z"
}
},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"project.display.posterior.distribution()"
]
},
{
"cell_type": "markdown",
"id": "51",
"metadata": {},
"source": [
"Finally, the posterior predictive plot propagates the sampled\n",
"parameter uncertainty into the calculated single-crystal reflection\n",
"intensities."
]
},
{
"cell_type": "code",
"execution_count": 33,
"id": "52",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-30T22:41:45.502857Z",
"iopub.status.busy": "2026-06-30T22:41:45.502697Z",
"iopub.status.idle": "2026-06-30T22:41:45.517924Z",
"shell.execute_reply": "2026-06-30T22:41:45.517181Z"
}
},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"project.display.posterior.predictive(expt_name='heidi')"
]
}
],
"metadata": {
"jupytext": {
"cell_metadata_filter": "-all",
"main_language": "python",
"notebook_metadata_filter": "-all"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.14.5"
}
},
"nbformat": 4,
"nbformat_minor": 5
}