Python API#
The public API is the names in powderline.__all__, plus the result
envelope, the warning shape and the recipe helpers below.
import powderline
recipe = powderline.load_recipe_asset("examples/example_LaB6/input.json")
checked = powderline.validate(recipe) # no engine needed
result = powderline.run(recipe, "output/") # needs the recipe's engine
import powderline imports no engine. validate uses only each gateway’s
engine-free schema layer. run imports the engine the recipe’s schema_name
names, when it runs.
Every recipe field, with its unit: Fields.
Every key of the result: Outputs.
Using the API in scripts and pipelines: Use PowderLine from Python.
Run and validate#
- powderline.run(recipe, output_dir, *, verbose: bool = False, validate_only: bool = False, **engine_options) dict[source]#
Run a recipe with the engine its
schema_namenames; return the standardized result dict.The gateway is resolved from the recipe (
<engine>.<workflow>→powderline.registry.get("<engine>")); the recipe,output_dir,verboseandvalidate_onlyare handed to itsruntogether with**engine_options, unchanged and uninterpreted. Each gateway takes its options as explicit keywords, so an option it does not know is aTypeErrornaming it:gsasii:execution_mode('auto'|'server'|'subprocess') andserver_timeout(seconds to wait for the GSAS-II server’s answer; default none, as for an in-process run, 0 = none; past it the run raisesExecutionBackendErrortimeoutand does not fall back in-process, the job being still on the server);topas:topas_dir(the folder holdingtc.exe),topas_version(the installed TOPAS release, e.g."6"; a real run needs it declared, here or in.powderline_config.yaml) andtopas_timeout(seconds pertc.exerun, default 3600, 0 = no limit; past it the run raisesExecutionBackendErrortimeout);easydiffraction: none.
allow_unsupported_engine_versionis the gateways’ test-only bypass of the engine version check and is refused here (TypeError); call the gateway’srundirectly for compatibility testing.Before anything on disk changes, the recipe is validated and the gateway’s engine is checked to be importable here: an invalid recipe or a run in an environment without the engine raises and leaves the output directory’s previous results in place. Then PowderLine’s own fixed-name outputs are removed from
output_dir(the engine’s files and anything else stay), so a reused directory holds one run’s results.- Parameters:
recipe – a recipe dict or a validated model.
output_dir – directory for the output files; created by the gateway.
verbose – print the gateway’s progress.
validate_only – validate and return the gateway’s summary dict (no engine needed, nothing written).
- Returns:
The gateway’s
okorgeneratedenvelope (powderline.results). Every engine returns these keys:status("ok", or"generated"when TOPAS’s input was written but notc.exeis declared),converged(True/False/Nonewhen the engine cannot report it or nothing was refined),envelope_version,run_id,rwp,r_exp,gof,chi2_red,simulation_mode,engine_details,warnings,elapsed_time,method,output_files,fit_profile,unit_cell_data,peak_list_data,refined_parameters,output_recipe,output_recipe_valid,parameter_dispositions. GSAS-II and TOPAS results also carryspf_peaksandspf_convergence_diagnostics, and GSAS-II resultspeak_fit; the Outputs reference page lists every key. A failed run never returns: it raises (below).- Raises:
pydantic.ValidationError – the recipe is invalid, or
schema_nameis missing or malformed.GatewayNotInstalledError – no gateway of that name is installed.
EngineNotAvailableError – the gateway’s engine is not installed here.
EngineVersionError – the installed engine is outside the supported versions.
EngineExecutionError – the engine ran and failed.
.reasonis one ofdiverged,unusable_site,peak_fit_failed,engine_crashed,output_missing,output_malformed;.resultis the failed envelope, listing the files kept for diagnosis (the engine’s own files andfailure.json).ExecutionBackendError – the engine did not run.
.reasonis one ofserver_unavailable,server_disabled,communication_failed,timeout,backend_error.TypeError – an option the gateway does not take, or
allow_unsupported_engine_version.ValueError – a gateway option with an invalid value.
- powderline.validate(recipe, *, verbose: bool = False) ValidationResult[source]#
Validate a recipe with the gateway its
schema_namenames; return aValidationResult.Engine-free: only the gateway’s schema layer runs, so this works where no engine is installed. A recipe that is already a validated model passes through (idempotent). Nothing is written and nothing is printed unless
verbose(forwarded to the gateway).- Parameters:
recipe – a recipe dict (as loaded by
powderline.load_recipe_asset()) or a validated model.verbose – print the gateway’s validation progress, where it has any.
- Returns:
ValidationResult(recipe=<validated model>, warnings=[...]).- Raises:
pydantic.ValidationError – the recipe is invalid (every problem listed with its field location); also when
schema_nameis missing or malformed.GatewayNotInstalledError – no gateway of the name
schema_namestarts with is installed.
- class powderline.ValidationResult(recipe: ~typing.Any, warnings: list[~powderline.exceptions.StructuredWarning] = <factory>)[source]#
What
validate()returns: the validated recipe and its warnings.recipe: the gateway’s validated model (e.g.GsasiiRietveldRecipe,TopasRietveldRecipe). Validation never changes a recipe, so the model is the recipe as written plus the schema’s defaults.warnings: the structured warnings (StructuredWarningdicts:code,message,field_path) collected from that model once, in the same call – a default applied, a symmetry remark (Phase.warnings()).
The warnings come with the model because they describe the recipe as written (for example, a block it left out). The validated model states the default instead, so a dump of it cannot show them. The result is frozen: it records one validation. To change the recipe, edit the dict and validate again.
- warnings: list[StructuredWarning]#
- powderline.load_recipe_asset(recipe_path: Path) dict[source]#
Read a recipe file and return it as a dict, uninterpreted.
.jsonis parsed as JSON;.yaml/.yml/.txtas YAML (an empty YAML document gives{}). Files are read as UTF-8 (a leading BOM is accepted). The content is not validated: pass it topowderline.validate()orpowderline.run().- Raises:
ValueError – the suffix is none of the above (
Unsupported recipe format); the text is not valid JSON / YAML; or the document is not a mapping. The message starts with the file path.OSError – the file cannot be read.
- powderline.support_matrix() List[Dict[str, Any]][source]#
One row per registered gateway: its declarations plus core’s.
Loads each gateway’s engine-free factory through the registry; imports no engine. Undeclared gateways get
"declared": False. A gateway whose declarations are malformed (only some keys present) raisesValueErrornaming it, rather than being listed as undeclared. Consumed by the docs generation (scripts/gen_schema_field_tables.py).
Exceptions#
Every PowderLine error derives from PowderLineError. An invalid recipe
raises pydantic’s own ValidationError instead
(Validation errors).
- exception powderline.GatewayNotInstalledError(message: str, *, gateway: str)[source]#
No gateway of this name is registered (the gateway package is absent).
- exception powderline.EngineNotAvailableError(*, gateway: str, dependency: str, env: str, install_command: str)[source]#
The gateway is present, but its engine dependency is not installed here.
- exception powderline.EngineVersionError(*, gateway: str, engine: str, installed: str, supported: str, message: str | None = None)[source]#
The installed engine version is outside the gateway’s supported range.
- exception powderline.EngineExecutionError(message: str, *, reason: str | None = None, result: dict | None = None)[source]#
The engine ran and failed (envelope
failure.kind == "engine").- default_reason = 'engine_crashed'#
- exception powderline.ExecutionBackendError(message: str, *, reason: str | None = None, result: dict | None = None)[source]#
The engine did not run: the execution backend failed (
failure.kind == "backend") – the GSAS-II server was unavailable or disabled, its output is not visible here, the request timed out, or a configuration error stopped the run before the engine started.- default_reason = 'backend_error'#
The result envelope#
The exceptions carry the failed envelope as .result, and every gateway
builds its result with these functions. The module also defines
ENVELOPE_VERSION ("1"), STATUSES (ok, generated, failed), and
ENGINE_REASONS and BACKEND_REASONS, the
failure reasons.
The run-result envelope every gateway produces and every caller receives.
Errors are values on the wire and raised in the local API: a gateway (or the
GSAS-II server, or a future worker) builds a failed envelope with
failure_envelope(); raise_if_failed() turns it into
EngineExecutionError (the engine ran and
failed) or ExecutionBackendError (the engine
did not run) with the envelope on .result. An ok or generated
envelope is what powderline.run() returns; a failed one is never
returned. Engine-free.
- powderline.results.failure_envelope(kind: str, reason: str, message: str, *, run_id: str | None = None, elapsed: float = 0.0, method: str = 'none', warnings: list | None = None, output_files: list | None = None, traceback: str | None = None, engine_details: dict | None = None, simulation_mode: bool | None = None) dict[source]#
A
failedenvelope.kindis"engine"(the engine ran and failed) or"backend"(it did not run).output_fileslists the files kept for diagnosis: the engine’s own files written so far and, oncepowderline.run()has recorded the failure,failure.json. PowderLine’s result tables are never written for a failed run.
- powderline.results.generated_envelope(*, run_id: str | None = None, elapsed: float, method: str, warnings: list, output_files: list, simulation_mode: bool, engine_details: dict) dict[source]#
A
generatedenvelope: the engine’s input was written but the engine did not run (TOPAS without tc.exe).
Structured warnings#
The shape of each entry in result["warnings"] and
ValidationResult.warnings. The codes are on
Messages.
- class powderline.exceptions.StructuredWarning[source]#
One entry of the
warningslist in results andvalidate()output.field_pathis the dotted recipe path the warning refers to, orNonewhen it is not tied to one field. Every gateway carries these into its results.levelis"warning"– something the user should act on or decide about (a default applied, a parameter at a limit) – or"info"– a record of something PowderLine did that changes no outcome, such as a coordinate held at its exact symmetry value. Absent means"warning"; consumers read it asw.get("level", "warning"). The CLI prints warnings and only counts info entries unless--verbose; a GUI can show both.
Recipe helpers#
Move a core block (payload.xrd_data, payload.fit_range or
payload.phases.<name>) from one recipe to another. This is how a series of
runs is chained (Run a series).
- powderline.recipes.block(recipe: dict, path: str) dict[source]#
A deep copy of the core block at
path(payload.xrd_data,payload.fit_range,payload.phases.<name>) of a recipe dict – the chaining currency between tools (KI-04).- Raises:
ValueError –
pathis not a core block (engine blocks are never shared between engines).KeyError – the recipe has no such block.
- powderline.recipes.with_block(recipe: dict, path: str, value: dict) dict[source]#
A deep copy of
recipewith the core block atpathreplaced by a copy ofvalue.The result is not validated here: the target recipe’s own schema is the gate (
powderline.validate()). RaisesValueErrorfor a path that is not a core block.