Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/emissions/CharacteristicMap.cpp
169678 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2002-2025 German Aerospace Center (DLR) and others.
4
// This program and the accompanying materials are made available under the
5
// terms of the Eclipse Public License 2.0 which is available at
6
// https://www.eclipse.org/legal/epl-2.0/
7
// This Source Code may also be made available under the following Secondary
8
// Licenses when the conditions for such availability set forth in the Eclipse
9
// Public License 2.0 are satisfied: GNU General Public License, version 2
10
// or later which is available at
11
// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
/****************************************************************************/
14
/// @file CharacteristicMap.cpp
15
/// @author Kevin Badalian ([email protected])
16
/// @date 2021-02
17
///
18
// Characteristic map for vehicle type parameters as needed by the MMPEVEM model
19
// Teaching and Research Area Mechatronics in Mobile Propulsion (MMP), RWTH Aachen
20
/****************************************************************************/
21
#include <config.h>
22
23
#include <cmath>
24
#include <cstring>
25
#include <stdexcept>
26
27
#include <utils/common/StringTokenizer.h>
28
#include <utils/emissions/CharacteristicMap.h>
29
30
31
// ===========================================================================
32
// method definitions
33
// ===========================================================================
34
void
35
CharacteristicMap::determineStrides() {
36
strides.clear();
37
strides.reserve(domainDim);
38
strides.push_back(imageDim);
39
for (int i = 1; i < domainDim; i++) {
40
strides.push_back((int)axes[i - 1].size() * strides[i - 1]);
41
}
42
}
43
44
45
int
46
CharacteristicMap::calcFlatIdx(const std::vector<int>& ref_idxs) const {
47
if (static_cast<int>(ref_idxs.size()) != domainDim) {
48
throw std::runtime_error("The number of indices differs from the map's"
49
" domain dimension.");
50
}
51
52
int flatIdx = 0;
53
for (int i = 0; i < domainDim; i++) {
54
if (ref_idxs[i] < 0) {
55
throw std::runtime_error("The argument indices aren't non-negative.");
56
}
57
flatIdx += ref_idxs[i] * strides[i];
58
}
59
return flatIdx;
60
}
61
62
63
int
64
CharacteristicMap::findNearestNeighborIdxs(const std::vector<double>& ref_p,
65
std::vector<int>& ref_idxs, double eps) const {
66
if (static_cast<int>(ref_p.size()) != domainDim) {
67
throw std::runtime_error("The argument point's size doesn't match the"
68
" domain dimension.");
69
}
70
71
ref_idxs = std::vector<int>(domainDim, -1);
72
for (int i = 0; i < domainDim; i++) {
73
if (axes[i][0] - eps <= ref_p[i] && ref_p[i] < axes[i][0]) {
74
ref_idxs[i] = 0;
75
} else if (axes[i][axes[i].size() - 1] <= ref_p[i]
76
&& ref_p[i] < axes[i][axes[i].size() - 1] + eps) {
77
ref_idxs[i] = (int)axes[i].size() - 1;
78
} else {
79
for (int j = 0; j < static_cast<int>(axes[i].size()) - 1; j++) {
80
if (axes[i][j] <= ref_p[i] && ref_p[i] < axes[i][j + 1]) {
81
// Pick the index that is closest to the point
82
if (ref_p[i] - axes[i][j] <= axes[i][j + 1] - ref_p[i]) {
83
ref_idxs[i] = j;
84
break;
85
} else {
86
ref_idxs[i] = j + 1;
87
break;
88
}
89
}
90
}
91
}
92
93
// The point lies outside of the valid range
94
if (ref_idxs[i] == -1) {
95
return -1;
96
}
97
}
98
99
return 0;
100
}
101
102
103
std::vector<double>
104
CharacteristicMap::at(const std::vector<int>& ref_idxs) const {
105
if (static_cast<int>(ref_idxs.size()) != domainDim) {
106
throw std::runtime_error("The number of indices differs from the map's"
107
" domain dimension.");
108
}
109
110
int flatIdx = calcFlatIdx(ref_idxs);
111
return std::vector<double>(flattenedMap.begin() + flatIdx,
112
flattenedMap.begin() + flatIdx + imageDim);
113
}
114
115
116
CharacteristicMap::CharacteristicMap(int domainDim, int imageDim,
117
const std::vector<std::vector<double>>& ref_axes,
118
const std::vector<double>& ref_flattenedMap)
119
: domainDim(domainDim),
120
imageDim(imageDim),
121
axes(ref_axes),
122
flattenedMap(ref_flattenedMap) {
123
// Check whether the dimensions are consistent
124
if (static_cast<int>(axes.size()) != domainDim) {
125
throw std::runtime_error("The number of axes doesn't match the specified"
126
" domain dimension.");
127
}
128
int expectedEntryCnt = imageDim;
129
for (auto& ref_axis : axes) {
130
expectedEntryCnt *= (int)ref_axis.size();
131
}
132
if (static_cast<int>(flattenedMap.size()) != expectedEntryCnt) {
133
throw std::runtime_error("The number of map entries isn't equal to the"
134
" product of the axes' dimensions times the image dimension.");
135
}
136
137
determineStrides();
138
}
139
140
141
CharacteristicMap::CharacteristicMap(const std::string& ref_mapString) {
142
// Split the map string into its three main parts
143
const std::vector<std::string> tokens = StringTokenizer(ref_mapString, "|").getVector();
144
if (tokens.size() != 3) {
145
throw std::runtime_error("The map string isn't made up of the 3 parts"
146
" dimensions, axes, and flattened entries.");
147
}
148
149
// Extract the domain and image dimensions
150
const std::vector<std::string> dimensionTokens = StringTokenizer(tokens[0], ",").getVector();
151
if (dimensionTokens.size() != 2) {
152
throw std::runtime_error("The domain and image dimensions aren't specified"
153
" correctly.");
154
}
155
domainDim = std::stoi(dimensionTokens[0]);
156
imageDim = std::stoi(dimensionTokens[1]);
157
158
// Create the map axes
159
const std::vector<std::string> axisTokens = StringTokenizer(tokens[1], ";").getVector();
160
if (static_cast<int>(axisTokens.size()) != domainDim) {
161
throw std::runtime_error("The number of axes doesn't match the specified"
162
" domain dimension.");
163
}
164
for (auto& ref_axisToken : axisTokens) {
165
std::vector<std::string> axisEntryTokens = StringTokenizer(ref_axisToken, ",").getVector();
166
std::vector<double> axisEntries;
167
for (auto& ref_axisEntryToken : axisEntryTokens) {
168
axisEntries.push_back(std::stod(ref_axisEntryToken));
169
}
170
axes.push_back(axisEntries);
171
}
172
173
// Create the flattened map
174
const std::vector<std::string> flattenedMapTokens = StringTokenizer(tokens[2], ",").getVector();
175
int expectedEntryCnt = imageDim;
176
for (auto& ref_axis : axes) {
177
expectedEntryCnt *= (int)ref_axis.size();
178
}
179
if (static_cast<int>(flattenedMapTokens.size()) != expectedEntryCnt) {
180
throw std::runtime_error("The number of map entries isn't equal to the"
181
" product of the axes' dimensions times the image dimension.");
182
}
183
flattenedMap.reserve(expectedEntryCnt);
184
for (auto& ref_flattenedMapToken : flattenedMapTokens) {
185
flattenedMap.push_back(std::stod(ref_flattenedMapToken));
186
}
187
188
determineStrides();
189
}
190
191
192
std::string
193
CharacteristicMap::toString() const {
194
// Write the domain and image dimensions
195
std::string mapString = std::to_string(domainDim) + ","
196
+ std::to_string(imageDim) + "|";
197
198
// Add the axes
199
for (int i = 0; i < static_cast<int>(axes.size()); i++) {
200
for (int j = 0; j < static_cast<int>(axes[i].size()); j++) {
201
mapString += std::to_string(axes[i][j])
202
+ (j == static_cast<int>(axes[i].size()) - 1 ? "" : ",");
203
}
204
mapString += (i == static_cast<int>(axes.size()) - 1 ? "|" : ";");
205
}
206
207
// Append the flattened map entries
208
for (int i = 0; i < static_cast<int>(flattenedMap.size()); i++) {
209
mapString += std::to_string(flattenedMap[i])
210
+ (i == static_cast<int>(flattenedMap.size()) - 1 ? "" : ",");
211
}
212
213
return mapString;
214
}
215
216
217
int
218
CharacteristicMap::getDomainDim() const {
219
return domainDim;
220
}
221
222
223
int
224
CharacteristicMap::getImageDim() const {
225
return imageDim;
226
}
227
228
229
std::vector<double>
230
CharacteristicMap::eval(const std::vector<double>& ref_p, double eps) const {
231
if (static_cast<int>(ref_p.size()) != domainDim) {
232
throw std::runtime_error("The argument's size doesn't match the domain"
233
" dimension.");
234
}
235
236
// Find the nearest neighbor and its image values
237
std::vector<int> nnIdxs;
238
if (findNearestNeighborIdxs(ref_p, nnIdxs, eps)) {
239
return std::vector<double>(imageDim, std::stod("nan"));
240
}
241
// Image values of the nearest neighbor
242
const std::vector<double> y_nn = at(nnIdxs);
243
// The result is based on the image values of the nearest neighbor
244
std::vector<double> y = y_nn;
245
246
// Interpolate
247
for (int i = 0; i < domainDim; i++) {
248
// Depending on the configuration of the points, different neighbors will be
249
// used for interpolation
250
const double s = ref_p[i] - axes[i][nnIdxs[i]];
251
if (std::abs(s) <= eps) {
252
continue;
253
}
254
bool b_constellation1 = s < 0 && nnIdxs[i] > 0;
255
bool b_constellation2 = s >= 0
256
&& nnIdxs[i] == static_cast<int>(axes[i].size()) - 1
257
&& nnIdxs[i] > 0;
258
bool b_constellation3 = s < 0 && nnIdxs[i] == 0
259
&& nnIdxs[i] < static_cast<int>(axes[i].size()) - 1;
260
bool b_constellation4 = s >= 0
261
&& nnIdxs[i] < static_cast<int>(axes[i].size()) - 1;
262
263
double dx = 1;
264
// Axis neighbor indices (i.e. the indices of the second support point)
265
std::vector<int> anIdxs = nnIdxs;
266
if (b_constellation1 || b_constellation2) {
267
anIdxs[i] -= 1;
268
dx = axes[i][nnIdxs[i]] - axes[i][anIdxs[i]];
269
} else if (b_constellation3 || b_constellation4) {
270
anIdxs[i] += 1;
271
dx = axes[i][anIdxs[i]] - axes[i][nnIdxs[i]];
272
} else {
273
continue;
274
}
275
// Image values of the axis neighbor
276
const std::vector<double> y_an = at(anIdxs);
277
278
for (int j = 0; j < imageDim; j++) {
279
double dy = 0;
280
if (b_constellation1 || b_constellation2) {
281
dy = y_nn[j] - y_an[j];
282
} else {
283
dy = y_an[j] - y_nn[j];
284
}
285
286
// Update
287
y[j] += s * dy / dx;
288
}
289
}
290
291
return y;
292
}
293
294