|
1 | 1 | from typing import TYPE_CHECKING
|
2 | 2 |
|
| 3 | +import pandas as pd |
3 | 4 | import pytest
|
4 | 5 |
|
5 | 6 | from message_ix_models.project.ssp.transport import main
|
| 7 | +from message_ix_models.tests.tools.iea.test_web import user_local_data # noqa: F401 |
6 | 8 | from message_ix_models.util import package_data_path
|
7 | 9 |
|
8 | 10 | if TYPE_CHECKING:
|
@@ -33,19 +35,109 @@ def input_xlsx_path(tmp_path_factory, input_csv_path) -> "pathlib.Path":
|
33 | 35 | return result
|
34 | 36 |
|
35 | 37 |
|
| 38 | +# Enumeration/flags for codes appearing in VARIABLE |
| 39 | +IN_ = 1 # Data appears in the input file only |
| 40 | +OUT = 2 # Data appears in the output file *and* with a modified magnitude |
| 41 | +I_O = IN_ | OUT # Both |
| 42 | + |
| 43 | +edt = "Energy|Demand|Transportation" |
| 44 | + |
| 45 | +#: IAMC 'Variable' codes appearing in the input file and/or output file. |
| 46 | +VARIABLE = { |
| 47 | + (0, f"Emissions|CH4|{edt}"), |
| 48 | + (OUT, f"Emissions|CH4|{edt}|Aviation"), |
| 49 | + (0, f"Emissions|CH4|{edt}|Aviation|International"), |
| 50 | + (I_O, f"Emissions|CH4|{edt}|Road Rail and Domestic Shipping"), |
| 51 | + # No emissions factors available for BC |
| 52 | + (IN_, f"Emissions|BC|{edt}"), |
| 53 | + (IN_, f"Emissions|BC|{edt}|Aviation"), |
| 54 | + (IN_, f"Emissions|BC|{edt}|Aviation|International"), |
| 55 | + # |
| 56 | + (I_O, f"Emissions|CO|{edt}|Aviation"), |
| 57 | + (I_O, f"Emissions|CO|{edt}|Road Rail and Domestic Shipping"), |
| 58 | + # |
| 59 | + (OUT, f"Emissions|N2O|{edt}|Aviation"), |
| 60 | + (I_O, f"Emissions|N2O|{edt}|Road Rail and Domestic Shipping"), |
| 61 | + # |
| 62 | + (IN_, f"Emissions|NH3|{edt}"), |
| 63 | + (IN_, f"Emissions|NH3|{edt}|Aviation"), |
| 64 | + (IN_, f"Emissions|NH3|{edt}|Aviation|International"), |
| 65 | + # |
| 66 | + (I_O, f"Emissions|NOx|{edt}|Aviation"), |
| 67 | + (I_O, f"Emissions|NOx|{edt}|Road Rail and Domestic Shipping"), |
| 68 | + # |
| 69 | + (IN_, f"Emissions|OC|{edt}"), |
| 70 | + (IN_, f"Emissions|OC|{edt}|Aviation"), |
| 71 | + (IN_, f"Emissions|OC|{edt}|Aviation|International"), |
| 72 | + # FIXME Should be OUT |
| 73 | + (0, f"Emissions|Sulfur|{edt}|Aviation"), |
| 74 | + # |
| 75 | + (IN_, f"Emissions|VOC|{edt}"), |
| 76 | + (I_O, f"Emissions|VOC|{edt}|Aviation"), |
| 77 | + (IN_, f"Emissions|VOC|{edt}|Aviation|International"), |
| 78 | + (I_O, f"Emissions|VOC|{edt}|Road Rail and Domestic Shipping"), |
| 79 | +} |
| 80 | + |
| 81 | + |
36 | 82 | @main.minimum_version
|
37 |
| -def test_main(tmp_path, input_csv_path) -> None: |
| 83 | +# @pytest.mark.usefixtures("user_local_data") |
| 84 | +@pytest.mark.parametrize("method", ("A", "B")) |
| 85 | +def test_main(tmp_path, test_context, input_csv_path, method) -> None: |
38 | 86 | """Code can be called from Python."""
|
39 | 87 | # Locate a temporary data file
|
40 | 88 | path_in = input_csv_path
|
41 | 89 | path_out = tmp_path.joinpath("output.csv")
|
42 | 90 |
|
43 | 91 | # Code runs
|
44 |
| - main(path_in=path_in, path_out=path_out) |
| 92 | + main(path_in=path_in, path_out=path_out, method=method) |
45 | 93 |
|
46 | 94 | # Output path exists
|
47 | 95 | assert path_out.exists()
|
48 | 96 |
|
| 97 | + # Read input file |
| 98 | + df_in = pd.read_csv(path_in) |
| 99 | + |
| 100 | + # Identify dimension columns |
| 101 | + dims_wide = list(df_in.columns)[:5] # …in 'wide' layout |
| 102 | + dims = dims_wide + ["Year"] # …in 'long' layout |
| 103 | + |
| 104 | + # Convert wide to long; sort |
| 105 | + df_in = df_in.melt(dims_wide, var_name="Year").sort_values(dims) |
| 106 | + |
| 107 | + # Input data already contains the variable names to be modified |
| 108 | + exp = {v[1] for v in VARIABLE if IN_ & v[0]} |
| 109 | + assert exp <= set(df_in["Variable"].unique()) |
| 110 | + region = set(df_in["Region"].unique()) |
| 111 | + |
| 112 | + # Read output file |
| 113 | + df_out = pd.read_csv(path_out).melt(dims_wide, var_name="Year") |
| 114 | + |
| 115 | + # Data have the same length |
| 116 | + assert len(df_in) == len(df_out) |
| 117 | + |
| 118 | + # Output has the same set of region codes as input |
| 119 | + assert region == set(df_out["Region"].unique()) |
| 120 | + |
| 121 | + # Diff data: |
| 122 | + # - Outer merge. |
| 123 | + # - Compute diff and select rows where diff is larger than a certain value |
| 124 | + df = df_in.merge(df_out, how="outer", on=dims).query( |
| 125 | + "abs(value_y - value_x) > 1e-16" |
| 126 | + ) |
| 127 | + |
| 128 | + # df.to_csv("debug0.csv") # DEBUG Dump to file |
| 129 | + # print(df.to_string(max_rows=50)) # DEBUG Show in test output |
| 130 | + |
| 131 | + # All of the expected 'variable' codes have been modified |
| 132 | + exp = {v[1] for v in VARIABLE if OUT & v[0]} |
| 133 | + assert exp == set(df["Variable"].unique()) |
| 134 | + |
| 135 | + cond = df.query("value_y < 0") |
| 136 | + if len(cond): |
| 137 | + msg = "Negative emissions totals after processing" |
| 138 | + print(f"\n{msg}:", cond.to_string(), sep="\n") |
| 139 | + assert False, msg |
| 140 | + |
49 | 141 |
|
50 | 142 | @main.minimum_version
|
51 | 143 | def test_cli(tmp_path, mix_models_cli, input_xlsx_path) -> None:
|
|
0 commit comments