Skip to content

Commit

Permalink
Support scenario specific point source files
Browse files Browse the repository at this point in the history
  • Loading branch information
dirkvdb committed Feb 28, 2023
1 parent c9a23b5 commit 0eb6556
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 11 deletions.
2 changes: 1 addition & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Release 3.x.x
-------------
- Added `separate_point_sources` output config option to configure wheter point sources should be output separately for chimere grids
- Added `scenario` model config option that causes to first search for input files with the `_scenario` suffix
- Added `scenario` model config option that causes to first search for input files with the `_scenario` suffix, for point sources the scnario name is check as infix: emap_{scenario}_{pollutant}_{year}_*.csv
- Support scaling of the emissions throug the scaling input file
- Support automatic scaling of the point sources when they exceed the reported total emissions

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ This section configures the model run
- `report_year` the report year of the emission data of the model run
- `spatial_pattern_exceptions` path to an xlsx file in which exceptions for spatial patterns are configured. These exceptions overrule the standard rules for spatial patterns.
- `included_pollutants` List of pollutants to include in the model run, this setting is optional, if it is not present all the configured pollutants will be included in the run.
- `scenario` First search for input files with the `_scenario` suffix before using the default input files, allows easy creation of scenarios with modified input files
- `scenario` First search for emission input files with the `_scenario` suffix before using the default emission input files, allows easy creation of scenarios with modified input files. For point sources `emap_{scenario}_{pollutant}_{year}_*.csv` is checked before `emap_{pollutant}_{year}_*.csv`
- `point_source_rescale_threshold` The threshold for allowing automatic rescaling of point sources when they exceed the reported total emissions [0 - 100]

