PAR(p) Inflow Model
Purpose
Section titled “Purpose”This spec defines the Periodic Autoregressive model of order (PAR(p)) used to capture temporal correlation in inflow time series, including the model definition, parameter semantics, the relationship between stored and computed quantities, the fitting procedure, model order selection, and validation invariants. Section 9 describes the optional PAR(p)-A extension that adds a single annual coefficient on top of the periodic AR structure to capture multi-year hydrological persistence.
1. Model Definition
Section titled “1. Model Definition”The Periodic Autoregressive model of order p (PAR(p)) captures temporal correlation in inflow time series while accounting for seasonal variation in parameters. For hydro at stage corresponding to season :
where:
- : Incremental inflow at stage (m³/s)
- : Seasonal mean for season
- : Autoregressive coefficient for lag in season
- : Innovation standard deviation for season (derived at load — see section 3)
- : Innovation (standardized noise)
- : Season index for stage (e.g., month 1–12)
The model order can vary by season and by hydro plant.
2. Parameter Set
Section titled “2. Parameter Set”For each hydro and each season (e.g., for monthly, for weekly), the complete PAR(p) model requires:
| Parameter | Symbol | Description |
|---|---|---|
| Seasonal mean | Mean inflow for season | |
| AR coefficients | Autoregressive coefficients | |
| Innovation standard deviation | Scale of the innovation term — derived, not independent (section 3) |
3. Stored vs. Computed Quantities
Section titled “3. Stored vs. Computed Quantities”The data model stores seasonal sample statistics and standardized AR coefficients — nothing else. The innovation scale is not a third stored quantity: the coefficient file holds the scale-invariant alone; at load the dimensionless innovation scale is derived from by a periodic-ACF closure (see “Deriving the innovation scale” below), then everything is converted to original-unit and using the seasonal stats and consumed by the LP stage subproblem.
Two planes. Every PAR() parameter sits on one of two planes, and the split is deliberate:
- Conditioning plane — the seasonal mean and seasonal sample standard deviation (both m³/s): the level and magnitude of the series. A study may re-condition these — e.g. a climate scenario that shifts both the seasonal mean and the seasonal variability.
- Dynamics plane — the standardized coefficients and the standardized innovation scale (both dimensionless): the shape of the temporal dependence, independent of magnitude. Only is an independent parameter here — is pinned by itself.
Runtime re-couples the two via and , so re-conditioning the first plane rescales both the coefficients and the noise while preserving the correlation structure. The standardization basis is the seasonal std: is the coefficient of the process normalised by — the classical standardization of the periodic AR literature, and the same basis an externally-fitted model supplies (see the Inputs & Outputs tab).
Stored in input files
Section titled “Stored in input files”These are provided in inflow_seasonal_stats.parquet and inflow_ar_coefficients.parquet:
| Stored quantity | Column | File | Symbol | Description |
|---|---|---|---|---|
| Seasonal sample mean | mean_m3s | inflow_seasonal_stats | Mean of historical observations for season | |
| Seasonal sample std | std_m3s | inflow_seasonal_stats | Standard deviation of historical observations for season | |
| AR coefficients | coefficient | inflow_ar_coefficients | AR coefficient standardized by seasonal std — the direct Yule-Walker output |
The AR order is not stored explicitly. It is derived at runtime from the count of coefficient rows per (hydro_id, stage_id) group in inflow_ar_coefficients.parquet.
The standardized coefficient is the direct output of the Yule-Walker fitting procedure (see section 5.4). It is dimensionless — the coefficient of the standardized process . The relationship to the original-unit coefficient used in the LP is:
Deriving the innovation scale
Section titled “Deriving the innovation scale”Unlike , , and , the innovation scale is not read from a file. It is pinned by itself under the model’s unit-marginal-variance contract — every season’s standardized process has unit variance — via a periodic-ACF closure.
Extend the recursion that defines (section 5.4) to every lag , not just :
Solving this system jointly across every season and every lag (where ) yields the model’s own implied periodic autocorrelation function — the autocorrelations the process would have if described an exactly stationary periodic AR process. The innovation scale follows from the same variance decomposition used in the fitting step (section 5.5):
A season with contributes no terms to the sum, so — a white-noise season gets no variance reduction from an AR part it does not have.
Computed at runtime
Section titled “Computed at runtime”From the stored quantities and the derived , the LP requires two additional quantities computed once at initialization:
Original-unit AR coefficients (for LP constraint matrix entries):
Innovation standard deviation (the noise scale):
No further autocorrelation values are needed beyond the closure derivation above. All required quantities — , , and the they depend on — are derived solely from the stored seasonal stats and AR coefficient file, with no historical data and no separate noise-scale input.
LP coefficients
Section titled “LP coefficients”The stored standardized coefficients are converted to original-unit at runtime (see section 7.2), and these enter the LP directly (see LP Formulation). The LP equation is:
where are state variables (lagged inflows) and is the sampled noise realization.
Whether the lagged inflows enter the Benders cuts is a separate, per-stage choice from whether they drive this inflow equation. A stage’s state_variables selection defaults to storage-only, so by default a PAR() study’s cuts span storage alone — the inflow-lag dimensions are projected out, not zero-padded. Enabling inflow_lags on a stage restores the full storage-plus-lag cut; disabling it on every stage while fitting a PAR() model raises a model-quality warning, since the fitted inflow memory then never reaches any cut. The storage-only projection is the mechanism by which a PAR() study can be coupled to a storage-only downstream boundary. See cut management.
4. Model Order Selection
Section titled “4. Model Order Selection”The PAR order can vary by season. Cobre exposes one order-selection
configuration key — estimation.order_selection — with exactly two accepted
values: "pacf" (the classical periodic PACF method, the default) and
"pacf_annual" (the same PACF rule augmented with the annual component,
PAR(p)-A — see section 9). The classical method is described below. The fitting
run is bounded by max_order (the largest lag considered, default 6),
min_observations_per_season (groups with fewer observations are skipped), and
an optional max_coefficient_magnitude safety net (any season whose fitted
coefficient exceeds it is reduced to order 0 before the contribution analysis
runs).
4.1 PACF (Periodic Partial Autocorrelation Function) — Default
Section titled “4.1 PACF (Periodic Partial Autocorrelation Function) — Default”The default method computes the periodic PACF via progressive periodic Yule-Walker matrix solves at orders , then selects the order using a significance threshold.
Algorithm:
-
For each order from 1 to , build and solve the periodic Yule-Walker system (section 5.4) at order . The last coefficient from the order- solution is the periodic PACF value at lag .
-
Select the order as the maximum lag with significant PACF:
where (95% confidence) and is the number of observations for season . If no lag is significant, (white noise).
-
Estimate AR coefficients at the selected order using the periodic Yule-Walker system (section 5.4).
Post-selection validation — iterative order reduction (Maceira & Damázio, 2006): After PACF selection, the recursively-composed contributions of each lag through the periodic monthly chain are computed. A negative composed contribution flags potential model instability — under SDDP the corresponding Benders cut can carry the negative composition into the future-cost recursion. When any season’s composed contribution is negative, the offending season’s AR ceiling is reduced and the PACF selection plus Yule-Walker fit are re-run at the new ceiling. The reduction iterates across all seasons until every season’s contribution recursion yields non-negative entries, or every offending season has been reduced to order 0.
For the PAR(p)-A path (section 9), two additional rules extend the PACF gate:
- Structural-zero short-circuit at lag 1. When the conditional FACP value at lag 1 is exactly zero — which happens when the standardised annual noise series collapses, typically because a degenerate
HistoryClass::ConstantorHistoryClass::Saturatedbucket has zeroed the seasonal std (section 5.7) — the selected order is forced to 0 (white noise). This blocks degenerate buckets from injecting spurious AR structure. - Minimum order 1 when lag 1 is well defined. When the lag-1 conditional FACP is non-zero but no lag exceeds the significance threshold, the model defaults to order 1 rather than order 0. Hydrological persistence makes a strict order-0 fit a poor default unless the lag-1 value is structurally absent.
5. Fitting Procedure
Section titled “5. Fitting Procedure”For multi-resolution studies (monthly→quarterly aggregation), the same fitting procedure applies after duration-weighted aggregation; see Multi-resolution studies.
This section documents the four-step procedure for fitting PAR(p) parameters from historical inflow data. The fitting is performed when the system derives parameters from inflow_history.parquet. When pre-computed parameters are provided directly in inflow_seasonal_stats.parquet and inflow_ar_coefficients.parquet, this procedure is not executed. The innovation scale is deliberately not part of the fitting: it is derived from the stored coefficients at load (section 3), identically for fitted and user-supplied models (section 5.6).
5.1 Notation
Section titled “5.1 Notation”Let be the historical observations for season . Define:
| Symbol | Description |
|---|---|
| Number of observations for season | |
| Sample mean for season | |
| Sample standard deviation for season | |
| Autocovariance at lag for season | |
| Autocorrelation at lag for season |
5.2 Step 1 — Seasonal Means and Standard Deviations
Section titled “5.2 Step 1 — Seasonal Means and Standard Deviations”Seasonal Mean:
Seasonal Standard Deviation:
The estimator uses the population divisor , not the Bessel-corrected . This matches the Maceira & Damázio (2006) convention and is shared by the classical PAR(p) and PAR(p)-A paths. The population divisor is required for self-consistent conditional FACP values and selected orders on the PAR(p)-A path — under a Bessel correction the sample-vs-population scale factor leaks through every Z⊗A cross-correlation. Using the same divisor for the classical path keeps the two paths’ seasonal-stats output reusable across configurations.
5.3 Step 2 — Seasonal Autocorrelations
Section titled “5.3 Step 2 — Seasonal Autocorrelations”The autocorrelation at lag for season is computed from standardized deviations.
Cross-seasonal autocovariance:
For observations at season with lag reaching back to season (mod , where is the cycle length):
where is the number of year-aligned valid pairs at lag for reference season . The estimator uses the population divisor , matching the convention adopted in section 5.2 and shared by the classical and PAR(p)-A paths.
Autocorrelation:
where is the standard deviation of season (cyclically, so season 0 = season ).
5.4 Step 3 — Yule-Walker Equations
Section titled “5.4 Step 3 — Yule-Walker Equations”For each season , the PAR(p) coefficients in standardized form are found by solving the periodic Yule-Walker system. Unlike the classical (stationary) Yule-Walker equations where all rows use the same reference season, the periodic variant shifts the reference season per row. This correctly accounts for the non-Toeplitz covariance structure of periodic autoregressive processes.
Matrix construction: Entry (0-indexed, ) is the correlation between the lagged observations and ; its reference season is that of the more recent of the two lags:
where is the number of seasons in the periodic cycle (e.g., 12 for monthly). The diagonal entries are always 1 (since for any season ). The matrix is symmetric but not Toeplitz when , because the reference season varies across the entries.
RHS construction: The RHS holds the correlations of the target (season ) with each lag:
Equivalently: form the extended correlation matrix of ; the system matrix is its lag block (rows/columns ) and the RHS is its first row (the target-lag correlations). Row of the system is the model’s second-moment recursion at lag .
The full system is:
where all season indices are taken modulo .
In matrix notation:
where:
- is the periodic correlation matrix (symmetric but not Toeplitz for )
- is the vector of target autocorrelations with per-row reference season shifting
Solution:
The system is solved via Gaussian elimination with partial pivoting (for small systems with , this is numerically adequate).
5.5 Step 4 — Store Standardized Coefficients
Section titled “5.5 Step 4 — Store Standardized Coefficients”The Yule-Walker solution is in standardized form — the direct output of step 3. It is stored as-is in inflow_ar_coefficients.parquet. No conversion to original units is performed, and no other quantity is written alongside it: the innovation scale is not a fitting output. It is derived from afterward, uniformly for every model regardless of its origin, by the periodic-ACF closure of section 3 — not computed here.
When every season of the cycle shares the same AR order, that closure value coincides exactly with this step’s own Yule-Walker system:
(using this section’s and the system’s RHS vector of section 5.4). When per-season orders differ across the cycle, the two need not agree: the closure solves for the model’s implied ACF jointly across every season (section 3), not from this season’s sample autocorrelations alone.
Only (one row per lag) is written to inflow_ar_coefficients.parquet.
5.6 Innovation Scale — Derived at Load, Not Fitted
Section titled “5.6 Innovation Scale — Derived at Load, Not Fitted”The innovation standard deviation for season is recovered at load from the closure-derived innovation scale (see section 3):
When every season of the cycle shares the same AR order, section 5.5 showed this reduces to the fitting system’s own closed-form expression:
5.7 Historical Bucket Classification
Section titled “5.7 Historical Bucket Classification”Before the seasonal stats and AR coefficients are used by the order-selection rules, each per-(hydro, season) historical bucket is classified by the shape of its observations. The classification can override the empirical for fitting purposes, and the override propagates to both the classical PAR(p) and the PAR(p)-A paths because both paths share the seasonal-stats producer.
Four classes are defined:
| Class | Detection rule | Override applied |
|---|---|---|
Default | None of the conditions below | None — use empirical |
Constant | Every observation equals the same value within float tolerance | |
ManyNegative | Strictly negative observations exceed 10% of the bucket | None — diagnostic only, fit proceeds on the empirical stats |
Saturated | The modal value (rounded to m³/s) occupies more than 50% of observations |
The classifier runs in the priority order Constant → ManyNegative → Saturated → Default. Constancy takes precedence over negative-pathology detection, which in turn takes precedence over saturation.
Why a zero seasonal std short-circuits the fit
Section titled “Why a zero seasonal std short-circuits the fit”When the override sets for a season, every downstream fitter degenerates predictably:
- On the classical PAR(p) path, the periodic autocorrelation becomes zero by the zero-std guard in section 5.3, so the PACF selection (section 4.1) reports no significant lag and returns order 0 implicitly.
- On the PAR(p)-A path, the standardised noise series collapses, the conditional FACP at lag 1 evaluates to exactly zero, and the structural-zero short-circuit (section 4.1) returns order 0 explicitly.
Either way, the bucket cannot inject spurious autoregressive structure into adjacent months’ PACFs, and no spatial-correlation contribution flows from it during scenario generation.
Interpretation of each class
Section titled “Interpretation of each class”Constantcaptures plants whose incremental inflow is structurally constant for a given month — typically regulated or transposed flows where the upstream subtraction yields the same value every year. Forcing records the deterministic level without inventing autoregressive dynamics.Saturatedcaptures flow caps (turbine or reservoir capacity) and low-flow constants (transposed ecological flows). The modal value is treated as the cap. There is no magnitude threshold — a cap of 0 m³/s qualifies just as readily as a cap at installed capacity.ManyNegativeflags buckets that the upstream incremental-inflow construction has driven below zero for more than 10% of observations. The condition is recorded for operator diagnostics but does not override the fit — the cause is upstream-data quality, not a methodological signal.Defaultis the standard path; the empirical stats and the chosen order-selection rule decide the order.
5.8 Partial-year studies and the pre-study lag window
Section titled “5.8 Partial-year studies and the pre-study lag window”A study horizon may be narrower than the seasonal cycle — e.g. a monthly model () running only September–December. The per-season fitting described above must then handle seasons that have few or no in-window observations. Two rules keep it well-defined.
Lag-reachability. A season is lag-reachable only if some stage of the (extended) horizon carries it. Each historical observation is resolved to a season from the stage date ranges, falling back to the season-map calendar for dates predating the horizon; an observation whose resolved season has no stage at all is skipped — its statistics would never be consumed. Full-cycle history therefore does not perturb a partial-year fit.
Pre-study lag synthesis (for ). The first study stage’s autoregressive lags reach back to seasons before the study start. For each lag , the season calendar positions before the first study season is introduced as a pre-study season (modular on the true cycle length ) — unless that season is already covered by a study stage (an in-window wrap lag, handled by the cycle-correct lag lookup). The seasonal statistics of those out-of-window seasons are estimated from history exactly as for in-window seasons, then feed the lag terms of the opening study stages — both the coefficient conversion and the deterministic base.
The wrap uses the true cycle length (the number of seasons in the season map), not the number of seasons in the study window, together with a season offset equal to the season of the first study stage — so, e.g., a March-start study maps the lag-1 season to February, not December.
Full-cycle invariance. When the study spans the full cycle (every season already has a study stage) or carries no out-of-window history, nothing is synthesized and the fit is bit-identical to before.
6. Validation Invariants
Section titled “6. Validation Invariants”Cobre enforces a small, well-defined set of invariants; they fall into three groups by where the check runs. Stability is a fitting-time guard rather than a post-hoc root test.
Enforced when loading inflow_ar_coefficients.parquet:
- AR order derivation: the number of coefficient rows per (hydro_id, stage_id) determines the AR order , and the lags must be present and contiguous .
- Stationarity of directly-supplied coefficients: when the AR coefficients for a (hydro, season) group are supplied directly rather than produced by the internal fitting procedure (section 5), the periodic-ACF closure (section 3) must accept them — the closure system must be solvable (non-singular), every implied innovation variance must be strictly positive, every implied autocorrelation must satisfy , and the periodic monodromy — the product, taken once around the full seasonal cycle, of each season’s AR companion matrix — must have spectral radius strictly below 1 (assessed via a conservative upper-bound estimate, so borderline sets are rejected rather than accepted). A set failing any condition is rejected outright. A model whose coefficients come from the internal fitting procedure is exempt from this check: its own order-reduction procedure (section 4.1) already guards stability during the fit.
Enforced when validating the assembled model:
- Positive sample std (error): a season with AR order must have — a zero seasonal std cannot normalise the AR coefficients.
- High explained variance (warning): (the AR part explains more than 99% of the season’s variance — a likely over-fit) raises an advisory model-quality warning; it is not fatal.
Enforced during fitting, not as a post-hoc test:
- Stability: rather than testing the roots of the AR polynomial, the fitter guards stability through the Maceira & Damázio contribution-based order reduction (section 4.1) — a negative lag-1 coefficient, a negative composed contribution, or a coefficient exceeding
max_coefficient_magnitudereduces the season’s order and refits. The periodic Yule-Walker solve additionally truncates the order when its correlation matrix is singular (section 5.4), so a rank-deficient system never yields coefficients.
7. PAR-to-LP Transformation
Section titled “7. PAR-to-LP Transformation”This section derives the explicit algebraic transformation from the canonical PAR(p) model (section 1) into the form consumed by the LP subproblem. The derivation identifies three precomputable components that are cached once at initialization and reused at every forward-pass stage transition.
7.1 Canonical Standardized Form
Section titled “7.1 Canonical Standardized Form”The PAR(p) model (section 1) operates on deviations from the seasonal mean, normalised by the seasonal (marginal) standard deviation — the same basis the stored coefficients use, and the same normalisation as the reference formulation (standardise by the marginal std, not the innovation std). In this standardized form:
where:
- : the stored AR coefficients, standardized by the seasonal std (the direct Yule-Walker output of section 5.4)
- : the seasonal (marginal) standard deviation for season
- : the innovation standard deviation of the standardized process — the closure-derived innovation scale of section 3, , with the model’s implied periodic ACF
- : unit-variance innovation noise
The innovation of the -standardized process is not unit-variance: because that process has unit marginal variance, its one-step innovation has standard deviation — which is exactly why appears here explicitly. The next step converts to original-unit for use in the LP.
7.2 Coefficient Conversion
Section titled “7.2 Coefficient Conversion”The stored standardized coefficients are converted to original-unit coefficients at runtime using the seasonal standard deviations from inflow_seasonal_stats.parquet:
The innovation standard deviation is also derived at this preprocessing step, from the closure-derived innovation scale (section 3):
These conversions are performed once at LP construction time. They require only the seasonal stats (), the stored , and the closure-derived — no historical data.
7.3 LP-Ready Form
Section titled “7.3 LP-Ready Form”Multiplying both sides of the canonical form (section 7.1) by and rearranging yields the LP-ready equation (the noise term becomes ):
where and are derived from stored quantities as described in section 7.2.
This decomposes the inflow into three additive components:
- Lag contribution: — linear function of past inflows (state variables or known values)
- Deterministic base: — constant offset per (stage, hydro), precomputed once
- Stochastic innovation: — noise draw scaled by the seasonal innovation standard deviation
7.4 Deterministic Base
Section titled “7.4 Deterministic Base”The deterministic base is defined as:
This is a precomputed constant per (stage, hydro) pair. It absorbs the mean-adjustment arithmetic that would otherwise be repeated at every forward-pass stage transition. With this definition, the LP-ready form (section 7.3) simplifies to:
For partial-year studies, the lag-season means for seasons preceding the study start are sourced from the pre-study lag window (section 5.8); when no such statistic exists for a given lag season, that lag’s mean contribution is treated as zero.
7.5 LP RHS Patching Operation
Section titled “7.5 LP RHS Patching Operation”The lagged inflows are LP variables, not substituted values. In the LP (see LP Formulation), they appear with coefficients in the AR dynamics constraint row, and separate equality constraints fix each lag variable to its incoming state value:
where is patched per scenario to the actual lagged inflow from the trajectory record.
Because the lag contribution is carried by the constraint matrix (not the RHS), the AR dynamics constraint RHS reduces to:
where:
- is the deterministic base for (stage, hydro), precomputed once at LP construction (section 7.4)
- is the noise scale for (stage, hydro), derived from the closure-derived (section 3) at initialization (section 7.2)
- is the scenario noise draw for this (stage, hydro)
The coefficients are written into the constraint matrix once at LP construction time as the coefficients on the lagged inflow variables; they are not recomputed per scenario.
No division, no mean subtraction, no repeated coefficient transformation — the three precomputed LP components eliminate all redundant arithmetic from the hot path.
7.6 Summary of LP Components
Section titled “7.6 Summary of LP Components”| Component | Symbol | Shape per stage | LP Role | Source |
|---|---|---|---|---|
| Lag coefficients | One per (hydro, lag) | Constraint matrix (AR dynamics row) | Derived from stored and at initialization (section 7.2) | |
| Deterministic base | One per hydro | AR dynamics constraint RHS (fixed term) | Precomputed from and | |
| Noise scale | One per hydro | AR dynamics constraint RHS (noise factor) | Derived from the closure-derived (section 3) and at initialization (section 7.2) |
8. Spatial Correlation Factorisation
Section titled “8. Spatial Correlation Factorisation”The PAR(p) fitting procedure (section 5) produces per-hydro noise terms that are treated as independent across hydro plants. Generating spatially correlated scenarios requires factorising the cross-hydro correlation matrix so that a vector of independent standard normal draws can be mapped to correlated noise. This section documents the choice of factorisation method and the rationale.
8.1 The Problem with Cholesky
Section titled “8.1 The Problem with Cholesky”The classical approach applies Cholesky factorisation: given with lower-triangular, correlated noise is obtained as where . Cholesky requires to be strictly positive-definite. In practice, estimated correlation matrices from hydro inflow series are frequently near-singular or rank-deficient for two reasons:
- Short sample records: Brazilian hydro inflow series span only a few decades of record, yielding a historical record length that is comparable to the number of hydro plants in some subsystems. When is close to the matrix dimension, the sample eigenvalues of cluster near zero.
- Heterogeneous series: Plants with near-identical hydrological regimes (upstream–downstream pairs, same river basin) produce columns that are nearly linearly dependent, reducing the effective rank of below its nominal dimension.
A near-singular causes Cholesky to fail or to produce numerically degenerate lower triangular factors. A separate filtering pass to remove “degenerate” hydros would be required before the factorisation, discarding information and introducing a non-transparent pre-processing decision.
8.2 Eigendecomposition with Clipped Square Root
Section titled “8.2 Eigendecomposition with Clipped Square Root”Cobre uses the symmetric matrix square root via eigendecomposition. The correlation matrix is decomposed as:
where is the orthogonal matrix of eigenvectors and is the diagonal matrix of eigenvalues. The symmetric square root is then:
To handle near-singular matrices, any eigenvalue (arising from floating-point rounding in the sample estimate) is clipped to zero before taking the square root:
Clipping negative eigenvalues to zero is the spectral projection of the sample matrix onto the positive-semidefinite cone — the nearest positive-semidefinite matrix in Frobenius norm; see Higham (2002) for the related nearest-correlation-matrix problem.
Correlated noise is then generated as where .
8.3 Why Eigendecomposition
Section titled “8.3 Why Eigendecomposition”The spectral form handles rank-deficient correlation matrices natively: eigenvectors corresponding to clipped (zero) eigenvalues contribute nothing to the factorisation, which is the correct behaviour for directions of zero variance. No prior filtering of degenerate hydro plants is needed.
The clipping threshold acts as a single, transparent parameter controlling which near-zero eigenvalues are treated as structural zeros. The cross-entity correlation structure is preserved for all eigenvalues above the threshold.
8.4 Trade-offs
Section titled “8.4 Trade-offs”| Property | Eigendecomposition (Cobre) | Cholesky |
|---|---|---|
| Handles rank-deficient | Yes — clipping makes it robust | No — requires positive-definiteness |
| Computational cost | Higher (full eigendecomposition) | Lower on well-conditioned matrices |
| Degenerate-hydro filtering pass | Not required | Required for near-singular |
| Transparency of approximation | Single clipping threshold | Opaque numerical failure or pivot |
The higher computational cost is acceptable because the factorisation is performed once per study configuration and not on the hot path of the forward pass.
9. Annual Component Extension (PAR(p)-A)
Section titled “9. Annual Component Extension (PAR(p)-A)”The classical PAR(p) of section 1 captures temporal dependence at lags up to a small order (kept low for monthly cycles, since the periodic Yule-Walker system becomes ill-conditioned at higher orders). On long Brazilian hydro series this is enough to reproduce the within-year persistence but not the multi-year persistence visible in dry/wet super-periods of the historical record. The PAR(p)-A extension adds a single annual coefficient on top of the periodic AR structure to capture that longer-range persistence without inflating the AR order.
The extension is selected by the order-selection method pacf_annual. When active, the model carries one additional triple per (hydro, season) on top of the classical parameter set.
9.1 Augmented Model
Section titled “9.1 Augmented Model”Let denote the rolling 12-month average of incremental inflows ending one stage before :
The PAR(p)-A model augments section 1 with the standardised deviation of from its own seasonal mean:
where:
- , : sample mean and population-divisor standard deviation of the season’s own annual regressor — the values of over stages in season , i.e. the rolling windows whose most recent observation falls in the preceding season (section 9.3)
- : original-unit annual coefficient at season — derived at runtime from the standardised stored coefficient (section 9.4)
- All other symbols carry their classical meaning from section 1
When the PAR(p)-A extension is inactive, the annual term is absent and the model reduces exactly to section 1.
9.2 Annual Component Parameters
Section titled “9.2 Annual Component Parameters”For each (hydro, season) the PAR(p)-A path stores three additional quantities:
| Quantity | Symbol | Description |
|---|---|---|
| Standardised annual coefficient | Yule-Walker output for the annual term — dimensionless | |
| Annual seasonal mean | Sample mean of season ‘s annual regressor (m³/s) | |
| Annual seasonal std | Population-divisor std of season ‘s annual regressor (m³/s, ) |
The standardised coefficient is the direct output of the extended periodic Yule-Walker system below (section 9.5). Storage of and alongside the seasonal statistics of enables the runtime unit conversion of section 9.4 without re-reading the historical record.
9.3 Estimating the Annual Seasonal Statistics
Section titled “9.3 Estimating the Annual Seasonal Statistics”Form every rolling 12-month average the chronological history admits, and assign each window to the season following its most recent observation — the season whose stages use that window as their regressor . The statistics stored for season therefore describe exactly the regressor that season’s stages see. For each (hydro, season ) bucket of values :
Both estimators use the population divisor , matching the convention of section 5.2 and ensuring no sample-vs-population scale factor leaks into the conditional FACP of section 9.5. At least 13 chronological observations are required for a hydro to participate in PAR(p)-A — that is the minimum needed to form one rolling 12-month average.
9.4 Runtime Unit Conversion
Section titled “9.4 Runtime Unit Conversion”The stored standardised coefficient is converted to the original-unit coefficient at LP construction time using the seasonal stats and annual stats:
The conversion mirrors section 7.2 for the classical AR coefficients. The annual term is then expanded through the window definition at LP construction: each of the 12 lag coefficients gains on top of its classical value, and the deterministic base absorbs the corresponding mean contribution through the lag-season means — the LP carries no separate annual variable, and the rolling-window value is realized entirely by the lag state variables it already carries.
9.5 Order Selection and Coefficient Estimation
Section titled “9.5 Order Selection and Coefficient Estimation”PAR(p)-A order selection conditions on the annual noise series. The order-selection input is the conditional FACP at lag , defined as the partial autocorrelation between the standardised current-season residual and the standardised residual at lag , conditioned on the intermediate standardised annual noise series and the previous annual innovation . Computing the conditional FACP requires a partitioned covariance decomposition that distinguishes , , and blocks.
The conditional FACP feeds the PACF order-selection rule of section 4.1, with the two PAR(p)-A-specific extensions (structural-zero short-circuit and minimum-order-1) already described there.
Cross-covariance divisor
Section titled “Cross-covariance divisor”The block uses the same year-aligned population divisor as the classical autocovariance (section 5.3). The and blocks use a max-bucket-size divisor:
The max-bucket convention is required because excludes the first year of by construction (a rolling 12-month window cannot anchor in the first 12 observations). The strict-pair count would distort the scale of the cross-correlations and bias the conditional FACP. The PAR(p) path never uses Z⊗A cross-correlations, so the divisor question is PAR(p)-A-specific.
Extended periodic Yule-Walker
Section titled “Extended periodic Yule-Walker”Once the order is selected, the coefficients are recovered by solving the extended periodic Yule-Walker system:
where is the partitioned covariance whose first rows replicate the classical periodic Yule-Walker rows (section 5.4) and whose last row adds the and entries. The RHS appends the target.
As in the classical case, the innovation scale is not a fitting output. For a PAR(p)-A model the closure of section 3 runs on the effective 12-lag system: the annual regressor is a linear functional of the last 12 inflows, so expanding it per lag yields an effective periodic AR whose standardized lag- coefficient is the classical (zero beyond ) plus the annual contribution . The innovation scale derives from that effective system’s implied ACF, and the runtime noise scale is exactly as in section 3.
9.6 Iterative Order Reduction
Section titled “9.6 Iterative Order Reduction”The Maceira & Damázio iterative reduction of section 4.1 is applied across the full periodic cycle on the PAR(p)-A path as well: after the initial fit, the recursively-composed contributions of each AR lag through the periodic monthly chain are evaluated, and any negative-contribution season has its AR ceiling reduced before refit. The annual coefficient does not enter the contribution-chain check — it is anchored to the rolling annual mean and so does not propagate through the lag chain.
9.7 Activation and Fallback
Section titled “9.7 Activation and Fallback”| Configuration | Path |
|---|---|
order_selection: "pacf" | Classical PAR(p) — annual triple absent (section 5) |
order_selection: "pacf_annual" | PAR(p)-A — annual triple required for every (hydro, season) |
Bucket flagged HistoryClass::Constant or Saturated (section 5.7) | Effective order 0 on either path; annual term suppressed when the seasonal std collapses |
| Hydro with fewer than 13 observations on PAR(p)-A path | Hard failure during fitting (no silent fallback to classical) |
The two paths share the seasonal-stats producer of section 5.2; switching between them does not silently change or . The PAR(p)-A path uses the same spatial-correlation factorisation as the classical path (section 8); the extension affects only the temporal model.
9.8 Conditioning-Swap Exactness
Section titled “9.8 Conditioning-Swap Exactness”Section 3 shows that re-conditioning the seasonal stats (, ) alone — leaving and the closure-derived untouched — rescales the classical dynamics while preserving the correlation structure exactly, for any per-season rescaling. The PAR(p)-A annual term does not share this property in general.
Like , the annual seasonal std is a functional of the process’s own second-moment structure — it is not an independent parameter of the annual term. Unlike , it is carried as stored conditioning data (section 9.2) rather than re-derived from at load. The runtime annual coefficient (section 9.4) is , so a conditioning swap preserves the annual term’s contribution to the correlation structure only when rescales by the same factor as in every season — a uniform rescaling for all , giving and leaving unchanged. A season-varying conditioning swap — rescaling by a different factor in different seasons — leaves at its pre-swap value while moves, shifting and perturbing the preserved correlation structure.
Implementation in Cobre
Section titled “Implementation in Cobre”The methodology above defines the PAR(p) inflow model; the tabs below cover how Cobre’s software surface configures, feeds, and reports on it.
Cobre’s config.json top-level estimation block, the scenarios/ PAR(p)
input files, and scenarios/correlation.json configure the estimation and
correlation pipeline the methodology above describes. This tab is the
field-level configuration reference; the equations these fields feed are in
the sections above.
config.json — estimation Block
Section titled “config.json — estimation Block”The estimation block is a top-level key of config.json (a sibling of
training), and controls the PAR(p) fitting pipeline whenever
inflow_history.parquet is present:
{ "estimation": { "max_order": 6, "order_selection": "pacf", "min_observations_per_season": 30, "max_coefficient_magnitude": null }}| Field | Type | Default | Description |
|---|---|---|---|
max_order | integer | 6 | Maximum lag order p_max considered by PACF order selection (methodology §4.1). |
order_selection | string | "pacf" | Order-selection criterion. Exactly two values are accepted: "pacf" (classical PAR(p), methodology §1–§8) and "pacf_annual" (PAR(p)-A, methodology §9). |
min_observations_per_season | integer | 30 | Recommended minimum observation count per (hydro, season) group. Below this count, estimation still proceeds — see Implementation notes. |
max_coefficient_magnitude | float or null | null | Optional safety net on fitted coefficient magnitude — see Implementation notes for the exact effect. |
Setting "order_selection": "pacf_annual" activates the PAR(p)-A annual
component (methodology §9): the periodic Yule-Walker system is extended with
the annual cross-correlation term, per-season sample statistics of the
rolling 12-month average are computed, and the fitted triple
(ψ, annual mean, annual std) is written to inflow_annual_component.parquet
— see the Inputs & Outputs tab. When absent (or set to "pacf"), only the
classical PAR(p) path runs and no annual triple is produced.
Inflow Source Resolution — Choosing Which Files to Provide
Section titled “Inflow Source Resolution — Choosing Which Files to Provide”The PAR(p) inflow model is built from up to five files in scenarios/. Three
of them drive path resolution — their presence or absence selects which
of seven estimation paths Cobre executes:
| Symbol | File | Role |
|---|---|---|
| H | scenarios/inflow_history.parquet | Raw historical observations for fitting |
| S | scenarios/inflow_seasonal_stats.parquet | User-supplied seasonal mean/std |
| R | scenarios/inflow_ar_coefficients.parquet | User-supplied AR coefficients |
The other two files layer orthogonally on top of the resolved path:
scenarios/correlation.json wins on every path when present (identity
correlation otherwise, unless a path estimates it from residuals); and
scenarios/inflow_annual_component.parquet is honored only on the
pass-through paths (rows 2, 3, 7 below) — the estimation paths (4, 5, 6)
always overwrite it with fitted values.
| # | H | S | R | Path | Seasonal stats | AR coefficients | Correlation |
|---|---|---|---|---|---|---|---|
| 1 | 0 | 0 | 0 | Deterministic | no PAR model | none | identity, unless correlation.json provided |
| 2 | 0 | 1 | 0 | UserStatsWhiteNoise | user file | order-0 (white noise) | identity, unless correlation.json provided |
| 3 | 0 | 1 | 1 | UserProvidedNoHistory | user file | user file | identity, unless correlation.json provided |
| 4 | 1 | 0 | 0 | FullEstimation | fitted from H | fitted from H (PACF + Yule-Walker + Maceira & Damázio) | estimated from H residuals, unless correlation.json provided |
| 5 | 1 | 0 | 1 | UserArHistoryStats | fitted from H | user file | estimated from H residuals using user coefficients, unless correlation.json provided |
| 6 | 1 | 1 | 0 | PartialEstimation | user file (used only for the Yule-Walker solve) | fitted from H | estimated from H residuals using the user stats, unless correlation.json provided |
| 7 | 1 | 1 | 1 | UserProvidedAll | user file | user file | identity, unless correlation.json provided (history is not re-consumed) |
Cases with R = 1 but H = 0 and S = 0 collapse to row 1 — AR coefficients
alone cannot drive estimation.
Practical recipes:
| Goal | Files to provide | Path |
|---|---|---|
| Smoke-test the LP without stochasticity | (no scenarios files) | 1 |
| Deterministic seasonal levels, no autoregression | inflow_seasonal_stats.parquet | 2 |
| Fully user-specified PAR(p) without raw observations | inflow_seasonal_stats.parquet, inflow_ar_coefficients.parquet | 3 |
| Hands-off: fit everything from raw observations | inflow_history.parquet | 4 |
| Fit stats from history, override the AR structure | inflow_history.parquet, inflow_ar_coefficients.parquet | 5 |
| Override the levels (mean/std) but let Cobre fit the AR | inflow_history.parquet, inflow_seasonal_stats.parquet | 6 |
| Provide every parameter, including the PAR(p)-A annual term | All three of H, S, R (and optionally the annual file) | 7 |
| Pin a custom spatial correlation on any path | Add correlation.json | any |
scenarios/inflow_history.parquet — Raw Historical Observations
Section titled “scenarios/inflow_history.parquet — Raw Historical Observations”Required to drive any of the estimation paths (H = 1 above). One row per historical observation:
| Column | Type | Required | Description |
|---|---|---|---|
hydro_id | INT32 | Yes | Hydro plant identifier (matches id in hydros.json). |
date | DATE | Yes | Observation date. |
value_m3s | DOUBLE | Yes | Historical inflow observation (m³/s); must be finite. |
Every hydro plant in hydros.json must have at least one observation in this
file when estimation is active. When a season has fewer observations than
min_observations_per_season, estimation still proceeds — see Implementation
notes for the exact behavior.
scenarios/correlation.json — Spatial Correlation Configuration
Section titled “scenarios/correlation.json — Spatial Correlation Configuration”Named correlation profiles, each grouping entities with a symmetric correlation matrix, plus an optional stage-to-profile schedule:
{ "method": "spectral", "profiles": { "default": { "correlation_groups": [ { "name": "basin_south", "entities": [ { "type": "inflow", "id": 0 }, { "type": "inflow", "id": 1 } ], "matrix": [ [1.0, 0.7], [0.7, 1.0] ] } ] }, "wet_season": { "correlation_groups": [ { "name": "basin_south", "entities": [ { "type": "inflow", "id": 0 }, { "type": "inflow", "id": 1 } ], "matrix": [ [1.0, 0.85], [0.85, 1.0] ] } ] } }, "schedule": [{ "stage_id": 0, "profile_name": "wet_season" }]}| Field | Required | Description |
|---|---|---|
method | Yes | Correlation decomposition method. "spectral" (eigendecomposition + clipped square root, methodology §8.2) is preferred; "cholesky" is also accepted for existing case files and is treated identically. |
profiles | Yes | Named profiles, each holding one or more correlation_groups. Must not be empty. |
schedule | No | Optional array of { stage_id, profile_name } entries activating a named profile for a specific stage. Absent means the "default" profile (or the sole profile) applies to every stage. Every profile_name referenced must exist in profiles. |
Each correlation group’s matrix must be square, match the length of
entities, have diagonal entries exactly 1.0, off-diagonal entries in
[-1.0, 1.0], and be symmetric.
Valid entity types in entities[].type:
"inflow"— hydro inflow series (idmatchesidinhydros.json)"load"— stochastic load demand (idmatchesidinbuses.json)"ncs"— non-controllable source availability (idmatchesidinnon_controllable_sources.json)
Same-type enforcement: every entity within a single correlation group must
share the same type. Mixing types in one group is rejected at case-load
time; to correlate inflow with load, define separate groups (one per type)
with matching correlation structure.
This is a topic-scoped index of the files PAR(p) estimation touches — it names each file and its role, it does not repeat their field-by-field schemas beyond what the Configure tab already shows for the config-authoring surface. The exhaustive, field-by-field case-directory and output reference is owned by the Reference corpus (Case Directory Format and Output Format pages).
Inputs
Section titled “Inputs”| File | Role |
|---|---|
scenarios/inflow_history.parquet | Raw historical inflow observations per (hydro, date) — drives the estimation path (H) when present (see the Configure tab). |
scenarios/inflow_seasonal_stats.parquet | User-supplied or fitted seasonal statistics per (hydro, stage). The std_m3s column is the stored sample standard deviation of the historical series — not the innovation standard deviation used for noise scaling (see Implementation notes). |
scenarios/inflow_ar_coefficients.parquet | User-supplied or fitted standardized AR coefficients, one row per (hydro, stage, lag) — schema [hydro_id, stage_id, lag, coefficient]. The AR order is derived from the row count, not stored explicitly. |
scenarios/inflow_annual_component.parquet | Optional user-supplied PAR(p)-A annual triple (coefficient, mean, std of the rolling 12-month average). Honored only on the pass-through paths — the estimation paths overwrite it with fitted values (see the Configure tab). |
scenarios/correlation.json | Named spatial-correlation profiles and optional stage schedule. Wins over estimated or identity correlation on every inflow-source path when present. |
initial_conditions.json (hydro past_inflows rows) | The AR-lag seed: past inflow values ordered most-recent-first, consumed by the opening study stages’ lag terms (methodology §7.5). |
For the complete field-by-field schema of each file above, see the Case Directory Format reference page in the Reference corpus.
Externally-fitted PAR models
Section titled “Externally-fitted PAR models”Cobre accepts fully user-supplied parameters — you may fit a PAR() model with
any method outside Cobre and hand it the two parquet files (the
UserProvidedNoHistory and UserProvidedAll paths on the Configure tab).
Cobre then skips fitting, validates the inputs, and derives the innovation
scale from your supplied via the periodic-ACF closure (see
Deriving the innovation scale). The fields do not
encode Cobre’s fitting method — they encode the two-plane
representation — so any fit maps onto them by the table
below. Let be your series’ seasonal sample std and your
original-unit AR coefficients.
| Column (file) | Fill with | Meaning |
|---|---|---|
mean_m3s (inflow_seasonal_stats) | Seasonal mean of the series (m³/s) | |
std_m3s (inflow_seasonal_stats) | Seasonal sample std of the series (m³/s) | |
coefficient (inflow_ar_coefficients) | AR coefficient standardized by the seasonal std |
There is no column for your model’s own innovation std : Cobre always derives from the you supply, the same way it derives for an internally-fitted model (see Deriving the innovation scale). If your own fit’s is not exactly consistent with your supplied under the periodic-ACF closure, Cobre’s derived differs from your original fit’s — the closure derives the noise scale so the seasonal std you supply is honored exactly.
Rules Cobre enforces (or that silently mis-scale the model if broken):
- Standardize by the seasonal std, consistently. The coefficients are those
of the series standardized by its seasonal sample std — the classical
standardization — and the you divide by must be exactly the
std_m3syou store: Cobre reconstructs and from the storedstd_m3s, so a mismatch is silent. - Cobre hard-rejects a coefficient set that fails its stationarity gate. The gate runs the periodic-ACF closure on your supplied and rejects the whole load when the closure system is singular, the implied innovation variance is not strictly positive for any season, any implied autocorrelation magnitude exceeds 1, or a conservative spectral-radius estimate of the periodic monodromy reaches 1. The gate runs only on directly-supplied coefficients — coefficients Cobre fits internally keep the fitter’s own fallbacks.
- Lags must be contiguous ; the AR order is implicit in the row count (there is no order column). White noise = no coefficient rows for that (hydro, stage), i.e. order 0, for which the closure gives .
- Resolvable season context is required whenever any AR order is .
Every (hydro, stage) group with at least one coefficient row must resolve to
a season via
season_definitionsor a per-stageseason_id(see Case Directory Format and Running Cobre — configuration) — this applies on every load path, including a plain file-load with no fitting.
Outputs
Section titled “Outputs”| File | Role |
|---|---|
output/stochastic/inflow_seasonal_stats.parquet | Fitted seasonal statistics, same schema as the input file — written whenever the estimation path ran. |
output/stochastic/inflow_ar_coefficients.parquet | Fitted AR coefficients, same schema as the input file — written whenever the estimation path ran. |
output/stochastic/inflow_annual_component.parquet | Fitted PAR(p)-A annual triple — written only when order_selection: "pacf_annual" and the estimation path ran. |
output/stochastic/fitting_report.json | Per-hydro summary of the selected AR order and coefficients — a human-readable complement to the parquet outputs above. |
output/stochastic/correlation.json | Round-trip export of the resolved correlation model, in the same schema as the scenarios/correlation.json input. |
output/stochastic/noise_openings.parquet | The backward-pass opening tree actually used during training, exported when exports.stochastic: true. Copying it back into scenarios/noise_openings.parquet replays the same opening tree on a subsequent run — see Scenario Generation for the opening-tree mechanics themselves (out of scope here). |
For the complete output schema (columns, types, file layout), see the Output Format reference page in the Reference corpus.
Non-normative software behavior for PAR(p) estimation — what Cobre does at runtime, beyond the equations above. This tab references the methodology body for the derivations rather than restating them.
std_m3s vs sigma_m
Section titled “std_m3s vs sigma_m”The two files in the Inputs & Outputs tab store two different standard
deviations, and the software surface never conflates them: std_m3s (column
in inflow_seasonal_stats.parquet) is the stored sample standard
deviation of the historical series for a (hydro, season) — a fixed,
on-disk number. sigma_m, the innovation standard deviation that scales the
stochastic innovation term, is never stored; it is derived at load from
std_m3s and the innovation scale r_m, which Cobre always derives from the
AR coefficients via the periodic-ACF closure (methodology §3). Reading
std_m3s as if it were sigma_m — or vice versa — silently rescales every
noise draw.
Where the closure runs relative to the fit
Section titled “Where the closure runs relative to the fit”The closure derivation of r_m is a load step, not a fitting step: it
runs after model assembly on every input path — pure file-load and all
estimation paths alike — so a fitted model and a user-supplied model with the
same coefficients get the same innovation scale. When per-season AR orders
differ across the periodic cycle, the closure-derived value can differ
slightly from the single-season Yule-Walker identity evaluated on the sample
autocorrelations (methodology §5.5) — the closure solves for the model’s own
implied ACF jointly across all seasons, which is what makes the generated
seasonal marginal std match std_m3s exactly. When every season shares the
same order, the two coincide to double precision.
Model-quality diagnostic: explained variance
Section titled “Model-quality diagnostic: explained variance”The model-quality warning for a near-deterministic season reports an
explained_variance field (), firing when
(methodology §6). It is a library-surface diagnostic type only:
Cobre computes it internally and does not render it on the CLI or expose it
through the python bindings. Keep this distinct from r_squared, the
unexplained variance reported in the stationarity gate’s rejection message —
the two are complementary quantities describing different situations:
explained_variance is a quality signal on an accepted model, while
r_squared in a rejection message is the reason a model was rejected
outright.
Order-selection behavior
Section titled “Order-selection behavior”The classical PACF order-selection rule (methodology §4.1) and the
Maceira & Damázio iterative reduction it triggers are applied automatically
whenever Cobre estimates from inflow_history.parquet. Two runtime details
sit alongside that rule but are not part of the derivation:
min_observations_per_seasonis a warning, not a load-time error. When a (hydro, season) group has fewer historical observations than the configured value (default 30), Cobre logs a model-quality warning naming the plant and season and proceeds with the fit anyway. The threshold is a quality signal, not a hard gate.max_coefficient_magnitudeis an independent safety net. When set and any fitted coefficient for a (hydro, season) exceeds the threshold in magnitude, Cobre clears that group’s coefficients entirely (order reset to 0, for which the closure then derivesr_m = 1— pure white noise). This runs as a separate pass from the Maceira & Damázio contribution-chain reduction (methodology §4.1) — a coefficient can trigger this magnitude cutoff even when its composed contribution is non-negative.
History classification runs before any order selection
Section titled “History classification runs before any order selection”Before PACF selection sees a (hydro, season) bucket, the history classification pipeline (methodology §5.7) runs automatically: constant, saturated, and negative-dominated series are detected and, for the first two, routed to a degenerate order-0 fit rather than left to over-fit a structurally uninformative bucket. This classification is unconditional — it always runs when estimating from history, on both the classical PAR(p) and PAR(p)-A paths.
PAR(p)-A annual-component runtime behavior
Section titled “PAR(p)-A annual-component runtime behavior”Setting order_selection: "pacf_annual" (Configure tab) extends the
estimation pipeline with the steps of methodology §9. At runtime this adds
one requirement not visible in the equations: a hydro needs at least 13
chronological observations to participate in PAR(p)-A — the minimum to
form one rolling 12-month average (§9.3). A hydro with fewer observations on
the PAR(p)-A path is a hard estimation failure; there is no silent
fallback to the classical path (methodology §9.7).
Determinism guarantee
Section titled “Determinism guarantee”PAR(p) fitting is one of the model families covered by Cobre’s order-stable parallel fitting guarantee: coefficients are fit independently per hydro and reassembled into a canonical per-hydro slot, so the fitted model is a function of the inputs alone — independent of how many threads run or how hydros are distributed across them. See Determinism Guarantees §3 for the full mechanism, which this chapter’s estimation pipeline shares with the computed-FPHA fitting pipeline described in Hydro Production Models.
Cross-References
Section titled “Cross-References”- LP Formulation — AR inflow dynamics in the LP: state expansion, lag column pinning, reduced-cost extraction
- Inflow Non-Negativity — Methods for handling negative realizations produced by the PAR(p) model
- Scenario Generation — When external scenarios are used in training, a PAR model is fitted to the external data for backward pass opening tree generation. The fitting procedure (section 5 above) applies equally to this derived model.
- Notation Conventions — Defines inflow symbols (, , , ) and unit conventions