Skip to content

Scenario.add_par("duration_period", …) has no effect with JDBCBackend #567

@khaeru

Description

@khaeru

Working on iiasa/message_ix#924, I ran into the following:

>>> import ixmp
>>> import message_ix

>>> mp = ixmp.Platform()
>>> s1 = message_ix.Scenario(mp, "model name", "scenario name", "new")
>>> s1.add_horizon([690, 719, 720], firstmodelyear=719)
>>> s1.par("duration_period")
   year  value unit
0   690    1.0    y
1   719   29.0    y
2   720    1.0    y

>>> s2 = Scenario(mp, "model name", "scenario name", "new")
>>> s2.add_horizon([690, 700, 720], firstmodelyear=700)
>>> s2.par("duration_period")
   year  value unit
0   690   10.0    y
1   700   10.0    y
2   720   20.0    y

Here the duration of the period 690 is different, but I am writing tests for which it should be the same. No problem: I explicitly set the value and then commit:

>>> dp = "duration_period"
>>> s1.add_par(dp, make_df(dp, year=690, value=10.0, unit="y"))
>>> s1.par("duration_period")
   year  value unit
0   690   10.0    y
1   719   29.0    y
2   720    1.0    y

>>> s1.add_set("technology", "t")  # else commit() raises an exception
>>> s1.commit("")
>>> s1.par("duration_period")
   year  value unit
0   690   10.0    y
1   719   29.0    y
2   720    1.0    y

To this point, all good. However:

>>> try:  # Don't actually solve
...     s1.solve()
... except Exception:
...     pass

>>> s1.par("duration_period")
   year  value unit
0   690   29.0    y
1   719   29.0    y
2   720    1.0    y

The value has been mutated from 10 to 29.

What's happening

This code (since it is in the private ixmp_source, I copy it here for clarity) always runs:

// initialize parameter period_duration
Parameter duration_period = getPar("duration_period");

// default duration of a period is 1 (relevant if set years has only one element)
for (int i = 1; i < years.size(); i++) {
	int dur = years.get(i) - years.get(i - 1);
	duration_period.addElement("" + years.get(i), (double) dur, "y");
}

// assume the first period lasts as long as the second
if (years.size() > 1) {
	duration_period.addElement("" + years.get(0), (double) (years.get(1) - years.get(0)), "y");
} else {
	duration_period.addElement("" + years.get(0), (double) (1), "y");
}

Thus with ixmp.JDBCBackend it is never possible to explicitly set duration_period for the first period (entry in the year set).

Workarounds

  1. In Fix historic vintages in map_tec_lifetime message_ix#924, I add an additional historical period (year = 680) that 'absorbs' this effect, with duration_period(year=680) never relied on for a specific value. This allows that duration_period(year=690) can have a defined value, regardless of other entries in the year set.

Since ixmp_source is unmaintained, this bug is essentially 'wontfix', along with some other sub-issues of iiasa/message_ix#254. I note it here mainly for clarity. We can however do the following:

  1. In implementing the new IXMP4Backend, not reproduce this behaviour.
  2. In message_ix, add a warning tries to set such a value, to the effect that:
    • if the user is using JDBCBackend, the value will be overwritten.
    • if the user is using IXMP4Backend, it will succeed, but the code may not be portable to JDBCBackend.

Metadata

Metadata

Assignees

No one assigned

    Labels

    backend.jdbcInteraction with ixmp_source via JDBCBackend & JPypebugwontfix

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions