Skip to main content
LDM partitions supplied regional forecasts into subregions without recalculating the parent model’s economics. All formulas below use the current structure modules.

Workbook and identifiers

MarketPropensityID comprises region, sector, segment, type and sub_type. Forecast rows also carry scenario.name, potential, impact_type, savings_reporting_level, additional declared IDs and variable. Duplicate combinations can fail the importer’s pivot. Every forecast market ID must resolve to a propensity configuration, and every subregion must resolve to a parent region. Propensity text is parsed as comma-separated key/value entries into float weights; blanks and textual none/nan yield no weight mapping. The declared column attribute.partition_type is stored on subregions; the partition loop does not independently filter on that field.

Year and unit conventions

The importer takes unique years from the forecast, pivots each disaggregated variable to annual columns and retains metadata. Supply sorted, consecutive integer years. Diffusion advances one unit per array position regardless of gaps in calendar-year labels. All allocation weights are dimensionless. Market numerator and denominator must have matching units; each propensity numerator/denominator pair must also match. Output forecast units equal input forecast units. No energy, currency or customer-count conversion is performed. Percentages or intensive metrics should not be disaggregated as though they were additive quantities without an explicit interpretation.

Relative propensity

For subregion ii, regional parameter xR,kx_{R,k} and configured coefficient aka_k: Bi=∑kakxi,kxR,kB_i=\sum_k a_k\frac{x_{i,k}}{x_{R,k}} With adoption_propensity_parameters equal to None or textual none, Bi=1B_i=1. Weights are neither normalized nor constrained to be positive by this function. A parameter with a negative behavioural relationship must be transformed or weighted appropriately in inputs. Zero denominators, missing keys and nonnumeric values are not repaired here.

Starting adoption

For starting_point_method="sales_and_barriers", read market MiM_i using <market_share_parameter>.<sector>.<segment> and starting sales NiN_i using starting_sales_parameter: si={Ni/Mi,Mi≠00,Mi=0,si0=min⁡(siBi,0.999)s_i=\begin{cases}N_i/M_i,&M_i\ne0\\0,&M_i=0\end{cases},\qquad s_i^0=\min(s_iB_i,0.999) When market is zero but starting sales are nonzero, the function sets zero and emits a warning. The cap is upper-only: negative starts remain negative and fail later diffusion validation. Literal "none" produces no starting point. The conditional written starting_point_method == "none" or None does not accept Python None as an additional alternative; Python None reaches the invalid-method error.

Raw partition methods

market_size uses ri,y=Mi/MRr_{i,y}=M_i/M_R, constant across study years for fixed market inputs. barriers_and_market_size uses ri,y=(Mi/MR)Bir_{i,y}=(M_i/M_R)B_i. diffusion uses ri,y=(Mi/MR)Fi(y)r_{i,y}=(M_i/M_R)F_i(y), with the starting point derived above. The only accepted partition_method strings are market_size, barriers_and_market_size and diffusion; other values raise ValueError. For Bass parameters p,q,t0p,q,t_0: F(t)=1−e−(p+q)(t−t0)1+(q/p)e−(p+q)(t−t0)F(t)=\frac{1-e^{-(p+q)(t-t_0)}}{1+(q/p)e^{-(p+q)(t-t_0)}} The equivalent starting time is: ti0=ln⁡(1+si0q/p)−ln⁡(1−si0)p+q+t0t_i^0=\frac{\ln(1+s_i^0q/p)-\ln(1-s_i^0)}{p+q}+t_0 For the first forecast position, evaluate at ti0+1t_i^0+1, then increment by one for subsequent positions and relabel the sequence with the supplied study years. Starting fractions and calculated diffusion fractions are checked against [0,1][0,1]. Exact one is allowed by the initial range check but is singular in the inverse logarithm; the normal starting-point calculation avoids it with the 0.999 cap. Require valid positive pp, a nonzero p+qp+q and finite inputs.

Normalization and multiplication

For every market-propensity configuration, the partition routine constructs a row for every supplied subregion, then normalizes each year column across those rows: wi,y=ri,y∑jrj,y,fi,y=wi,yFyw_{i,y}=\frac{r_{i,y}}{\sum_jr_{j,y}},\qquad f_{i,y}=w_{i,y}F_y The implementation’s normalization set is the supplied subregion list, not an additional grouping by attribute.partition_type or parent region. A configuration can therefore include unrelated subregions unless the caller restricts inputs. Use one coherent partition for a regional forecast and reconcile separately by parent region. For finite weights with nonzero column sums, ∑ifi,y=Fy\sum_i f_{i,y}=F_y. This equality does not establish geographic correctness. Negative weights, overlapping areas or inconsistent market denominators can still create invalid allocations. There is no general zero-sum fallback, nonnegative clipping or rounding to integer customers.

Output and execution

get_disaggregated_forecasts multiplies partitions by each forecast series and appends market attributes, subregion, scenario, potential, impact type, reporting level, additional IDs and variable. Annual columns follow the forecast’s study-year list. Combined output is a concatenated dataframe; the CLI writes a timestamped CSV under ./results/ with the dataframe index included. The CLI’s --input-data-xlsx default is ./data/DERP_input.xlsx; --log-file defaults to ./logs/ldm.log. These are filenames, not restrictions on which model’s forecasts can be supplied. Empty forecast lists or missing matches can fail in concatenation or next(...); no general empty-output recovery is implemented.

Reproducible checks

For F=100F=100 GWh, market shares 0.6/0.4 and propensity factors 0.5/1.5, raw weights are 0.3/0.6. Normalization gives 1/3 and 2/3, producing 33.333… and 66.666… GWh. Changing the units to MWh scales forecast outputs only; weights remain identical. Check named tables, unique IDs, complete geographic coverage, annual ordering, finite/nonnegative weights, valid diffusion starts and per-year conservation. Test zero regional denominators explicitly before production use. Existing tests cover import, dictionary parsing, starting points, diffusion, partitioning and logging. They verify implementation behaviour, not the empirical validity of the selected spatial proxies.