Use XML-specified `<service-pre-tech-change>` as the per-period pre-tech anchor in `EnergyFinalDemand`
Use XML-specified <service-pre-tech-change> as the per-period pre-tech anchor in EnergyFinalDemand
Summary
Make <service-pre-tech-change> an authoritative, per-period pre-tech anchor for EnergyFinalDemand.
If the user supplies a value in XML for period t, we preserve it and use it as the base for computing service in t+1.
If not supplied, behavior is unchanged (pre-tech continues to evolve from t-1 × macro scaler).
Why
Currently, even when <service-pre-tech-change> is present, mPreTechChangeServiceDemand[t] is regenerated from the previous period, so user inputs are effectively ignored. For workflows with external industry/sector outlooks, users need to pin the level of pre-tech service in specific years and let GCAM dynamics resume thereafter.
Behavior (before → after)
-
Before
-
preTech[t] = preTech[t-1] * macroScaler[t]always. - Any XML value for
service-pre-tech-change[t]is overwritten during solve.
-
-
After (this change)
- If XML provides
service-pre-tech-change[t]: keep it aspreTech[t](do not overwrite); it becomes the base used to computeservice[t+1]. - If not provided: fall back to existing behavior.
- If XML provides
This is backward-compatible for models that don’t supply the tag.
Files / functions touched
-
objects/sectors/source/energy_final_demand.h- Make
<service-pre-tech-change>parsable (removeNOT_PARSABLE). - Add
private: std::vector<bool> mPreTechWasParsed;to track, per period, whether XML provided a value.
- Make
-
objects/sectors/source/energy_final_demand.cpp-
completeInit(...)- Size/fill
mPreTechWasParsed(getmaxper(); each entry frommPreTechChangeServiceDemand[per].isInited()). - Base year: set
preTech[0]frombase-service[0]only if the user did not providepreTech[0].
- Size/fill
-
calcFinalDemand(...)-
Calibration periods:
preTech[t] = base-service[t]only if not user-provided. -
Post-calibration:
service[t] = preTech[t-1] * macroScaler[t](0 ifpreTech[t-1] <= 0); updatepreTech[t] = service[t]only if not user-provided; then apply tech change as before.
-
Calibration periods:
-
initCalc(...)- Add one-time per period
NOTICElogs (to bothstdoutandmain_log) like:[EnergyFinalDemand:<mName>] region=<Region> year=<Y> : preserved user pre-tech=<value> - will affect service in <Y+Δ>
- Add one-time per period
-
Example XML
<scenario>
<world>
<region name="South Korea">
<energy-final-demand name="chemical">
<base-service year="1975">0.0471943</base-service>
<base-service year="1990">0.3690912</base-service>
<base-service year="2005">1.4055232</base-service>
<base-service year="2010">1.7611064</base-service>
<base-service year="2015">2.1527168</base-service>
<base-service year="2021">2.4223745</base-service>
<service-pre-tech-change year="2021">2.53</service-pre-tech-change>
<service-pre-tech-change year="2025">2.49</service-pre-tech-change>
<service-pre-tech-change year="2030">2.27</service-pre-tech-change>
<service-pre-tech-change year="2035">1.93</service-pre-tech-change>
<service-pre-tech-change year="2040">1.52</service-pre-tech-change>
<service-pre-tech-change year="2045">1.11</service-pre-tech-change>
</energy-final-demand>
</region>
</world>
</scenario>
2021/2025/2030/.../2045 preTech[t] are preserved as entered and used as bases for computing service in the following periods.
Nice—here’s a clean, ready-to-paste section for your issue:
Console log example (one NOTICE per period)
The following is stdout with per-period notices when <service-pre-tech-change> is provided for South Korea / chemical. Each notice appears once in initCalc for that period and indicates that the user pre-tech value is preserved and will anchor the next period’s service:
XML parsing complete.
Starting new scenario: Base-Service-Test
Tue Aug 26 14:07:16 2025:WARNING:printLogHeader: hector version 3.2.0
Tue Aug 26 14:07:16 2025:WARNING:printLogHeader: hector version 3.2.0
Starting a model run. Running period 11
Model run beginning.
Period 0: 1975
Model solved with last period's prices.
Period 1: 1990
Model solved normally. Iterations period 1: 409. Total iterations: 410
Period 2: 2005
Model solved normally. Iterations period 2: 173. Total iterations: 582
Period 3: 2010
Model solved normally. Iterations period 3: 218. Total iterations: 800
Period 4: 2015
Model solved normally. Iterations period 4: 186. Total iterations: 986
Period 5: 2021
Creating an uninitialized logger stdout
[EnergyFinalDemand:chemical] region=South Korea period=5 (2021) : preserved user pre-tech=2.53 - will affect 2025
Model solved normally. Iterations period 5: 157. Total iterations: 1142
Period 6: 2025
[EnergyFinalDemand:chemical] region=South Korea period=6 (2025) : preserved user pre-tech=2.49 - will affect 2030
Model solved normally. Iterations period 6: 2029. Total iterations: 3170
Period 7: 2030
[EnergyFinalDemand:chemical] region=South Korea period=7 (2030) : preserved user pre-tech=2.27 - will affect 2035
Model solved normally. Iterations period 7: 479. Total iterations: 3649
Period 8: 2035
[EnergyFinalDemand:chemical] region=South Korea period=8 (2035) : preserved user pre-tech=1.93 - will affect 2040
Model solved normally. Iterations period 8: 820. Total iterations: 4468
Period 9: 2040
[EnergyFinalDemand:chemical] region=South Korea period=9 (2040) : preserved user pre-tech=1.52 - will affect 2045
Model solved normally. Iterations period 9: 396. Total iterations: 4864
Period 10: 2045
[EnergyFinalDemand:chemical] region=South Korea period=10 (2045) : preserved user pre-tech=1.11 - will affect 2050
Model solved normally. Iterations period 10: 219. Total iterations: 5083
Period 11: 2050
Model solved normally. Iterations period 11: 597. Total iterations: 5679
Notes / questions
- Do we want a warning if a user enters non-positive
service-pre-tech-change[t](since that zeroes the next period’s service)? - Is the current opt-in (supplying the tag) sufficient, or should a separate switch be added?
If there’s agreement on the approach, I can open a minimal PR.