Changelog¶
All notable changes to this project are documented here. The format follows Keep a Changelog, and the project uses semantic versioning. Before 1.0, minor versions may include breaking changes; they are listed under Changed.
0.1.1 - 2026-10-16¶
Fixes from testing the first release as a user would: following the documentation, and on edge cases of real data.
Fixed¶
applycould hang after writing its outputs, with pyarrow 26, when its output was captured (from a script, a notebook or CI): parquet datasets still cached at exit deadlocked Arrow's thread pool. The cache is now released before the process exits.- Commands did not find each other's default outputs. Without
outputs.artifact,calibrateandapplystopped with "Missing required option 'artifact'", and with notask_name,screenlooked intrustlayer_outputs/unknown/whilecomputeandcalibratewrote totrustlayer_outputs/powerflow/. Every command now uses one rule:trustlayer_outputs/<task_name>/<cp_type>/(unknownwhentask_nameis not set), and the artifact<calibration predictions>_<variables>.trustlayerthere. screencrashed withKeyError: 'branch'(or'bus') when asked for a variable that was not calibrated, e.g. after droppingline_loadingfromvariablesbut not fromcommands.screen.variables. It now says which variable is missing.- A
line_loadinglower limit inscreen_boundswas used byapplybut ignored byscreen, so the two gave different statuses for the same scenarios. Both ignore it now, as documented: a loading is screened against its rating only. - Missing (NaN) predictions:
applyleft line values with a missing prediction unscreened whilescreenmarked them uncertain; both mark them uncertain now. The terminal line, the summary tables and the crosstab show uncertain point-prediction statuses when there are any, instead of leaving those scenarios out of the counts. - Coverage of voltages counted values with no true value as misses, while line loading left them out. Both leave them out now, and so does
screen, which counted them as values within their limits; a value whose prediction is missing still counts as not covered. calibrate's line-loading bounds depended on the true values: a missing true voltage made them zero-width. Bounds now depend on the predictions only, and equalapply's.- One NaN in the embeddings switched the kernel off silently (every scenario fell back to the stratum quantile). Such embeddings are refused, naming the scenarios, by the CLI and by
KernelIntervalCalibrator. computeoverwrote any file at the artifact path, an input included. It now refuses a path that is an input, a directory, or a file that is not a trustlayer artifact.- Switching a run between one and two target families with the same
outputs.calibrated_predictions(orapplied_predictions) failed: the two datasets were written inside the previous one, or the previous folder was refused. The previous output is now replaced. - A rerun with fewer variables left the previous reports behind, e.g. the line-loading tables and figures next to a new voltage-only summary. Report folders are cleared of trustlayer's own files before each report. The example configs now each write their coverage report to their own folder.
calibrateaccepted missing set-points (NaNVm_targetat PV buses) and gave NaN bounds; it now refuses them, asapplydoes.apply --output <file>in a run with both families gave aNotADirectoryErrortraceback; it now asks for a directory.outputs.outputandcommands.calibrate.outputwere ignored when calibrating both families.mean_widthincoverage_by_stratum.csvandcoverage_by_group.csvdropped every element with any infinite bound, so withsparse_strata: infiniteit could be NaN overall. It is now the mean over values with finite bounds, as documented.*_kernel_tuning_details.csvrepeated the overall mean width on every stratum row.--variables Vm,line_loadingwas rejected; comma-separated and space-separated lists both work.- The sparse-strata message counted bus-stratum pairs as buses (e.g. "3948 buses" on a 14-bus grid); it now says pairs and lists at most five strata.
- Python API:
ChannelSet(valid_masks=None)crashed infit; channels or columns of the wrong shape were broadcast silently (now aValueError); non-integer stratum codes were cast to integers at fit but not at prediction (now rejected);scenario_statusfailed on zero values;KernelIntervalCalibrator.fitrequiredembedding_pooling, which it only records (now"mean"by default). - Config builder:
build_configdropped unknown variables silently, andvalidate_configacceptedalphaoutside (0, 1). - Documentation: the Python API example "from GridFM files" fitted the power-flow set-points as predictions, giving bounds that were too narrow at generator buses; it now sets them aside, as the CLI does. The README Quick start runs
applybut had noinputs.apply. Also: the extra named forline_loading(datakit), an outdated report file name, the bounds of sparse strata, the troubleshooting messages, andclassify_status/scenario_statusin the API reference.
Changed¶
calibrateandapplyreport how many values use pooled bounds (sparse_strata: pooled, no guarantee within their stratum) and, forcp_type: kernel, the share of scenarios in which some value uses the stratum quantile;apply's status summary lists the same. Before, only then_pooledcolumn and thekcp_*columns showed them.calibrateandapplyprint a note when the config differs from the artifact (alpha,cp_type,embedding_pooling, variables): the artifact is used as fitted, and the note says whencomputeshould run again.computeprints a note whentask_nameis neitherPowerFlownorOptimalPowerFlow, since predictions are then read as they are, with no set-points set aside. Spaces in the name no longer matter (Power Flow).- Clearer errors: a missing input names its config key (
Missing inputs.apply.predictions in the config (or --predictions)), a missing artifact says to runcomputefirst, a file that is not an artifact says so, swappedpf_nodeandpf_edgefiles are pointed out instead of printing Arrow's schema, and duplicate or missing rows name the scenarios.alphamust lie in (0, 1),versionmust be 1, andscreen_boundsentries take onlylowerandupper, as numbers, withlowerbelowupper. A missing optional extra (datakitfor line loading) is a one-line error, like the others. - With one target family,
calibrateandapplyprint the path of the dataset rather than its folder.
0.1.0 - 2026-10-15¶
First public release.
Added¶
- Two conformal methods for continuous grid-state predictions:
- stratified split-conformal prediction (
cp_type: element); - kernel-weighted conformal prediction (
cp_type: kernel), localized in a scenario-embedding space, withn_eff(default, target 600) orknnbandwidth selection andeuclideanorcosinedistance. - Stratification by
n_outages,outage_type,system_load_binandpred_bins. - Direct targets (
Vm,Vaand other continuous task outputs) and the derivedline_loading. - The config-first CLI,
gridfm_trustlayer compute | calibrate | screen | apply, with ten example configs inexamples/config/. - A top-level Python API:
from gridfm_trustlayer import NaiveIntervalCalibrator, KernelIntervalCalibrator, ChannelSet, .... A worked example is in the README, under "Python API". CITATION.cffand a contributing guide,CONTRIBUTING.md.- Statuses per value and per operating scenario, the same in
screenandapply. A value iswithin_limitswhen its whole interval lies inside the limits, aviolationwhen it lies entirely beyond a limit, anduncertainwhen it straddles one. An operating scenario takes the worst status of its values:secure,uncertainorinsecure, overall and per variable. The point prediction is classified the same way, as a baseline, and reported next to the interval. screenchecks both against the true solution:scenarios.csv(one row per scenario),status_by_stratum.csv(counts per status, stratum and method, with how many were truly violated), new sections insummary.md, andpred,statusandpoint_statusin the screening detail. The existing alarm tables are unchanged.applygives the statuses of new scenarios whenscreen_boundsis set:<variable>_statusand<variable>_point_statusin its data, and a report inoutputs.status_dir(defaultstatus/next to the data) with the same tables without the truth, plus scenarios by interval status against point status. On labeled data it gives exactly the statuses ofscreen(tested).- The documentation now presents the workflow as fit (
compute, always), validate (calibrateandscreen, on scenarios with a known solution) and operate (apply, on new scenarios). gridfm_trustlayer apply: bounds for new scenarios without a known AC solution (inputs.apply). Same bounds ascalibrate(tested), without the coverage analysis. It reads only what the layer needs: the predictions (with the power-flow set-points,Vm_targetat PV buses andVa_targetat the reference bus), the topology, loads and branch parameters of each scenario when the layer is stratified by outages or load or calibratesline_loading, and the embeddings forcp_type: kernel. Outputs are named*_bounds.parquet, apart fromcalibrate's, and hold no*_targetcolumns, since for new scenarios those are placeholders except at the set-points. The config builder offers the new block.load_ground_truth(..., solved=False)reads scenario descriptions without solved values.gridfm_trustlayer --version, andpython -m gridfm_trustlayeras an alternative to the command.- Each command ends with a short summary (coverage and width per variable and the weakest stratum after
calibrate; caught, missed and false alarms afterscreen), and long runs print their progress block by block. - Pull request and issue templates, and Dependabot updates for the GitHub Actions.
- Readable reports.
calibrateandscreeneach write asummary.md(method, data, tables by stratum, figures and files) next to their CSVs, which renders on GitHub or in any Markdown viewer. Coverage is given with an approximate 95% interval and the coverage of the worst bus or line, andscreenreports caught, missed and false alarms per stratum (screening_by_stratum.csv), not only overall and per element. The reports ofcalibratego tooutputs.coverage_dir, by defaultcoverage/next to the calibrated predictions.screentakes--no-figuresandsave_figures: false, likecalibrate. - A demo notebook,
examples/demo/demo.ipynb: the whole workflow on IEEE 14-bus N-1 to N-5 contingencies, with predictions from the released GENCO checkpointgridfm/genco-pf-datakit(case14_ieee/small/seed0) on freshly generated datakit scenarios: the model's error by number of outages, coverage, why the bounds are stratified by number of outages (against one set of bounds), screening against the point prediction (with every overload the point prediction misses, and the misses by number of outages), new scenarios without their solution, how kernel-weighted bounds adapt to each scenario and screen with them, the trade-offalphasets between AC power flows and missed overloads, and how coverage weakens for lines loaded near their rating (withelement,strat: pred_binsand the kernel). The data (32 MB: 9,723 calibration, 4,848 test and 294 new scenarios) is included. It runs in under a minute, is saved with its outputs, and CI runs it and checks the results the docs quote. - Parallel processes,
n_jobs(config) or--n-jobs(every command). The cross-validated bandwidth choice (n_eff_target: auto) runs its fold × candidate fits in 4 processes by default, with the same result: on 20,000 IEEE-118 calibration scenarios, 37 s instead of 75 s, at 5.0 GB of peak memory instead of 2.4 GB. Whenn_jobsis set, every command also processes its blocks of scenarios in parallel; this is off by default because on IEEE-118 test sets of 5,000 and 20,000 scenarios it was slower and took more memory, as reading parquet dominates and Arrow already uses several threads. Outputs are identical either way (tested). load_interval_artifactsin the Python API: reads any artifact, including the mixed bus-and-line artifacts of the CLI, and returns its calibrators by family ("volt","line").classify_statusandscenario_statusin the Python API: the screening rule ofscreenandapply, for any bounds.- A release workflow: publishing a GitHub release builds the sdist and wheel, checks that the tag matches the package version, and attaches both to the release.
sparse_strata: a bus or line with fewer thanceil(2 / alpha) - 1valid calibration samples in a stratum (19 atalpha = 0.1), or in a test stratum absent from the calibration data, gets its quantile over all calibration scenarios there (pooled, the default), or infinite bounds (infinite, which keeps the per-stratum guarantee). Pooled bounds lose the guarantee within the stratum, so they are never silent:computelists the strata concerned, a<variable>_pooledcolumn marks the rows, and the coverage report counts them inn_pooled. The common case is the voltage magnitude of generator buses, which is predicted only when the generator is out: in the N-1 stratum, only when it is the single outage. Alsocompute --sparse-strata, the config builder and the Python API (fit(..., sparse_strata=...),pooled_masks).- Known values are reported apart. In a power flow,
Vmat PV buses andVaat the reference bus are set-points given to the model, always inside their (zero-width) bounds. They are now left out of every coverage, width and screening number, counted inn_knowncolumns, and named in the reports and terminal summaries (e.g.261,580 known values not counted). The calibrated and applied voltage predictions mark them inVm_knownandVa_known. - Cross-validated choice of the kernel bandwidth:
n_eff_target: auto(withcp_type: kernel,bandwidth: n_eff) makescomputepick the target, and optionally the distance (distances: [euclidean, cosine]), by K-fold cross-validation on the calibration set only, over a grid or a random sample with a fixedbudget, grouped by load profile when datakit'sload_scenario_idxis available. The rule is coverage first, then the narrowest intervals, with uniform weights as the fallback. Off by default; options undertuning:. The choice is recorded in the artifact and reported in<artifact>_kernel_tuning*.csv. Also available from Python asgridfm_trustlayer.conformal.tuning.tune_n_eff_target. - A config builder:
gridfm_trustlayer.interactive.config_builder(), a Jupyter form (optional extrainteractive; notebook inexamples/notebooks/config_builder.ipynb). It shows only the options that apply and validates the result with the CLI's own checks. The underlyingbuild_config()works without Jupyter. - A documentation site, https://gridfm.github.io/gridfm-trustlayer/, built with MkDocs from
docs/and published to GitHub Pages on every push tomain(.github/workflows/docs.yml): getting-started guides, a user guide, references for the CLI, the configuration keys, the data schemas and the Python API, and the methodology.
Changed (relative to the internal 0.0.1 snapshot)¶
- Breaking: calibration and test data must be separate datasets. The config now takes
inputs.calibration(read bycompute) andinputs.test(read bycalibrateandscreen), each with its ownpredictions,pf_node,pf_edgeand, forcp_type: kernel,embeddings. - The in-file split is removed:
calibration_fraction,random_seed,--calibration-fractionand--random-seedare no longer accepted. - So is the in-sample mode, in which
calibrateevaluated on the scenarioscomputewas fitted on. - A random split of one file hides near-duplicate scenarios on both sides, which inflates coverage and kernel gains. The in-sample mode measured coverage on the fitting data.
- The config is rejected if both blocks point at the same file, and
calibraterefuses test predictions identical to the calibration predictions, using a fingerprint stored in the artifact. - Migration: move the old flat
inputskeys underinputs.calibrationand add aninputs.testblock for an independently generated dataset. A flatinputsblock now fails with a message pointing here. analysis_pf_node/analysis_pf_edgeand per-commandcommands.<verb>.inputsoverrides are removed, since each command reads its own dataset block.- Calibration quantiles now use the split-conformal finite-sample correction. Each tail at level
alpha / 2takes theceil((n + 1)(1 - alpha / 2))-th smallest residual for the upper bound and thefloor((n + 1) alpha / 2)-th for the lower bound. Version 0.0.1 used the interpolated empirical quantile, which does not carry the finite-sample coverage guarantee. Bounds are therefore slightly wider (by at most one order statistic per tail). This applies tocp_type: elementand to the stratum fallback quantiles ofcp_type: kernel. - No more zero-width bounds from too few calibration samples. 0.0.1 returned a zero-width interval where an element had no valid residual. A finite bound at level
alpha / 2needs at leastceil(2 / alpha) - 1valid residuals, e.g. 19 atalpha = 0.1; a stratum with fewer now uses the element's quantile over all strata (seesparse_strata), and an element with fewer over all strata gets infinite bounds. Figures plot the finite widths; the CSV outputs count the infinite ones. - Kernel defaults:
bandwidth: n_effwithn_eff_target: 600(0.0.1:bandwidth: knn,k_nn: 20). Artifacts record their bandwidth, so existing artifacts are read unchanged. - The package version is defined once, in
gridfm_trustlayer/_version.py, and read by the build. - Large outputs are partitioned parquet datasets, in the layout of gridfm-datakit: the calibrated predictions (
*_with_intervals.parquet) and the screening detail are directories with onescenario_partition=k/folder per 200 scenarios, zstd-compressed.pd.read_parquet(path)reads them as one table. The screening detail is now parquet (screening_detail_*.parquet/) instead of CSV, with one row per scenario, element and variable holding the true value, the bounds and four violation flags; the per-row limits, duplicated value columns and 0/1 count columns are gone, since the counts follow from the flags. On a 20,000-scenario IEEE-118 test set,screenwrites 58 MB instead of 1 GB. Inputs may be single files or partitioned directories. calibrateandscreenprocess the test set block by block (about 500,000 rows at a time): each block is read with only the needed columns and scenarios, calibrated, screened and written, and the reports are built from per-entity sums, so memory no longer grows in proportion to the test set. Reports and rows are identical whatever the block size (tested). On a 20,000-scenario IEEE-118 test set, peak memory fell from 6.3 to 2.8 GB forcalibrateand from 3.1 to 1.3 GB forscreen, which also runs about seven times faster. The loaders read only the columns they need and no longer re-sort the data for every column, which also lowers the memory ofcompute.- The calibrated voltage predictions include
Vm_target/Va_targetfrom the ground truth when the predictions file has none, soscreennever needs to reload it. cp_type: kernelis several times faster and uses less memory. The weighted quantile now takes each element's total weight from one matrix product and scans only the tails of the sorted residuals, in two levels (blocks, then positions), instead of accumulating the weights over all of them; scenarios whose total weight cannot reach the level skip it entirely; distances, bandwidth and weights are computed once per scenario and shared by all variables; and the test embeddings are read once when they fit in memory. The quantiles are identical to the full computation (tested). On synthetic data at IEEE-118 scale,predict_intervalsis 6-12x faster with a third of the peak memory, and the cross-validated bandwidth choice (n_eff_target: auto) about 5x faster.- The bandwidth solve of
bandwidth: n_effreaches the target effective sample size to a relative 1e-6 instead of 1e-3, using safeguarded Newton steps (5-16 iterations, against 15 bisection steps for the old tolerance) and stopping each test scenario as soon as it converges. Previously all scenarios of a batch iterated until the last one converged, so a scenario's bandwidth depended on which others were processed with it, and a weighted quantile could move to a neighbouring residual depending on where the solve stopped. With the tighter tolerance, two different solvers give the same bounds for 99.996% of the entries (IEEE-118, 10,000 test scenarios). Compared with the old tolerance, about 0.5% of individual bounds change; coverage and width summaries are unchanged to four decimals. - Breaking: new names and layout for the reports, with fewer, clearer files and rounded numbers:
calibrate:coverage/summary.md,coverage_by_stratum.csv(wastrust_reliability_*.csv),coverage_by_group.csv(wastrust_summary_*.csv),coverage_by_element_{voltage,line_loading}.csv(wastrust_analysis_*.csv), and three figures per variable incoverage/figures/: coverage by stratum with its interval, coverage per bus or line, width by stratum. Voltage and line-loading results share one table per level instead of one file per family.screen:summary.md,screening_by_stratum.csv(new),screening_by_group.csv(wastrust_screen_summary_*.csv),screening_by_element_{voltage,line_loading}.csv(wastrust_screen_entity_*.csv),screening_detail_{voltage,line_loading}.parquet/(wastrust_screen_detail_*.parquet/), andfigures/(wasscreen_figures/) with a confusion matrix and, withstrat, recall and precision per stratum. The tables have one row per screened side (lower,upper,any) and name the countscaught,missed,false_alarmsandclearedinstead oftp,fn,fpandtn.- Terminal summaries give widths with their unit (
Vain degrees) and counts with thousands separators. computereads the calibration set block by block, like the other commands, and keeps only the arrays the calibrators need. The artifact is identical (tested, and checked on IEEE-118); peak memory on 20,000 IEEE-118 calibration scenarios fell from 3.0 to 2.0 GB, and no longer grows with the number of calibration scenarios.- The two screening figures were laid out twice; they now take half the time.
- The command-line code is split into one module per command (
gridfm_trustlayer/cli/compute.py,calibrate.py,screen.py,apply.py, with shared helpers incommon.py);CONTRIBUTING.mdmaps the code. The CI caches its pip downloads. - Outages and angle units are judged as in the calibration set. The artifact stores which branches and generators are in service in the calibration set and whether its angles are in degrees;
calibrate,screenandapplycount outages and convert angles against it, so a scenario gets the same stratum whatever other scenarios it comes with. Artifacts without it fall back to a scan of the data at hand, as before. - Installation: trustlayer is published on PyPI (
pip install gridfm-trustlayer). A newdatakitextra installs whatline_loadingneeds (gridfm-datakit 1.1.0 or later);graphkitadds gridfm-graphkit, which needs PyTorch. Thejuliapkg<0.1.26cap is gone, since datakit 1.1.0 supports both juliapkg APIs. Minimum versions: pyarrow 16 and matplotlib 3.8.4, the first that work with numpy 2. - CLI errors (a bad config, a missing file) are one line on stderr and exit code 2 instead of a traceback;
GRIDFM_TRUSTLAYER_DEBUG=1shows the traceback. - An output path is only replaced if it holds a previous trustlayer output (a parquet file or dataset with interval bounds), and never if it is an input of the command.
- A missing (NaN) bound now makes a value
uncertaininstead ofwithin_limits. outputs.screened_predictions, accepted but never used, is removed.scripts/inspect_parquet.pyis removed; read outputs withpd.read_parquetor pyarrow (see "Reading the results" in the docs).
Fixed¶
screenfailed to find the calibrated data of a single-family run (e.g.line_loadingonly) whenoutputs.calibrated_predictionswas a folder-like path:calibratewrites the partitioned dataset at that very path, andscreenlooked for files inside it.- Config validation took a missing
bandwidthasknn, while the kernel's default isn_eff:n_eff_target: autowas rejected unlessbandwidth: n_effwas written out, and the check thatn_eff_targetis not belown_eff_minwas skipped for configs relying on the default. - The bandwidth solve of
bandwidth: n_effcould print an overflowRuntimeWarningwhen the effective sample size is flat in the bandwidth; the step was already discarded in favour of bisection, so results are unchanged. - The width box plots used an argument of matplotlib that is deprecated from 3.11 and removed in 3.13, which would have broken the figures with a future matplotlib.
cp_type: kernelgave zero-width bounds to test scenarios in a stratum absent from the calibration data, e.g. N-6 when calibration stops at N-5. They now get the pooled quantile (or infinite bounds withsparse_strata: infinite), andkcp_fallbackmarks them. The cross-validated bandwidth choice was affected in the same way when a fold held a stratum that the other folds lacked.- With
cp_type: element, a test stratum absent from the calibration data used the unstratified quantile silently; it now followssparse_stratalike any sparse stratum, and is marked and counted. - Set-points no longer shrink the bounds of buses that are PV in only some scenarios. Their zero residuals (the prediction is replaced by the set-point) entered the calibration and pulled the quantiles towards zero, so the
Vmbounds of such buses were far too narrow when the bus was PQ. Known values are now excluded from the fit. On a 5,000-scenario IEEE-118 test set,Vmcoverage at these buses rose from 0.001 to 0.919. Buses of a single type were not affected. - Coverage and screening no longer count the set-points, which were always covered and inflated the reported
VmandVacoverage. - The screening confusion matrix swapped false alarms and misses in its labels; it is now drawn with true violations in rows and alarms in columns, and each cell named.
screennow finds the calibrated parquets inside a directory. Before, it failed whenoutputs.calibrated_predictionswas a directory (a mixed voltage and line-loading run), reading the whole directory as one parquet. It also failed with the default output paths whenever fewer variables were screened than calibrated, e.g. screeningVmafter calibratingVmandVa.- In a run with one target family,
calibratenow writes tooutputs.calibrated_predictions_volt/_linewhen set, which is wherescreenreads from. - The
kernel_fallbackdiagnostic ofKernelIntervalCalibratorno longer flags every test scenario when some element has no valid calibration residual in its stratum, e.g. a branch that is out of service in every scenario. Such an element still uses the fallback bound, but it no longer counts as a kernel fallback. The bounds themselves were not affected. - Box plots in the analysis figures no longer produce
NaNpercentiles (and aRuntimeWarning) when some widths are infinite. - A scenario alone, or in a small batch, could get the wrong stratum. An outage only counted for an element in service somewhere in the same dataset, so
applyon a single N-1 scenario counted N-0 and gave it the narrower N-0 bounds; and angle units were guessed per dataset, so a test set with small angles could stay in degrees. Both now follow the calibration set (see Changed). - Missing true or predicted values (NaN or infinite) are left out of the fit. Before,
elementreturned a NaN quantile for that element in every scenario, andkernelcounted the value as a perfect prediction, narrowing its bounds. --outputpointing at an input deleted it before the run failed, including datakit's raw data, which has the same partitioned layout as trustlayer outputs.scenario_statusraisedKeyErroron missing statuses (None, asapplywrites for known values and out-of-service lines), even where they were masked out.kcp_fallbackflagged nearly every PowerFlow scenario: a generator bus has a validVmresidual in few calibration scenarios, and counted even where itsVmis a known set-point. Only the elements with an interval in the test scenario count now; the bounds were not affected. The bandwidth tuning'sfallback_sharehad the same distortion.kcp_n_effcould be 0 or NaN for embeddings with many dimensions, where the squared kernel weights underflow in float32; it is now taken from the bandwidth solve.- With
strat: pred_bins, the known values (which have no prediction bin) formed a second "all" row per variable incoverage_by_stratum.csvandscreening_by_stratum.csv. commands.compute.n_eff_target: autowith a top-leveltuningblock made the other commands fail;tuningis now checked bycomputeonly.tuning.n_eff_rangewithout a whole number in it (e.g.[1.5, 1.7]) gave a candidate outside the range; it is now rejected.pred_binserrors namedsystem_load_bin.