Repository: IAMconsortium/nomenclature

Issue:
Implement processor to "undo" region processing
It can be useful/required to "undo" or reverse the renaming done as part of the region-processing, so taking the model-native-in-ensemble-region-names and rename to model-native-as-used-originally-region-names.

My hunch is that this would be better implemented as a new type of processor (instead of an argument of `RegionProcessor.apply()` or a new method there), so that we can easily call a list of processors using

```python
process(IamDataFrame, DataStructureDefinition, processor=[ReverseRegionProcessor, SomeOtherProcessor])
```

# Question to be discussed

Whether to drop or keep common regions as part of the operation, and (if that would be optional) how to configure/specify this...

Discussion:
phackstock: Updated to `RegionProcessor.revert()`

Patch:
diff --git a/nomenclature/processor/region.py b/nomenclature/processor/region.py
index 918e6d7..3c6e1b6 100644
--- a/nomenclature/processor/region.py
+++ b/nomenclature/processor/region.py
@@ -292,6 +292,10 @@ class RegionAggregationMapping(BaseModel):
     def rename_mapping(self) -> Dict[str, str]:
         return {r.name: r.target_native_region for r in self.native_regions or []}
 
+    @property
+    def reverse_rename_mapping(self) -> Dict[str, str]:
+        return {renamed: original for original, renamed in self.rename_mapping.items()}
+
     def validate_regions(self, region_codelist: RegionCodeList) -> None:
         if invalid := region_codelist.validate_items(self.all_regions):
             raise RegionNotDefinedError(region=invalid, file=self.file)
@@ -599,6 +603,18 @@ class RegionProcessor(Processor):
         # cast processed timeseries data and meta indicators to IamDataFrame
         return IamDataFrame(_data, meta=model_df.meta), difference
 
+    def revert(self, df: pyam.IamDataFrame) -> pyam.IamDataFrame:
+        model_dfs = []
+        for model in df.model:
+            model_df = df.filter(model=model)
+            if mapping := self.mappings.get(model):
+                # remove common regions, then apply inverse-renaming of native-regions
+                model_df = model_df.filter(
+                    region=mapping.common_region_names, keep=False
+                ).rename(region=mapping.reverse_rename_mapping)
+            model_dfs.append(model_df)
+        return pyam.concat(model_dfs)
+
 
 def _aggregate_region(df, var, *regions, **kwargs):
     """Perform region aggregation with kwargs catching inconsistent-index errors"""


Test patch:
diff --git a/tests/test_region_aggregation.py b/tests/test_region_aggregation.py
index b2f60b8..c11bbab 100644
--- a/tests/test_region_aggregation.py
+++ b/tests/test_region_aggregation.py
@@ -10,7 +10,7 @@ from nomenclature import (
     process,
 )
 from nomenclature.error.region import RegionAggregationMappingParsingError
-from pyam import IamDataFrame
+from pyam import IamDataFrame, assert_iamframe_equal
 from pyam.utils import IAMC_IDX
 
 from conftest import TEST_DATA_DIR, clean_up_external_repos
@@ -244,3 +244,34 @@ def test_mapping_from_external_repository():
         )
     finally:
         clean_up_external_repos(dsd.config.repositories)
+
+
+def test_reverse_region_aggregation():
+    processor = RegionProcessor.from_directory(
+        TEST_DATA_DIR / "region_processing" / "complete_processing_list",
+        DataStructureDefinition(TEST_DATA_DIR / "region_processing/dsd"),
+    )
+
+    obs = processor.revert(
+        IamDataFrame(
+            pd.DataFrame(
+                [
+                    ["m_a", "s_a", "World", "Primary Energy", "EJ/yr", 1, 6.0],
+                    ["m_a", "s_a", "region_A", "Primary Energy", "EJ/yr", 0.5, 3],
+                    ["m_a", "s_b", "region_B", "Primary Energy", "EJ/yr", 2, 7],
+                    ["m_c", "s_a", "World", "Primary Energy", "EJ/yr", 1, 2],
+                ],
+                columns=IAMC_IDX + [2005, 2010],
+            )
+        )
+    )
+    exp = IamDataFrame(
+        pd.DataFrame(
+            [
+                ["m_a", "s_a", "region_a", "Primary Energy", "EJ/yr", 0.5, 3],
+                ["m_a", "s_b", "region_B", "Primary Energy", "EJ/yr", 2, 7],
+            ],
+            columns=IAMC_IDX + [2005, 2010],
+        ),
+    )
+    assert_iamframe_equal(obs, exp)