Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/emissions/hbefa2sumo.py
169674 views
1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
4
# Copyright (C) 2022-2025 German Aerospace Center (DLR) and others.
5
# This program and the accompanying materials are made available under the
6
# terms of the Eclipse Public License 2.0 which is available at
7
# https://www.eclipse.org/legal/epl-2.0/
8
# This Source Code may also be made available under the following Secondary
9
# Licenses when the conditions for such availability set forth in the Eclipse
10
# Public License 2.0 are satisfied: GNU General Public License, version 2
11
# or later which is available at
12
# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
13
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
14
15
# @file hbefa2sumo.py
16
# @author Michael Behrisch
17
# @date 2022-06-14
18
19
# This script parses the coefficients for the HBEFA4 model and converts them to a cpp
20
# table and a markdown file for the docs. It also writes the list of classes.
21
# The inputs and some docs are in simo/projects/sumo/data/emissions/HBEFA4 (non-public).
22
23
import os
24
import sys
25
import csv
26
from collections import defaultdict
27
if 'SUMO_HOME' in os.environ:
28
sys.path.append(os.path.join(os.environ['SUMO_HOME'], 'tools'))
29
import sumolib # noqa
30
31
32
def get_options(args=None):
33
op = sumolib.options.ArgumentParser(description="Generate cpp file and docs from HBEFA4 csv data")
34
op.add_argument("csv", nargs='?', default="HBEFACoefs_ms.csv.gz",
35
help="define the input data file")
36
op.add_argument("-o", "--output-basename", dest="basename", default="HBEFA4",
37
help="define the base name of all output files")
38
op.add_argument("-p", "--pm-mode", choices=('sum', 'plain', 'non-exhaust'), default="sum",
39
help="how to handle particular matter values")
40
return op.parse_args(args=args)
41
42
43
def main(args=None):
44
options = get_options(args)
45
coeffs = defaultdict(dict)
46
errors = defaultdict(dict)
47
share = defaultdict(list)
48
id = {}
49
with sumolib.openz(options.csv, "rt") as data:
50
for line in csv.DictReader(data):
51
translation = {"<": "lt", ">": "gt", " ": "_", "'": "_", "*": "s", "&": "_", ";": "_", ",": "_", "/": "_"}
52
segment = line['Subsegment'].replace("<=", "le").translate(line['Subsegment'].maketrans(translation))
53
if segment[:2] == "PC" and line["VC"] != "PC":
54
continue
55
id[segment] = (line['Subsegment'], line["i"])
56
co = [line[c] for c in ('E0', 'V', 'A', 'V2', 'V3', 'AV', 'AV2')]
57
if line['e'] in coeffs[segment] and coeffs[segment][line['e']] != co:
58
print("Error! two data sets", segment)
59
else:
60
coeffs[segment][line['e']] = co
61
errors[segment][line['e']] = float(line['wmape'])
62
s = "%.4f%%" % (100 * float(line["Share"])) if line["Share"] != "NA" else "-"
63
if line["e"] == "FC":
64
share[line["VC"]].append((segment, s))
65
print(len(coeffs), [(vc, sum([float(si[1][:-1]) for si in s if si[1] != "-"])) for vc, s in share.items()])
66
67
wiki = options.basename + ".md"
68
cpp = options.basename + ".cpp"
69
txt = options.basename + "classes.txt"
70
classes = []
71
with open(wiki, "w") as wiki_out, open(cpp, "w") as cpp_out, open(txt, "w") as txt_out:
72
print("""
73
double
74
HelpersHBEFA4::myFunctionParameter[%s][7][7] = {""" % len(coeffs), file=cpp_out)
75
for vc, s in share.items():
76
print("##", vc, file=wiki_out)
77
print("| SUMO emission class | HBEFA subsegment | HBEFA subsegment ID | fleet share 2022 | "
78
"error CO2 | error CO | error HC | error FC | error NOx | error PM | error FC_MJ |", file=wiki_out)
79
print("| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |""", file=wiki_out)
80
for segment, val in s:
81
print(" {", file=cpp_out)
82
print(8 * " " + "//", segment, file=cpp_out)
83
err = []
84
for emi in ("CO2(total)", "CO", "HC", "FC", "NOx", "PM", "FC_MJ"):
85
scale = 1000.
86
if emi in errors[segment] and coeffs[segment][emi] != 7 * ["0"]:
87
err.append("%.2f%%" % (100 * errors[segment][emi]))
88
else:
89
err.append("-")
90
if emi == "CO2(total)" and emi not in coeffs[segment]:
91
emi = "CO2(rep)"
92
if emi == "PM" and "PM (non-exhaust)" in coeffs[segment] and options.pm_mode != "plain":
93
if options.pm_mode == "sum":
94
emi = "PM + PM (non-exhaust)"
95
coeffs[segment][emi] = [float(c1) + float(c2) for c1, c2 in
96
zip(coeffs[segment]["PM"], coeffs[segment]["PM (non-exhaust)"])]
97
else:
98
emi = "PM (non-exhaust)"
99
if "PM (non-exhaust)" in errors[segment]:
100
err[-1] += ", %.2f%%" % (100 * errors[segment]["PM (non-exhaust)"])
101
if emi == "FC_MJ":
102
if coeffs[segment]["FC"] != 7 * ["0"]:
103
scale = 0.
104
err[-1] = "-"
105
else:
106
scale = 1000. / 3.6
107
print(8 * " " + "{", ", ".join(["%.3e" % (scale * float(c))
108
for c in coeffs[segment][emi]]), "}, //", emi, file=cpp_out)
109
print(" },", file=cpp_out)
110
classes.append((segment, "" if vc in ('PC', 'LCV', 'MC') else " | PollutantsInterface::HEAVY_BIT"))
111
print("|", segment, "|", " | ".join(id[segment]), "|", val, "|", " | ".join(err), "|", file=wiki_out)
112
print("};", file=cpp_out)
113
114
print("""
115
HelpersHBEFA4::HelpersHBEFA4() : PollutantsInterface::Helper("HBEFA4", HBEFA4_BASE, -1) {
116
int index = HBEFA4_BASE;""", file=cpp_out)
117
print("HBEFA4/zero", file=txt_out)
118
for c in classes:
119
print(' myEmissionClassStrings.insert("%s", index%s); index++;' % c, file=cpp_out)
120
print("HBEFA4/%s" % c[0], file=txt_out)
121
122
123
if __name__ == "__main__":
124
main()
125
126