This file records detailed technical notes on significant refactors and migrations.
All "Type 1" cross-year state variables were migrated from the plain Python dict self.DYNAMIC to the typed variables_table (self.V) via self.V.write_var / self.V.read_var. This centralises cross-year state in the validated variable store and removes the need for dict lookups.
Old key in self.DYNAMIC |
New variable name in self.V |
Notes |
|---|---|---|
price_index |
price_index |
|
investment_price_index |
investment_price_index |
|
delta_price_yoy |
delta_price_yoy |
Numerically identical to dp_all; written as raw numpy array |
normal_output |
normal_output |
Written/read as DataFrame with from_df=True |
normal_output_growth_helper |
normal_output_growth_helper |
Written as raw numpy array |
profit_rate_mean |
profit_rate_mean |
In-place numpy modification possible (read_var returns reference) |
cpi |
cpi |
|
labour_force |
labour_force |
Column renamed LF → labour_force on write, back on read |
inc_exog |
inc_exog |
Column renamed dlabor_exog → inc_exog on write, back on read |
gdp_history |
gdp_base |
gdp_base was already written to self.V; DYNAMIC write removed and reads updated |
SourceCode/model_class.pySourceCode/initiate_modules.pySourceCode/ftt_power.py
write_var(from_df=True)requires the input DataFrame to have a column named exactlyvar_name; variables whose DataFrames used a different column name require a rename before write and a rename-back after readread_varreturns a direct reference (no copy) to the stored numpy array, so in-place modifications (e.g. theprofit_rate_meanfirst-year correction) propagate without an explicit write-backdelta_price_yoyis stored as a flat numpy array; downstream code that needs a labelled DataFrame reconstructs it viaMRIO_vec_to_df_DEF(self.V.read_var('delta_price_yoy', year), 'dp')gdp_basewas already registered inself.Vand written there alongside the old DYNAMIC write; migration simply removed the DYNAMIC write and updated all reads
Three 3-dimensional final demand consumption variables (household_consumption, fcf_consumption, government_consumption) were migrated from self.DYNAMIC to self.V. These are indexed by [REG_imp × REG_exp × TRAD_COMM] and hold constant-price consumption flows in thousands of USD.
Old key in self.DYNAMIC |
New variable name in self.V |
Original column | Notes |
|---|---|---|---|
household_consumption |
household_consumption |
VIPA |
Stored as flat array; indexed form reconstructed on read |
fcf_consumption |
fcf_consumption |
VDFA |
Same pattern |
government_consumption |
government_consumption |
VIGA |
Index order [REG_exp, REG_imp, TRAD_COMM] on read to match downstream usage |
SourceCode/model_class.pySourceCode/initiate_modules.py
- All three variables are stored as flat numpy arrays (163 regions × 163 regions × 120 sectors); downstream code that requires the original indexed DataFrame form uses
read_var(..., as_df=True).rename(columns={var_name: original_col}).set_index([...]) EXOG_VARS.HH_BASE,EXOG_VARS.FCF_BASE, andEXOG_VARS.GOV_BASEare set fromself.Vat the start of each year> model_start; foryear == model_startthey retain their original values loaded byexog_vars.py- Init block (
year == model_start) writesyear-1values from the initialEXOG_VARSbases so the variable store has meaningful values from the start government_consumptionis written in two code paths: the dynamic path (drops helper columns, renames VIGA) and the base-year fallback that resetsEXOG_VARS.GOV_BASE's index before writing
Four-dimensional variables (indexed by [REG_imp × PROD_COMM × REG_exp × TRAD_COMM]) cannot be stored as flat numpy arrays in the existing variables_table without prohibitive memory use. Two new methods were added to variables_table to handle sparse DataFrames, and the 4D variables were migrated from self.DYNAMIC (or equivalent local state) to self.V.
| Method | Behaviour |
|---|---|
write_var_df(var_name, year, df) |
Writes a DataFrame as parquet to GLORIA_results/ following the generate_var_table naming convention; keeps the two most recent years in an in-memory cache (df_data); older years are evicted from memory (parquet on disk remains available) |
read_var_df(var_name, year) |
Serves from memory cache if available; loads from parquet on miss without re-caching |
Parquet file naming: results_file_long_{scenario_name}_{var_name}_{year}.parquet
| Variable | Source | Dimensions | Notes |
|---|---|---|---|
energy_flows |
local in initiate_modules |
[REG_imp, REG_exp, PROD_COMM, TRAD_COMM] |
Written once per year; read cross-year in ftt_power.py |
emission_cost_intermediates |
self.DYNAMIC |
[REG_imp, REG_exp, PROD_COMM, TRAD_COMM] |
Carbon tax incidence on intermediate flows |
emission_cost_hh |
self.DYNAMIC |
[REG_imp, REG_exp, PROD_COMM, TRAD_COMM] |
Carbon tax incidence on household consumption |
emission_cost_fcf |
self.DYNAMIC |
[REG_imp, REG_exp, PROD_COMM, TRAD_COMM] |
Carbon tax incidence on FCF |
emission_cost_gov |
self.DYNAMIC |
[REG_imp, REG_exp, PROD_COMM, TRAD_COMM] |
Carbon tax incidence on government consumption |
cbam_cost_intermediates |
self.DYNAMIC |
[REG_imp, REG_exp, PROD_COMM, TRAD_COMM] |
CBAM incidence on intermediate flows |
cbam_cost_hh |
self.DYNAMIC |
[REG_imp, REG_exp, PROD_COMM, TRAD_COMM] |
CBAM incidence on household consumption |
cbam_cost_fcf |
self.DYNAMIC |
[REG_imp, REG_exp, PROD_COMM, TRAD_COMM] |
CBAM incidence on FCF |
cbam_cost_gov |
self.DYNAMIC |
[REG_imp, REG_exp, PROD_COMM, TRAD_COMM] |
CBAM incidence on government consumption |
new_IO |
IO_model.IND_BASE (end-of-year snapshot) |
[REG_imp, PROD_COMM, REG_exp, TRAD_COMM] |
IO table (z_bp, output, a_bp, a_tech); written at end of year, consumed as EXOG_VARS.IND_BASE at start of next year |
SourceCode/variables.pySourceCode/initiate_modules.pySourceCode/model_class.pySourceCode/ftt_power.py
- DataFrame (not
scipy.sparse) was chosen for the sparse store: easier groupby/merge/filter operations, direct parquet serialisation, and no need to manage index mappings scipy.sparseis imported inInputOutput.pybut is not used — actual matrix conversion goes DataFrame → dense numpy via.unstack().to_numpy()new_IOis not year-indexed in the original code but is effectively year-indexed (written at end of year Y, read asyear-1at start of year Y+1); migrated accordinglyftt_power.pyusesread_var_df('energy_flows', year-1)and a read-modify-write pattern forenergy_flowsin the same year (reads, calls.update(), writes back)
emission_cost_intermediates/hh/fcf/gov and cbam_cost_intermediates/hh/fcf/gov followed a write → internal use → write again pattern inside initiate_modules. The initial write (pre-loop) was immediately overwritten by the first iteration of the within-year convergence loop. The pre-loop writes were replaced with local Python dicts (emission_cost_dfs, cbam_cost_dfs), eliminating 8 unnecessary parquet writes per simulation year.
- Pre-loop: calculate tax/cbam incidence →
self.V.write_var_df('emission_cost_*', year, ...)× 4,self.V.write_var_df('cbam_cost_*', year, ...)× 4 - Pre-loop reads:
self.V.read_var_df('emission_cost_*', year)andself.V.read_var_df('cbam_cost_*', year)in tax_rev, price, and HH sections - Iteration loop: recalculate and
self.V.write_var_df(...)again on every iteration
- Pre-loop: calculate → store in
emission_cost_dfs/cbam_cost_dfsdicts (no V write) - Pre-loop reads: access
emission_cost_dfs['intermediates']etc. directly - Iteration loop: recalculate and
self.V.write_var_df(...)as before (first and only write to V)
The iteration loop's emission_cost_old capture uses emission_cost_dfs['intermediates'] on the first iteration and reads from self.V on subsequent iterations (after the loop's own write).
If SWITCH_WITHIN_YEAR_LOOP = False, the loop body never executes and self.V would never receive the values. A post-loop block writes the local dicts to self.V in this case to ensure cross-year reads remain valid.
SourceCode/initiate_modules.py