A comprehensive Python package for analyzing small-angle scattering (SAS) data with 14 form factor models and 2 structure factor models, featuring residual-bootstrap uncertainty quantification and high-performance cross-platform C extensions. The scattering model kernels are adapted from SasView (BSD-3-Clause).
- 14 Form Factor Models: Sphere, Ellipsoid, Cylinder variants (core-shell, elliptical), Barbell, Fuzzy Sphere, Onion, Linear Pearls, Lamellar HG, Polymer Micelle, Pringle, Core Multi-Shell, and Paracrystal lattices (BCC, FCC)
- 2 Structure Factor Models: Hayter-MSA (charged particles) and Hard Sphere interactions
- Modular Architecture: Easy integration of new scattering models
- Windows MSVC Support: Automatic detection and configuration of Microsoft Visual C++ compiler
- Linux/macOS GCC: Optimized builds with proper RPATH configuration
- Shared Core Library: Single
libsas_corelibrary for common functions (Bessel, quadrature, polynomials) - 50-100x Speedup: High-performance C implementations vs. pure Python
- Residuals Bootstrapping: Comprehensive uncertainty quantification
- Robust Fitting: Multiple optimization algorithms with parameter bounds
- HDF5 Data Storage: Efficient batch processing and result management
- Cluster Computing: ETH HPC integration for large-scale analyses
- Automatic Build System: Smart detection of compilers and dependencies
- Extensible Design: Dictionary-driven parameter system for custom models
- Comprehensive Documentation: Detailed examples and API reference
- Python: 3.8 or higher
- C Compiler:
- Windows: Microsoft Visual C++ 14.0+ (Visual Studio Build Tools 2019/2022 or Visual Studio Community)
- Linux: GCC (via
build-essentialpackage) - macOS: Clang (via Xcode Command Line Tools)
- Operating System: Windows 10/11, Linux (Ubuntu/Debian/RHEL/CentOS), macOS 10.14+
git clone https://github.com/TobiasMKaufmann/ScatterBootstrap.git
cd ScatterBootstrap# Install build essentials and Python development headers
sudo apt update
sudo apt install build-essential python3-dev
# Install HDF5 libraries for full tables support
sudo apt install libhdf5-dev pkg-config# Install development tools
sudo yum groupinstall "Development Tools" # CentOS/RHEL
sudo dnf groupinstall "Development Tools" # Fedora
sudo yum install python3-devel # CentOS/RHEL
sudo dnf install python3-devel # Fedora
# HDF5 support
sudo yum install hdf5-devel # CentOS/RHEL
sudo dnf install hdf5-devel # Fedora# Install Xcode command line tools
xcode-select --install
# Install HDF5 via Homebrew
brew install hdf5Option 1: Visual Studio Build Tools (Recommended)
- Download and install Microsoft C++ Build Tools
- During installation, select:
- "Desktop development with C++"
- Ensure "MSVC v142+" and "Windows 10/11 SDK" are checked
- Restart your terminal/IDE after installation
Option 2: Visual Studio Community
Install Visual Studio Community with the "Desktop development with C++" workload.
Verification:
clShould show the MSVC compiler version. If not, the build system will automatically locate and configure it.
It's recommended to use a virtual environment:
# Create virtual environment
python3 -m venv venv
# Activate virtual environment
source venv/bin/activate # Linux/macOS
# OR
venv\Scripts\activate # WindowsThe build system automatically detects your platform and compiler, compiles all 16
C extensions (libsas_core plus 14 form factor and 2 structure factor models), and
installs scatterbootstrap in editable mode:
pip install --upgrade pip
pip install -e .[dev]What Gets Built:
libsas_core- Shared library with Bessel functions, quadrature, and utilities- Form Factors: sphere, ellipsoid, barbell, core_shell_cylinder, core_multi_shell, elliptical_cylinder, fuzzy_sphere, lamellar_hg, linear_pearls, onion, polymer_micelle, pringle, bcc_paracrystal, fcc_paracrystal
- Structure Factors: hayter_msa, hardsphere
Build System Features:
- Automatic MSVC detection and configuration on Windows
- Proper RPATH settings on Linux/macOS for library dependencies
- Intelligent function export detection
- Automatic discovery of new models (no
setup.pyedits required) - Parallel compilation of the model extensions (one job per CPU core;
override with
SCATTERBOOTSTRAP_BUILD_JOBS=N, or=1to build serially)
You can verify the build directly:
ls src/scatterbootstrap/lib/*.so src/scatterbootstrap/form_factors/*/*.so src/scatterbootstrap/structure_factors/*/*.so # Linux/macOS
dir src\scatterbootstrap\lib\*.pyd src\scatterbootstrap\form_factors\*\*.pyd src\scatterbootstrap\structure_factors\*\*.pyd # Windowspython -m scatterbootstrap.coreThis runs a small built-in demo (fits a noisy sphere form factor) and prints the recovered parameters, confirming the package and all C extensions loaded correctly.
The package includes comprehensive form factor implementations for various particle geometries:
| Model | Description | Key Parameters |
|---|---|---|
| sphere | Homogeneous sphere | sld, sld_solvent, radius |
| ellipsoid | Prolate/oblate ellipsoid | sld, sld_solvent, radius_polar, radius_equatorial |
| core_shell_cylinder | Cylinder with shell coating | core_sld, shell_sld, solvent_sld, radius, thickness, length |
| barbell | Dumbbell-shaped particles | sld, solvent_sld, radius_bell, radius, length |
| core_multi_shell | Multi-layer spherical shells | core_sld, core_radius, solvent_sld, n_shells, sld_array[N], thickness_array[N] |
| elliptical_cylinder | Cylinder with elliptical cross-section | radius_minor, r_ratio, length, sld, solvent_sld |
| fuzzy_sphere | Sphere with fuzzy interface | sld, sld_solvent, radius, fuzziness |
| lamellar_hg | Head-group bilayer | length_tail, length_head, sld, sld_head, sld_solvent |
| linear_pearls | Linear chain of spheres | radius, edge_sep, fp_num_pearls, pearl_sld, solvent_sld |
| onion | Multi-shell sphere (onion model) | sld_core, radius_core, sld_solvent, n_shells, sld_in[N], sld_out[N], thickness[N], A[N] |
| polymer_micelle | Star polymer micelle | ndensity, v_core, v_corona, solvent_sld, core_sld, corona_sld, radius_core, rg, d_penetration, n_aggreg |
| pringle | Hyperbolic paraboloid | radius, thickness, alpha, beta, sld, sld_solvent |
| bcc_paracrystal | Body-centered cubic lattice | dnn, d_factor, radius, sld, solvent_sld |
| fcc_paracrystal | Face-centered cubic lattice | dnn, d_factor, radius, sld, solvent_sld |
Interparticle interference effects for concentrated systems:
| Model | Description | Key Parameters |
|---|---|---|
| hayter_msa | Hayter-Penfold MSA for charged particles | radius_effective, volfraction, charge, temperature, saltconc, dielectconst |
| hardsphere | Hard sphere repulsion | radius_effective, volfraction |
All models follow a modular design:
- C Implementation: High-performance kernel in
src/scatterbootstrap/form_factors/<model>/<model>.c - Python Wrapper: Easy-to-use interface in
src/scatterbootstrap/form_factors/<model>/wrapper.py - Shared Core: Common functions (Bessel, quadrature) in
src/scatterbootstrap/lib/libsas_core - Explicit Selection: Models are selected by name, passed as
form_factor_model="..."/structure_factor_model="..."toform_factor(),structure_factor(),intensity(),fit_data(), etc. Usescatterbootstrap.list_form_factor_models()andscatterbootstrap.list_structure_factor_models()to see all available names.
Adding New Models: See the "Implementing Custom Models" section below for a complete guide.
Models are selected by name, passed directly as arguments to form_factor(),
structure_factor(), and intensity() — no source code edits required.
from scatterbootstrap import form_factor, structure_factor, intensity
import numpy as np
# Define scattering parameters as keyword arguments
q = 0.1 # scattering vector (Å⁻¹)
# Barbell form factor parameters
model_params = {
"sld": 4.0e-6, # barbell scattering length density
"solvent_sld": 1.0e-6, # solvent scattering length density
"radius_bell": 20.0, # bell radius (Å)
"radius": 10.0, # cylinder radius (Å)
"length": 50.0 # cylinder length (Å)
}
# Structure factor parameters (Hayter-MSA for charged particles)
structure_params = {
"radius_effective": 24.8, # effective radius (Å)
"volfraction": 0.16363, # volume fraction
"charge": 28.288, # particle charge
"temperature": 300, # temperature (K)
"saltconc": 0.093723, # salt concentration (M)
"dielectconst": 78.3 # dielectric constant
}
# Calculate form factor (returns F² directly)
F2 = form_factor(q, "barbell", **model_params)
# Calculate structure factor
S_q = structure_factor(q, "hayter_msa", **structure_params)
# Total scattering intensity with scaling
scale = 1.0
background = 0.001
I_q = intensity(q, scale, background, "barbell", "hayter_msa", **model_params, **structure_params)
print(f"Form Factor F²(q): {F2:.6e}")
print(f"Structure Factor S(q): {S_q:.4f}")
print(f"Total Intensity I(q): {I_q:.6e}")Switching Models: To use a different form factor (e.g., sphere, core_shell_cylinder,
ellipsoid) or structure factor (hardsphere), simply pass a different model name and the
matching parameters:
from scatterbootstrap import form_factor, list_form_factor_models, list_structure_factor_models
print(list_form_factor_models()) # all 14 available form factor model names
print(list_structure_factor_models()) # all 2 available structure factor model names
F2 = form_factor(q, "sphere", sld=4.0e-6, sld_solvent=1.0e-6, radius=50.0)Each model's required parameters are documented in its wrapper module, e.g.
scatterbootstrap.form_factors.sphere.wrapper.compute_form_factor.
The framework provides a complete pipeline for fitting experimental data and quantifying uncertainties through bootstrap resampling.
from scatterbootstrap import fit_data, residuals_bootstrap, compute_confidence_intervals, plot_fit_data
import numpy as np
# Choose the form factor and (optional) structure factor models by name
FORM_FACTOR_MODEL = "barbell"
STRUCTURE_FACTOR_MODEL = "hayter_msa" # set to None to fit form factor only
# Load experimental data
q_exp = np.loadtxt('path/to/data.dat', usecols=0) # q values (Å⁻¹)
I_exp = np.loadtxt('path/to/data.dat', usecols=1) # intensity values
# sigma_exp = np.loadtxt('path/to/data.dat', usecols=2) # uncertainties (optional)
# Define initial parameter guesses (customize for your model and data)
initial_params = {
"scale": 1.0,
"background": 0.001,
"sld": 4.0e-6,
"solvent_sld": 1.0e-6,
"radius_bell": 20.0, # Barbell-specific
"radius": 10.0,
"length": 50.0,
"radius_effective": 24.8, # Structure factor
"volfraction": 0.16,
"charge": 28.0,
"temperature": 300.0,
"saltconc": 0.1,
"dielectconst": 78.3
}
# Define which parameters to fit (True) vs. keep fixed (False)
fit_params = {
"scale": True,
"background": True,
"sld": True,
"solvent_sld": False, # Often kept fixed
"radius_bell": True,
"radius": True,
"length": True,
"radius_effective": True,
"volfraction": True,
"charge": True,
"temperature": False, # Usually kept fixed
"saltconc": True,
"dielectconst": False # Usually kept fixed
}
# Step 1: Fit experimental data
print("Fitting data...")
fitted_params, covariance, param_order = fit_data(
q_exp, I_exp,
form_factor_model=FORM_FACTOR_MODEL,
structure_factor_model=STRUCTURE_FACTOR_MODEL,
initial_params=initial_params,
fit_params=fit_params
)
# Convert fitted parameters array back to dictionary
fitted_dict = {key: fitted_params[i] for i, key in enumerate(param_order)}
# Add fixed parameters
for key, value in initial_params.items():
if key not in fitted_dict:
fitted_dict[key] = value
print("Fitted parameters:")
for key, value in fitted_dict.items():
if fit_params.get(key, False):
print(f" {key}: {value:.6e}")
# Step 2: Bootstrap uncertainty quantification
print("\nPerforming bootstrap analysis...")
bootstrap_results = residuals_bootstrap(
q_exp, I_exp,
form_factor_model=FORM_FACTOR_MODEL,
structure_factor_model=STRUCTURE_FACTOR_MODEL,
all_params=fitted_dict, # Use fitted values as starting point
fit_params=fit_params,
n_iterations=1000, # Increase for better statistics
rng=42, # Optional seed for reproducibility
n_jobs=-1, # Use all CPU cores (1 = serial, N = N processes)
)
# Step 3: Compute confidence intervals
confidence_intervals = compute_confidence_intervals(bootstrap_results, confidence_level=0.95)
print("\n95% Confidence Intervals:")
for param, (lower, upper) in confidence_intervals.items():
mean_val = np.mean([r[param] for r in bootstrap_results])
print(f" {param}: {mean_val:.6e} [{lower:.6e}, {upper:.6e}]")
# Step 4: Visualize results
print("\nGenerating plots...")
plot_fit_data(
q_exp, I_exp,
params=fitted_dict,
form_factor_model=FORM_FACTOR_MODEL,
structure_factor_model=STRUCTURE_FACTOR_MODEL,
title="Barbell Model Fit",
folder="./results", # Output directory
)
print("Analysis complete! Results saved to ./results/")Key Functions:
fit_data(): Non-linear least squares fitting using scipy's Levenberg-Marquardt algorithmresiduals_bootstrap(): Resamples residuals to quantify parameter uncertainties (parallel vian_jobs)fit_bootstrap_many(): Fits + bootstraps a whole collection of datasets in parallelcompute_confidence_intervals(): Calculates percentile-based confidence intervalsplot_fit_data(): Creates publication-ready plots with fit overlays
fit_bootstrap_many() runs the full fit → bootstrap → confidence interval
pipeline for many datasets and parallelizes the work across CPU cores. It
budgets cores automatically (parallelizing across datasets, or — for a single
dataset — across that dataset's bootstrap) so it never oversubscribes, and it is
reproducible: a given rng seed yields the same result regardless of n_jobs.
import numpy as np
import scatterbootstrap as sb
# name -> (q, I) for each measurement
datasets = {
"sample_A": (q_A, I_A),
"sample_B": (q_B, I_B),
"sample_C": (q_C, I_C),
}
initial_params = {"scale": 1.0, "background": 0.001,
"sld": 4e-6, "sld_solvent": 1e-6, "radius": 50.0}
fit_params = {"scale": True, "background": True, "sld": False,
"sld_solvent": False, "radius": True}
results = sb.fit_bootstrap_many(
datasets,
form_factor_model="sphere",
initial_params=initial_params, # or a {name: params} mapping per dataset
fit_params=fit_params,
n_iterations=1000,
rng=42,
n_jobs=-1, # use all cores across datasets
)
for name, res in results.items():
lo, hi = res["confidence_intervals"]["radius"]
print(f"{name}: radius = {res['fitted_params']['radius']:.2f} 95% CI [{lo:.2f}, {hi:.2f}]")Each results[name] is a dict with fitted_params, param_order,
covariance, confidence_intervals, and (unless keep_ensembles=False) the
full bootstrap ensemble. Pass initial_params/fit_params/bounds either as
a single shared config or as a {name: ...} mapping for per-dataset settings.
macOS/Windows: call
fit_bootstrap_many(andresiduals_bootstrapwithn_jobs != 1) from inside anif __name__ == "__main__":guard, as required by Python'smultiprocessing. On Linux this is not needed.
Performance Tips:
- Start with
n_iterations=100for testing, increase to 1000-5000 for final analysis - Parallelize the bootstrap with
n_jobs: the refits are independent, son_jobs=-1(all cores) gives a near-linear speedup. Results are identical to a serial run for the samerngseed, regardless ofn_jobs. - Use the cluster computing framework (
src/cluster/) for large bootstrap analyses (it automatically setsn_jobsfrom$SLURM_CPUS_PER_TASK) - Set
structure_factor_model=Noneif structure factor effects are negligible
ScatterBootstrap/
├── src/
│ ├── scatterbootstrap/ # The installable Python package
│ │ ├── __init__.py # Public API (START HERE)
│ │ ├── core.py # Main analysis functions
│ │ ├── _lib_finder.py # Locates compiled .so/.pyd/.dll files
│ │ │
│ │ ├── lib/ # Shared C library components
│ │ │ ├── libsas_core.so/.pyd/.dll # Compiled shared library
│ │ │ ├── sas_J1.c/h # Bessel J1 functions
│ │ │ ├── sas_J0.c/h # Bessel J0 functions
│ │ │ ├── sas_JN.c/h # Bessel Jn functions
│ │ │ ├── sas_3j1x_x.c # 3*j1(x)/x function
│ │ │ ├── gauss76.c/h # 76-point Gauss quadrature
│ │ │ ├── gauss150.c/h # 150-point Gauss quadrature
│ │ │ ├── polevl.c/h # Polynomial evaluation
│ │ │ ├── utils.c/h # Math utilities (SINCOS, etc.)
│ │ │ └── sas_core.h # Common header and constants
│ │ │
│ │ ├── form_factors/ # Form factor models (14 models)
│ │ │ ├── __init__.py
│ │ │ ├── sphere/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── wrapper.py # Python interface
│ │ │ │ ├── sphere.c # C implementation
│ │ │ │ └── sphere.so/.pyd/.dll # Compiled library
│ │ │ ├── ellipsoid/ # [Same structure]
│ │ │ ├── core_shell_cylinder/ # [Same structure]
│ │ │ ├── barbell/ # [Same structure]
│ │ │ ├── core_multi_shell/ # [Same structure]
│ │ │ ├── elliptical_cylinder/ # [Same structure]
│ │ │ ├── fuzzy_sphere/ # [Same structure]
│ │ │ ├── lamellar_hg/ # [Same structure]
│ │ │ ├── linear_pearls/ # [Same structure]
│ │ │ ├── onion/ # [Same structure]
│ │ │ ├── polymer_micelle/ # [Same structure]
│ │ │ ├── pringle/ # [Same structure]
│ │ │ ├── bcc_paracrystal/ # [Same structure + sphere_form.c]
│ │ │ └── fcc_paracrystal/ # [Same structure + sphere_form.c]
│ │ │
│ │ └── structure_factors/ # Structure factor models (2 models)
│ │ ├── __init__.py
│ │ ├── hayter_msa/
│ │ │ ├── __init__.py
│ │ │ ├── wrapper.py # Python interface
│ │ │ ├── hayter_msa.c # C implementation
│ │ │ └── hayter_msa.so/.pyd/.dll # Compiled library
│ │ └── hardsphere/ # [Same structure]
│ │
│ ├── data_extraction_functions.py # Bootstrap results analysis tools
│ ├── data_extraction_example.py # Usage examples for data extraction
│ ├── fitted_params_table.py # Parameter validation utilities
│ ├── plot_comparisons.py # Visualization and comparison tools
│ ├── initial_params.json # Default parameter configurations
│ │
│ └── cluster/ # ETH HPC cluster computing framework
│ ├── README.md # Complete cluster workflow guide
│ ├── process_data.py # Batch bootstrap processing
│ ├── submit_job.sh # SLURM job submission script
│ ├── setup_cluster.py # Dependency installation for cluster
│ ├── transfer.sh # File transfer and job management
│ └── requirements_cluster.txt # Minimal cluster dependencies
│
├── tests/ # pytest test suite
│
├── requirements.txt # Python dependencies
├── setup.py # Cross-platform build system with MSVC support
├── pyproject.toml # Modern Python package configuration
├── MANIFEST.in # Package manifest for distribution
├── LICENSE # MIT License
├── CHANGELOG.md # Version history and changes
├── EXAMPLE_ADDING_NEW_MODEL.md # Guide for adding new models
└── README.md # This file
Key Directories:
src/scatterbootstrap/lib/: Shared C library used by all modelssrc/scatterbootstrap/form_factors/: Individual form factor implementations (each in its own subdirectory)src/scatterbootstrap/structure_factors/: Structure factor implementationssrc/cluster/: HPC cluster tools for large-scale analysistests/: Automated test suite (run withpytest)
For computationally intensive bootstrap analyses with thousands of iterations, this package includes a complete cluster computing framework optimized for ETH HPC systems (Euler/Leonhard).
# Navigate to cluster directory
cd src/cluster
# Edit transfer.sh with your NetHz credentials
nano transfer.sh # Set CLUSTER_USER="your_nethz"
# Upload files and submit SLURM job
./transfer.sh to
# Monitor job status
./transfer.sh status
./transfer.sh check
# Download results when complete
./transfer.sh retrieve- Automated Workflow: Single command to upload, build, and submit jobs
- SLURM Integration: Optimized resource allocation and job scheduling
- Remote Monitoring: Check status, view logs, and track progress remotely
- Intelligent File Transfer: Automatic exclusion of unnecessary files
- Batch Processing: Process multiple datasets with 5000+ bootstrap iterations
- HDF5 Output: Efficient storage of all results in structured format
to- Upload and submit jobretrieve- Download results (PRIMARY METHOD)status- Check job queuecheck- Comprehensive status with logslogs- List available log filesviewlog JOBID- View specific job logsclean- Remove temporary fileslist- List all cluster filesdelete- Remove all files from cluster
- Prepare: Edit
initial_params.jsonwith your parameters - Upload:
./transfer.sh tosubmits job to SLURM - Monitor:
./transfer.sh checktracks progress - Retrieve:
./transfer.sh retrievedownloads HDF5 results - Analyze: Use
data_extraction_functions.pyfor analysis
Each dataset generates an HDF5 file with:
- Original experimental data (q, I)
- Initial and fitted parameters
- All bootstrap samples (5000 by default)
- 95% confidence intervals
- Metadata (dataset name, processing stage)
For complete documentation, see src/cluster/README.md.
- numpy (≥1.20.0): Numerical computations
- scipy (≥1.7.0): Scientific computing and optimization
- matplotlib (≥3.3.0): Plotting and visualization
- pandas (≥1.3.0): Data manipulation and analysis
- cffi (≥1.14.0): C Foreign Function Interface
- tables (≥3.6.0): HDF5 data storage support
- tqdm: Progress bars for long computations
error: Microsoft Visual C++ 14.0 or greater is required
Solution:
- Install Visual Studio Build Tools
- Select "Desktop development with C++" workload
- Restart terminal after installation
- The build system automatically searches these locations:
C:\BuildToolsC:\Program Files (x86)\Microsoft Visual Studio\2022\BuildToolsC:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools
Advanced: If cl.exe is not in PATH, setup.py will automatically configure the MSVC environment.
error LNK2001: unresolved external symbol
Solution:
- Ensure Windows SDK is installed (included with Build Tools)
- Verify
libsas_core.libandlibsas_core.pydexist insrc/scatterbootstrap/lib/ - Rebuild:
python setup.py clean --all && pip install -e .
error: Could not find HDF5 installation
Solution: Install HDF5 development libraries:
sudo apt install libhdf5-dev pkg-config # Ubuntu/Debian
sudo dnf install hdf5-devel # Fedora
sudo yum install hdf5-devel # CentOS/RHELxcrun: error: invalid active developer path
Solution:
xcode-select --installPermissionError: [Errno 13] Permission denied
Solution: Use virtual environment (recommended) or:
pip install --user -e .ImportError: cannot import name 'form_factor'
Solution:
- Verify C extensions built:
ls src/scatterbootstrap/form_factors/sphere/*.so(Linux/macOS) ordir src\scatterbootstrap\form_factors\sphere\*.pyd(Windows) - Rebuild:
pip install -e . - Check for compilation errors in build output
CMD.exe does not support UNC paths as current directories
Solution: Map network drive to a letter (e.g., Z:\) or work from local directory.
Check Build Status:
# Verify the package imports and lists all compiled models
python -c "import scatterbootstrap as sb; print(sb.list_form_factor_models()); print(sb.list_structure_factor_models())"
# List built libraries
ls src/scatterbootstrap/lib/*.so src/scatterbootstrap/form_factors/*/*.so src/scatterbootstrap/structure_factors/*/*.so # Linux/macOS
dir src\scatterbootstrap\lib\*.pyd src\scatterbootstrap\form_factors\*\*.pyd src\scatterbootstrap\structure_factors\*\*.pyd # WindowsClean and Rebuild:
# Remove compiled artifacts, then rebuild from scratch
python setup.py clean --all
pip install -e .- C extensions provide 50-100x speedup over pure Python implementations
- For large datasets, consider using the cluster processing scripts in
src/cluster/ - Bootstrap analysis can be computationally intensive; start with smaller sample sizes for testing
The package ships with an automated pytest suite that builds and exercises
every compiled model plus the fitting and bootstrap pipeline:
pip install -e .[dev]
pytest # run all tests
pytest --cov=scatterbootstrap # with a coverage reportContributions are welcome — see CONTRIBUTING.md for the full guide and CODE_OF_CONDUCT.md for community expectations. Bug reports and questions go to the issue tracker.
# Format and lint before opening a pull request
black src/scatterbootstrap tests
flake8 src/scatterbootstrap tests --max-line-length=100 --extend-ignore=E203,W503Models are auto-discovered and auto-compiled: dropping a new model directory
into the package is all that is required — no edits to setup.py or core.py.
-
Create the directory
src/scatterbootstrap/form_factors/<name>/(usestructure_factors/for a structure factor) containing:<name>.cand<name>.h— the C kernel,wrapper.py— the thin Python interface,__init__.py.
-
Write the C kernel. Form factor kernels expose an
Fq(...)function and may reuse the shared numerical core (#include "../../lib/sas_core.h"for Bessel functions, Gauss–Legendre quadrature, etc.). Structure factor kernels expose anIq(...)function. Keep the exported functions non-static./* sphere.c — adapted from SasView (https://www.sasview.org/) */ #include "../../lib/sas_core.h" void Fq(double q, double *f1, double *f2, double sld, double sld_solvent, double radius) { const double bes = sas_3j1x_x(q * radius); const double contrast = sld - sld_solvent; const double volume = M_4PI_3 * radius * radius * radius; const double fq = contrast * volume * bes; *f1 = 1.0e-2 * fq; /* <F> */ *f2 = 1.0e-4 * fq * fq; /* <F^2> */ }
-
Write
wrapper.pyexposingcompute_form_factor(q, **params)(orcompute_structure_factor). Usefind_libraryto locate the compiled binary in a platform-independent way:import ctypes, os import numpy as np from ..._lib_finder import find_library _lib = ctypes.CDLL(find_library("sphere", os.path.dirname(__file__))) _lib.Fq.restype = None _lib.Fq.argtypes = [ctypes.c_double, ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double), ctypes.c_double, ctypes.c_double, ctypes.c_double] def compute_form_factor(q, sld, sld_solvent, radius, **kwargs): q = np.atleast_1d(q).astype(float) out = np.zeros_like(q) f1, f2 = ctypes.c_double(), ctypes.c_double() for i, qv in enumerate(q): _lib.Fq(qv, ctypes.byref(f1), ctypes.byref(f2), sld, sld_solvent, radius) out[i] = f2.value return out
__init__.py:from .wrapper import compute_form_factor __all__ = ["compute_form_factor"]
-
Rebuild and test.
pip install -e .compiles the new kernel, and the model is immediately available by name:import scatterbootstrap as sb print(sb.list_form_factor_models()) # your model now appears sb.form_factor(0.1, "<name>", sld=4e-6, sld_solvent=1e-6, radius=50)
Add a representative parameter set to
tests/conftest.pyso the smoke tests cover your model, then runpytest.
- Modular C libraries — each model is a separate shared library.
- Shared core — reuse
libsas_corefor Bessel functions and quadrature. - Uniform interface — every wrapper exposes a
compute_*function takingqand**params. - Name-based selection — models are chosen at call time, never by editing source.
- Auto-discovery — the build system finds and compiles all models.
See EXAMPLE_ADDING_NEW_MODEL.md for a fuller
walkthrough and the existing sphere/hardsphere models for reference.
MIT License - see LICENSE file for details.
Note: The scattering model kernels in src/scatterbootstrap/form_factors/ and
src/scatterbootstrap/structure_factors/, and the shared numerical core in
src/scatterbootstrap/lib/, are adapted from SasView
and are subject to SasView's BSD 3-Clause License. All C source files contain
appropriate attribution headers.
If you use this software in your research, please cite it using the metadata in CITATION.cff, or:
@software{scatterbootstrap,
title = {ScatterBootstrap: Small-angle scattering model fitting with
bootstrap-based uncertainty quantification},
author = {Kaufmann, Tobias},
year = {2025},
url = {https://github.com/TobiasMKaufmann/ScatterBootstrap},
version = {0.4.0}
}- Author: Tobias Kaufmann
- Email: tkaufman@student.ethz.ch
- Repository: https://github.com/TobiasMKaufmann/ScatterBootstrap
Note: This package is designed for scientific research in small-angle scattering
analysis. The main analysis API lives in scatterbootstrap.core (re-exported from
the top-level scatterbootstrap package) and provides tools for computing
scattering intensities, fitting, bootstrapping, and uncertainty analysis.