Internals API#
The modules behind the public API, for contributors and gateway authors.
None of this is the public API: use powderline.run
and the powderline command to run recipes.
Command line#
The powderline command (reference). The pixi task
kicker runs the same main. The module imports no engine.
- powderline.kicker.main(argv: list[str] | None = None) int[source]#
Entry point of the
powderlinecommand; returns the exit status.
- powderline.kicker.is_template_file(recipe_dict: dict, input_path: Path) tuple[bool, str | None][source]#
Detect a recipe file that is a template and must not be run directly (CLI-only guard).
Two-level detection:
the path contains “template” (case-insensitive);
the payload is missing, or lacks the fields the workflow needs: a
*.rietveldrecipe needsxrd_data,instrument,phasesandrefinement_controls; a*.spfonesingle_peaksinstead ofphases; any other schema name is a template only when all ofxrd_data,instrumentandrefinement_controlsare absent.
- Returns:
(is_template, reason);(False, None)for a runnable recipe.
Examples
>>> is_template_file({}, Path("example_template/input.json")) (True, "filename or path contains 'template'") >>> is_template_file({"schema_name": "gsasii.rietveld", "payload": {"xrd_data": {...}, ...}}, Path("in.json")) (False, None)
Doctor#
powderline --doctor’s report. report() returns a dict with the keys
powderline, python, platform, gateways, server and problems. Each
entry of gateways has workflows, engine_version_spec,
engine_installed, engine_status and problem. format_report() renders
the dict, and main() prints it and returns 1 when problems is not empty.
The module imports no engine; it imports the gateway modules when the report
runs. The stale-install case it detects is KI-13.
Gateway registry#
Gateways are discovered from the powderline.gateways entry-point group.
These are the hooks an out-of-tree engine uses.
- class powderline.registry.Gateway(name: str, capabilities: Callable[[], Dict[str, Any]], validate: Callable[[...], Any], run: Callable[[...], dict])[source]#
A gateway’s contract.
name: registry name ("gsasii","topas","easydiffraction").capabilities(): dict with at leastname,workflowsandengine_version_spec(a PEP 440 specifier string).validate(recipe, *, verbose=False): schema layer, engine-free. Takes a recipe dict or an already-validated model, raises on an invalid recipe, returns the validated model. The same explicit keywords on every gateway; new options are added to the contract deliberately.run(recipe, output_dir, **opts): runtime layer; imports the engine lazily; returns the standardized result dict. Withvalidate_only=Trueit returns thevalidate_only_summarydict (the same keys on every gateway).
- powderline.registry.available() list[str][source]#
Sorted names of every registered gateway (explicit and entry-point).
- powderline.registry.get(name: str) Gateway[source]#
Return the gateway registered as
name, loading its factory on first use.- Raises:
GatewayNotInstalledError – no gateway of that name is registered at all. When the entry-point group is empty the message says the install metadata is stale (the in-repo gateways are always declared).
- powderline.registry.register_gateway(name: str, factory: Callable[[], Gateway]) None[source]#
Register a gateway factory explicitly (tests/dev escape hatch).
Takes precedence over an entry point of the same name.
- powderline.registry.check_engine_version(*, gateway: str, engine: str, installed, spec: str, allow_unsupported: bool = False) None[source]#
Raise
EngineVersionErrorunlessinstalledsatisfiesspec.A hard error.
allow_unsupported=Trueis the explicit test-only bypass for compatibility testing of candidate engine versions; gateways expose it asallow_unsupported_engine_versionon theirrun().
Gateway run functions#
Each gateway’s run is what powderline.run forwards to, after it has
validated the recipe and chosen the gateway. Their keyword options are exactly
the engine options powderline.run accepts for that engine.
Warning
Run recipes with powderline.run or the powderline command, not with these.
Calling a gateway’s run directly skips what powderline.run does on every
engine: PowderLine’s fixed-name outputs are not cleared first, and a failed
run writes no failure.json.
- powderline.gateways.gsasii.gateway.run(recipe, output_dir, *, verbose: bool = False, validate_only: bool = False, execution_mode: Literal['auto', 'server', 'subprocess'] = 'auto', server_timeout: float | None = None, allow_unsupported_engine_version: bool = False) dict[source]#
Execute a GSAS-II refinement from an in-memory recipe.
The full pipeline: schema validation, the engine version check, output directory creation, and refinement execution with automatic backend selection. Reached through
powderline.run()for agsasii.*recipe, which forwardsexecution_mode.Note: the CLI’s template-file guard is not applied here. When calling programmatically the caller is responsible for ensuring the recipe contains real data in
payload.xrd_data.- Parameters:
recipe – a recipe dict or a validated model. XRD data must already be present in
payload.xrd_data.output_dir – directory where output files will be written (
dummy.gpx,dummy.lst, CSV reports,fit_profile.txt). Created if it does not exist.verbose – if True, print detailed progress to stdout.
validate_only – if True, validate the recipe and return a summary dict without running a refinement. No output files are written and the engine version is not checked.
execution_mode –
the backend used to execute the refinement:
'auto'(default)Try the persistent GSAS-II server first (auto-starting it if needed); fall back to in-process execution if unavailable.
'server'Use the server only. Fails with an error result if the server cannot be started.
'subprocess'Skip the server entirely; always run in this process. Suitable for batch/HPC workflows where each refinement is isolated.
Only the calling user’s own server is used (per-user state directory, signed requests and responses; v0.1.2). If the
POWDERLINE_NO_SERVERenvironment variable is truthy (1/true/yes/on),'auto'runs in-process without starting a server and'server'returns an error result.server_timeout – seconds to wait for the server’s answer to a submitted job (
'auto'/'server').None(default) or0: no limit, as for an in-process run. Past the limit the run raisesExecutionBackendErrortimeoutand never falls back in-process: the job is still running on the server (one job at a time), so a second run would double the work.allow_unsupported_engine_version – test-only bypass of the engine version check. Not reachable through
powderline.run().
- Returns:
Normal refinement (
validate_only=False) — theokenvelope (powderline.results); a failed run raises, see below:status("ok"),converged(bool, orNonefor a simulation and for single peak fitting),envelope_version("1"); there is nosuccesskeyrun_id(str) — UUID4 string uniquely identifying this runrwp(float | None) — final weighted-profile R factorelapsed_time(float) — wall-clock secondsmethod(str) —'server'or'subprocess'fit_profile(pd.DataFrame) — columns: two_theta, y_obs, y_weights, y_calc, y_diff, y_bkg, q_values, d_spacingsunit_cell_data(dict) —{phase_name: pd.DataFrame}with columns: parameter, value, esdpeak_list_data(dict) —{phase_name: pd.DataFrame}with columns: h, k, l, multiplicity, d_spacing, 2theta, …refined_parameters(pd.DataFrame) — 9 columns: parameter_name, descriptive_name, phase_name, phase_idx, atom_name, atom_idx, value, esd, category (empty DataFrame with all 9 columns when nothing was refined)spf_peaks(pd.DataFrame) — SPF peak report (empty for non-SPF runs)spf_convergence_diagnostics(pd.DataFrame) — per-peak diagnostics for peaks with convergence issues (empty when all converged or for non-SPF runs)peak_fit(dict | None) — SPF only:{"parameters": [{"name", "value", "esd"}]}, every parameter GSAS-II’s peak fit varied by its own name (Back;0,U,pos3…) with its ESD; the source of the output recipe for a peak fitoutput_files(list[str]) — paths written tooutput_dirr_exp,gof,chi2_red,simulation_mode,engine_detailsand the structuredwarningslist
Validate-only (
validate_only=True) — the summary every gateway returns (powderline.registry.validate_only_summary): no refinement executed, no output files written, norun_id;engineholdsrefinement_cycles.- Raises:
pydantic.ValidationError – the recipe fails schema validation.
ValueError – an invalid
execution_modeorserver_timeout.EngineNotAvailableError – GSAS-II is not importable (not for
validate_only).EngineVersionError – the installed GSAS-II is outside
ENGINE_VERSION_SPEC.EngineExecutionError – the engine ran and failed (
.reason:diverged,unusable_site,peak_fit_failed,engine_crashed;.resultis the failed envelope listing the engine’s own files written so far, which are kept).ExecutionBackendError – the engine did not run (
.reason:server_unavailable,server_disabled,communication_failed,timeout,backend_error).OSError –
output_dircannot be created.
- powderline.gateways.topas.gateway.run(recipe, output_dir, *, verbose: bool = False, validate_only: bool = False, topas_dir=None, topas_version=None, topas_timeout=None, allow_unsupported_engine_version: bool = False) dict[source]#
Run a recipe; see
native_run.run_native()for the result dict.topas_dir/topas_version: the TOPAS installation and its version (elsetopas.dir/topas.versionin.powderline_config.yaml).topas_timeout: the limit on each tc.exe run in seconds (elsetopas.timeout, else 3600; 0 = no limit); past it the run fails withExecutionBackendErrortimeout.allow_unsupported_engine_versionis the test-only bypass of the engine version check; the check is skipped forvalidate_onlyand when notc.exeis found (generate-only).
- powderline.gateways.easydiffraction.gateway.run(recipe, output_dir, *, verbose: bool = False, validate_only: bool = False, allow_unsupported_engine_version: bool = False) dict[source]#
Run a recipe; see
native_run.run_native()for the result dict.allow_unsupported_engine_versionis the test-only bypass of the engine version check; the check is skipped forvalidate_only.
GSAS-II gateway helpers#
These functions belong to the frozen GSAS-II extraction and setup code
(powderline.gateways.gsasii.extractors and .setters).
Output generation#
- powderline.gateways.gsasii.extractors.export_refined_parameters_csv(param_dict: Dict[str, Dict[str, Any]], output_file: Path, proj: Any = None, include_category: bool = True) DataFrame | None[source]#
Export refined parameters to CSV file and return the DataFrame.
- Parameters:
param_dict – Dictionary from extract_refined_params_* functions
output_file – Path to output CSV file
proj – GSAS-II project object (optional, needed for phase/atom names)
include_category – If True, add ‘category’ and ‘descriptive_name’ columns (default: True)
- Returns:
DataFrame with the exported parameters, or None if param_dict is empty.
- Output CSV / DataFrame columns:
parameter_name: GSAS-II internal parameter name
descriptive_name: Human-readable parameter description (if include_category=True)
phase_name: Name of associated phase (None if not phase-specific)
phase_idx: Index of associated phase (None if not phase-specific)
atom_name: Label of associated atom (None if not atom-specific)
atom_idx: Index of associated atom (None if not atom-specific)
value: Refined value
esd: Estimated standard deviation (None for fixed parameters)
category: Parameter category (instrument, background, cell, etc.) if include_category=True
- powderline.gateways.gsasii.extractors.calculate_cell_esds_from_A_matrix(phase_idx: int, proj: Any, phase_name: str) List[float | None][source]#
Calculate unit cell parameter ESDs using GSAS-II’s reciprocal metric tensor conversion.
GSAS-II refines reciprocal metric tensor components (A-matrix: A11, A22, A33, A12, A13, A23) and this function converts those ESDs to direct lattice parameter ESDs (a, b, c, α, β, γ).
- Parameters:
phase_idx – Phase index (0-based) for parameter naming (e.g., “0::A0”)
proj – GSAS-II project object after refinement
phase_name – Name of the phase
- Returns:
The 7 ESDs, in order
[esd_a, esd_b, esd_c, esd_alpha, esd_beta, esd_gamma, esd_volume].- Return type:
- Raises:
RuntimeError – If covariance data is unavailable or the ESD calculation fails.
Note
The A-matrix parameters (A0-A5) in GSAS-II correspond to: A0=A11, A1=A22, A2=A33, A3=A12, A4=A13, A5=A23 (reciprocal metric tensor) NOT direct cell parameters a, b, c, α, β, γ.
- powderline.gateways.gsasii.extractors.extract_refined_params_from_project(proj: Any, verbose: bool = False) Dict[str, Dict[str, Any]][source]#
Extract all refined parameters with their values and ESDs from proj.data.
Uses the covariance data stored in proj.data[‘Covariance’][‘data’] after refinement. This includes both independent and dependent parameters (via ComputeDepESD).
- Parameters:
proj – GSAS-II project object after refinement
verbose – If True, print progress messages.
- Returns:
Mapping of parameter names to
{"value": float, "esd": float}. Returns an empty dict if no covariance data is available.- Return type:
- Raises:
RuntimeError – If ComputeDepESD fails (dependent parameter ESDs cannot be calculated).
Example
>>> params = extract_refined_params_from_project(proj) >>> params[":0:U"] {"value": 47.406, "esd": 8.314}
Note
ESDs are read from GSAS-II’s covariance matrix (proj.data['Covariance']).
Unit-cell ESDs are converted from the reciprocal metric tensor (the A-matrix)
to the direct lattice parameters by calculate_cell_esds_from_A_matrix().
Post-refinement extraction helpers (private)#
The four private _extract_* helpers hold the post-refinement extraction and
file-writing logic.
- powderline.gateways.gsasii.extractors._extract_fit_profile(hist: Any, output_dir: Path) dict[source]#
Extract fit profile arrays from histogram and save fit_profile.txt.
- Parameters:
hist – GSAS-II histogram object after refinement.
output_dir – Directory to write
fit_profile.txt.
- Returns:
Column-oriented data (JSON-serializable) with keys
two_theta,y_obs,y_weights,y_calc,y_diff,y_bkg,q_values,d_spacings.- Return type:
- powderline.gateways.gsasii.extractors._extract_spf_peak_report(proj: Any, hist: Any, recipe: GsasiiRietveldRecipe | GsasiiSpfRecipe, output_dir: Path, verbose: bool) tuple[dict, dict][source]#
Extract single peak fitting results and save report files.
Called only for
gsasii.spfruns whererecipe.payload.single_peaksis set. Returns two column-oriented dicts (JSON-serializable) that are normalised to DataFrames byrun().Also writes: -
single_peaks_report.txt— per-peak widths and convergence status -peak_convergence_diagnostics.txt— only when peaks have issues- Parameters:
proj – GSAS-II project object after refinement.
hist – GSAS-II histogram object.
recipe – Validated
gsasii.spfrecipe model.output_dir – Directory to write report files.
verbose – If True, print convergence warnings to stdout.
- Returns:
(spf_peaks_data, spf_diagnostics_data)where each is a column-oriented dict, or({}, {})when single peaks are not used.
- powderline.gateways.gsasii.extractors._extract_phase_reports(proj: Any, hist: Any, recipe: GsasiiRietveldRecipe | GsasiiSpfRecipe, param_dict: dict, output_dir: Path) tuple[dict, dict][source]#
Extract unit cell and peak list reports for all phases.
Writes
{phase}_unit_cell_report.csvand{phase}_peak_list_report.csvfor each phase.- Parameters:
proj – GSAS-II project object after refinement.
hist – GSAS-II histogram object.
recipe – Validated
gsasii.rietveld/gsasii.spfrecipe model (used to check if phases exist).param_dict – Refined parameter dict from
extract_refined_params_from_project()(needed for cell ESDs).output_dir – Directory to write CSV files.
- Returns:
(unit_cell_data, peak_list_data)— each is a{phase_name: list-of-records}dict (JSON-serializable).
- powderline.gateways.gsasii.extractors._extract_refined_parameters(param_dict: dict, output_dir: Path, proj: Any, verbose: bool) list[source]#
Export refined parameters to CSV and return as list-of-records.
- Parameters:
param_dict – From
extract_refined_params_from_project().output_dir – Directory to write
refined_parameters.csv.proj – GSAS-II project object (for phase/atom name mappings).
verbose – If True, print export status to stdout.
- Returns:
List of dicts (records) with the 9-column schema. Returns
[]whenparam_dictis empty (simulation mode, SPF, etc.).
Background functions#
- powderline.gateways.gsasii.setters.set_chebyshev_background(proj: Any, hist: Any, chebyshev_dict: dict, print_info: bool = False) None[source]#
Set the Chebyshev background for a given histogram.
Chebyshev polynomials provide smooth curved backgrounds. Coefficients start at 0th order (constant term) and increase: [c0, c1, c2, …] represents c0 + c1*T1(x) + c2*T2(x) + … where Tn are Chebyshev polynomials.
The function manipulates proj.data[hist.name][‘Background’][0] which has structure: [background_type, refine_flag, num_coefficients, c0, c1, c2, …]
- Parameters:
proj – GSAS-II project object containing the histogram
hist – Histogram object to set the background for
chebyshev_dict – Dictionary containing Chebyshev background parameters: - num_coefficients (int): Number of Chebyshev coefficients - coefficients (list[float]): Coefficient values [c0, c1, c2, …] - refine_flag (bool): Whether to refine background during fitting
print_info – If True, print background configuration to stdout
- Returns:
None
- Raises:
ValueError – If number of coefficients doesn’t match list length or required background entries are missing
Examples
>>> chebyshev_dict = { ... 'num_coefficients': 3, ... 'coefficients': [100.0, -50.0, 10.0], ... 'refine_flag': True ... } >>> set_chebyshev_background(proj, hist, chebyshev_dict)
- powderline.gateways.gsasii.setters.set_single_peak_background(proj: Any, hist: Any, bkg_single_peaks_dict: dict, print_info: bool = False) None[source]#
Set single peak background for histogram using pseudo-Voigt profiles.
Single peaks are useful for modeling known impurity peaks or other non-background features that shouldn’t be included in the main phase refinement. Each peak is described by a pseudo-Voigt profile (weighted sum of Gaussian and Lorentzian).
Peak profile: I(2θ) = intensity * [η*L(2θ) + (1-η)*G(2θ)] where: - G(2θ) is Gaussian with width sigma - L(2θ) is Lorentzian with width gamma - η (eta) is mixing parameter (0=pure Gaussian, 1=pure Lorentzian)
- Parameters:
proj – GSAS-II project object
hist – Histogram object to set single peaks for
bkg_single_peaks_dict – Dictionary with keys: - positions: List of [[2θ, refine], …] for peak positions - intensities: List of [[I, refine], …] for peak heights - pv_gaussian_sigma_sq: List of [[σ², refine], …] Gaussian variances, centideg² - pv_lorentzian_gamma: List of [[γ, refine], …] for Lorentzian widths All lists must have same length (number of peaks)
print_info – If True, print peak configuration to stdout
- Returns:
None
- Raises:
ValueError – If parameter lists have inconsistent lengths
Examples
>>> # Two single peaks at 2θ=35.5° and 42.0° >>> single_peaks = { ... 'positions': [[35.5, False], [42.0, False]], ... 'intensities': [[50.0, True], [30.0, True]], ... 'pv_gaussian_sigma_sq': [[0.1, False], [0.1, False]], ... 'pv_lorentzian_gamma': [[0.15, False], [0.15, False]] ... } >>> set_single_peak_background(proj, hist, single_peaks)