Eclipse SUMO - Simulation of Urban MObility
CharacteristicMap.cpp
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 // Copyright (C) 2002-2024 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 /****************************************************************************/
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 
29 
30 
31 // ===========================================================================
32 // method definitions
33 // ===========================================================================
34 void
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 
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 
189 }
190 
191 
192 std::string
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
219  return domainDim;
220 }
221 
222 
223 int
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 }
int imageDim
Image dimension of the map.
int calcFlatIdx(const std::vector< int > &ref_idxs) const
Compute the index of a map entry in the flattened map.
CharacteristicMap(int domainDim, int imageDim, const std::vector< std::vector< double >> &ref_axes, const std::vector< double > &ref_flattenedMap)
Constructor.
std::vector< int > strides
Stride for each domain dimension in the flattened map.
std::vector< double > flattenedMap
Flattened map entries.
std::vector< double > eval(const std::vector< double > &ref_p, double eps=1e-6) const
Evaluate a point in the map using linear interpolation.
int getDomainDim() const
Get the dimension of the map's domain.
std::vector< std::vector< double > > axes
Vector containing the values along each domain axis in ascending order.
int findNearestNeighborIdxs(const std::vector< double > &ref_p, std::vector< int > &ref_idxs, double eps=1e-6) const
Determine the indices of the nearest neighbor of a point in the map.
void determineStrides()
Determine the stride for each map dimension in the flattened map.
std::vector< double > at(const std::vector< int > &ref_idxs) const
Access a map entry using its indices.
int domainDim
Dimension of the map's domain.
std::string toString() const
Encode the map as a string.
int getImageDim() const
Get the image dimension of the map.
std::vector< std::string > getVector()
return vector of strings
NLOHMANN_BASIC_JSON_TPL_DECLARATION std::string to_string(const NLOHMANN_BASIC_JSON_TPL &j)
user-defined to_string function for JSON values
Definition: json.hpp:21838