gcam-core icon indicating copy to clipboard operation
gcam-core copied to clipboard

Use XML-specified `<service-pre-tech-change>` as the per-period pre-tech anchor in `EnergyFinalDemand`

Open choiHenry opened this issue 4 months ago • 0 comments

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 as preTech[t] (do not overwrite); it becomes the base used to compute service[t+1].
    • If not provided: fall back to existing behavior.

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 (remove NOT_PARSABLE).
    • Add private: std::vector<bool> mPreTechWasParsed; to track, per period, whether XML provided a value.
  • objects/sectors/source/energy_final_demand.cpp

    • completeInit(...)

      • Size/fill mPreTechWasParsed (getmaxper(); each entry from mPreTechChangeServiceDemand[per].isInited()).
      • Base year: set preTech[0] from base-service[0] only if the user did not provide preTech[0].
    • calcFinalDemand(...)

      • Calibration periods: preTech[t] = base-service[t] only if not user-provided.
      • Post-calibration: service[t] = preTech[t-1] * macroScaler[t] (0 if preTech[t-1] <= 0); update preTech[t] = service[t] only if not user-provided; then apply tech change as before.
    • initCalc(...)

      • Add one-time per period NOTICE logs (to both stdout and main_log) like: [EnergyFinalDemand:<mName>] region=<Region> year=<Y> : preserved user pre-tech=<value> - will affect service in <Y+Δ>

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.

choiHenry avatar Aug 26 '25 04:08 choiHenry