### Output section
Expand Down
39 changes: 31 additions & 8 deletions logic/emissioninventory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,18 +348,41 @@ EmissionInventory create_emission_inventory(SingleEmissions totalEmissionsNfr,

static SingleEmissions read_country_pollutant_point_sources(const fs::path& dir, const Pollutant& pol, const RunConfiguration& cfg, RunSummary& runSummary)
{
auto match = fmt::format("emap_{}_{}_", pol.code(), static_cast<int>(cfg.year()));
// first check if emap_{scenario}_{pol}_{year}_ exists
// otherwise use emap_{pol}_{year}_

SingleEmissions result(cfg.year());
auto scenarioMatch = std::string();
auto match = fmt::format("emap_{}_{}_", pol.code(), static_cast<int>(cfg.year()));
if (auto scenario = cfg.scenario(); !scenario.empty()) {
scenarioMatch = fmt::format("emap_{}_{}_{}_", scenario, pol.code(), static_cast<int>(cfg.year()));
}

std::unordered_set<fs::path> pathsToUse;

for (auto iter : fs::directory_iterator(dir)) {
if (iter.is_regular_file() && iter.path().extension() == ".csv") {
const auto& path = iter.path();
if (str::starts_with(path.filename().u8string(), match)) {
merge_unique_emissions(result, parse_point_sources(path, cfg));
runSummary.add_point_source(path);
auto insertMatchingFiles = [&pathsToUse](std::string_view match, const fs::path& dir) {
for (auto iter : fs::directory_iterator(dir)) {
if (iter.is_regular_file() && iter.path().extension() == ".csv") {
const auto& path = iter.path();
if (str::starts_with(path.filename().u8string(), match)) {
pathsToUse.insert(iter.path());
}
}
}
};

if (!scenarioMatch.empty()) {
// Find scenario specific matches
insertMatchingFiles(scenarioMatch, dir);
}

if (pathsToUse.empty()) {
insertMatchingFiles(match, dir);
}

SingleEmissions result(cfg.year());
for (auto& path : pathsToUse) {
merge_unique_emissions(result, parse_point_sources(path, cfg));
runSummary.add_point_source(path);
}

return result;
Expand Down
1 change: 1 addition & 0 deletions logic/include/emap/modelpaths.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class ModelPaths
fs::path scalings_path() const;

const fs::path& data_root() const noexcept;
void set_data_root(const fs::path& root);
const fs::path& output_path() const noexcept;
fs::path boundaries_vector_path() const noexcept;
fs::path eez_boundaries_vector_path() const noexcept;
Expand Down
2 changes: 2 additions & 0 deletions logic/include/emap/runconfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ class RunConfiguration
fs::path scalings_path() const;

const fs::path& data_root() const noexcept;
void set_data_root(const fs::path& root);

const fs::path& output_path() const noexcept;
const fs::path& spatial_pattern_exceptions() const noexcept;
fs::path boundaries_vector_path() const noexcept;
Expand Down
5 changes: 5 additions & 0 deletions logic/modelpaths.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ const fs::path& ModelPaths::data_root() const noexcept
return _dataRoot;
}

void ModelPaths::set_data_root(const fs::path& root)
{
_dataRoot = root;
}

const fs::path& ModelPaths::output_path() const noexcept
{
return _outputRoot;
Expand Down
5 changes: 5 additions & 0 deletions logic/runconfiguration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ const fs::path& RunConfiguration::data_root() const noexcept
return _paths.data_root();
}

void RunConfiguration::set_data_root(const fs::path& root)
{
_paths.set_data_root(root);
}

const fs::path& RunConfiguration::output_path() const noexcept
{
return _paths.output_path();
Expand Down
5 changes: 5 additions & 0 deletions logic/runsummary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,11 @@ void RunSummary::write_summary(const fs::path& outputDir) const
write_summary_spreadsheet(outputDir / "summary.xlsx");
}

const std::set<fs::path>& RunSummary::used_point_sources() const noexcept
{
return _pointSources;
}

void RunSummary::write_summary_spreadsheet(const fs::path& path) const
{
std::error_code ec;
Expand Down
2 changes: 2 additions & 0 deletions logic/runsummary.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class RunSummary

void write_summary(const fs::path& outputDir) const;

const std::set<fs::path>& used_point_sources() const noexcept;

private:
struct GnfrCorrection
{
Expand Down
42 changes: 41 additions & 1 deletion logic/test/emissioninventorytest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "emap/configurationparser.h"
#include "emap/scalingfactors.h"

#include "infra/tempdir.h"
#include "runsummary.h"
#include "testconfig.h"
#include "testconstants.h"
Expand All @@ -21,7 +22,13 @@ static RunConfiguration create_config(const SectorInventory& sectorInv, const Po
outputConfig.path = "./out";
outputConfig.outputLevelName = "GNFR";

return RunConfiguration("./data", {}, ModelGrid::Invalid, ValidationType::NoValidation, 2016_y, 2021_y, "", 100.0, {}, sectorInv, pollutantInv, countryInv, outputConfig);
return RunConfiguration("./data", {}, ModelGrid::ChimereCams, ValidationType::NoValidation, 2016_y, 2021_y, "test", 100.0, {}, sectorInv, pollutantInv, countryInv, outputConfig);
}

static void create_empty_point_source_file(const fs::path& path)
{
const auto csvHeader = "type;scenario;year;reporting_country;nfr_sector;pollutant;emission;unit;x;y;hoogte_m;diameter_m;temperatuur_C;warmteinhoud_MW;debiet_Nm3/u;dv;type_emissie;EIL_nummer;exploitatie_naam;NACE_code;EIL_Emissiepunt_Jaar_Naam;Activiteit_type";
file::write_as_text(path, csvHeader);
}

TEST_CASE("Emission inventory")
Expand Down Expand Up @@ -166,6 +173,39 @@ TEST_CASE("Emission inventory")
checkEmission(inv, EmissionIdentifier(countries::DE, EmissionSector(sectors::nfr::Nfr1A3ai_i), pollutants::As), 111.0, 0.0);
checkEmission(inv, EmissionIdentifier(countries::DE, EmissionSector(sectors::nfr::Nfr1A3aii_i), pollutants::As), 222.0, 0.0);
}

SUBCASE("Use scenario point sources if present")
{
TempDir temp("pointscenario");

// Modify the data root, so we can change the available point source files
cfg.set_data_root(temp.path());

const auto pointSourcesPath = temp.path() / "01_data_emissions" / "inventory" / "reporting_2021" / "pointsources" / "BEF";

SUBCASE("Both scenario and non scenario available")
{
const auto pm10ScenarioPath1 = pointSourcesPath / fmt::format("emap_test_PM10_{}_something.csv", static_cast<int>(cfg.year()));
const auto pm10ScenarioPath2 = pointSourcesPath / fmt::format("emap_test_PM10_{}_something_else.csv", static_cast<int>(cfg.year()));
const auto pm10OtherScenarioPath = pointSourcesPath / fmt::format("emap_test2_PM10_{}_something.csv", static_cast<int>(cfg.year()));
const auto pm10NonScenarioPath = pointSourcesPath / fmt::format("emap_PM10_{}_something.csv", static_cast<int>(cfg.year()));
const auto noxNonScenarioPath = pointSourcesPath / fmt::format("emap_NOx_{}_something.csv", static_cast<int>(cfg.year()));

create_empty_point_source_file(pm10ScenarioPath1);
create_empty_point_source_file(pm10ScenarioPath2);
create_empty_point_source_file(pm10OtherScenarioPath);
create_empty_point_source_file(pm10NonScenarioPath);
create_empty_point_source_file(noxNonScenarioPath);

RunSummary summary;
read_country_point_sources(cfg, countries::BEF, summary);

CHECK(summary.used_point_sources().size() == 3);
CHECK(summary.used_point_sources().count(pm10ScenarioPath1) == 1); // The PM10 scenario specific file should be used, the other PM10 files are ignored
CHECK(summary.used_point_sources().count(pm10ScenarioPath2) == 1); // The PM10 scenario specific file should be used, the other PM10 files are ignored
CHECK(summary.used_point_sources().count(noxNonScenarioPath) == 1); // The regular NOx file will be used as there is no scenario specific one
}
}
}

}

0 comments on commit 0eb6556

Please sign in to comment.