Path: blob/main/src/utils/emissions/CharacteristicMap.cpp
169678 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2002-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file CharacteristicMap.cpp14/// @author Kevin Badalian ([email protected])15/// @date 2021-0216///17// Characteristic map for vehicle type parameters as needed by the MMPEVEM model18// Teaching and Research Area Mechatronics in Mobile Propulsion (MMP), RWTH Aachen19/****************************************************************************/20#include <config.h>2122#include <cmath>23#include <cstring>24#include <stdexcept>2526#include <utils/common/StringTokenizer.h>27#include <utils/emissions/CharacteristicMap.h>282930// ===========================================================================31// method definitions32// ===========================================================================33void34CharacteristicMap::determineStrides() {35strides.clear();36strides.reserve(domainDim);37strides.push_back(imageDim);38for (int i = 1; i < domainDim; i++) {39strides.push_back((int)axes[i - 1].size() * strides[i - 1]);40}41}424344int45CharacteristicMap::calcFlatIdx(const std::vector<int>& ref_idxs) const {46if (static_cast<int>(ref_idxs.size()) != domainDim) {47throw std::runtime_error("The number of indices differs from the map's"48" domain dimension.");49}5051int flatIdx = 0;52for (int i = 0; i < domainDim; i++) {53if (ref_idxs[i] < 0) {54throw std::runtime_error("The argument indices aren't non-negative.");55}56flatIdx += ref_idxs[i] * strides[i];57}58return flatIdx;59}606162int63CharacteristicMap::findNearestNeighborIdxs(const std::vector<double>& ref_p,64std::vector<int>& ref_idxs, double eps) const {65if (static_cast<int>(ref_p.size()) != domainDim) {66throw std::runtime_error("The argument point's size doesn't match the"67" domain dimension.");68}6970ref_idxs = std::vector<int>(domainDim, -1);71for (int i = 0; i < domainDim; i++) {72if (axes[i][0] - eps <= ref_p[i] && ref_p[i] < axes[i][0]) {73ref_idxs[i] = 0;74} else if (axes[i][axes[i].size() - 1] <= ref_p[i]75&& ref_p[i] < axes[i][axes[i].size() - 1] + eps) {76ref_idxs[i] = (int)axes[i].size() - 1;77} else {78for (int j = 0; j < static_cast<int>(axes[i].size()) - 1; j++) {79if (axes[i][j] <= ref_p[i] && ref_p[i] < axes[i][j + 1]) {80// Pick the index that is closest to the point81if (ref_p[i] - axes[i][j] <= axes[i][j + 1] - ref_p[i]) {82ref_idxs[i] = j;83break;84} else {85ref_idxs[i] = j + 1;86break;87}88}89}90}9192// The point lies outside of the valid range93if (ref_idxs[i] == -1) {94return -1;95}96}9798return 0;99}100101102std::vector<double>103CharacteristicMap::at(const std::vector<int>& ref_idxs) const {104if (static_cast<int>(ref_idxs.size()) != domainDim) {105throw std::runtime_error("The number of indices differs from the map's"106" domain dimension.");107}108109int flatIdx = calcFlatIdx(ref_idxs);110return std::vector<double>(flattenedMap.begin() + flatIdx,111flattenedMap.begin() + flatIdx + imageDim);112}113114115CharacteristicMap::CharacteristicMap(int domainDim, int imageDim,116const std::vector<std::vector<double>>& ref_axes,117const std::vector<double>& ref_flattenedMap)118: domainDim(domainDim),119imageDim(imageDim),120axes(ref_axes),121flattenedMap(ref_flattenedMap) {122// Check whether the dimensions are consistent123if (static_cast<int>(axes.size()) != domainDim) {124throw std::runtime_error("The number of axes doesn't match the specified"125" domain dimension.");126}127int expectedEntryCnt = imageDim;128for (auto& ref_axis : axes) {129expectedEntryCnt *= (int)ref_axis.size();130}131if (static_cast<int>(flattenedMap.size()) != expectedEntryCnt) {132throw std::runtime_error("The number of map entries isn't equal to the"133" product of the axes' dimensions times the image dimension.");134}135136determineStrides();137}138139140CharacteristicMap::CharacteristicMap(const std::string& ref_mapString) {141// Split the map string into its three main parts142const std::vector<std::string> tokens = StringTokenizer(ref_mapString, "|").getVector();143if (tokens.size() != 3) {144throw std::runtime_error("The map string isn't made up of the 3 parts"145" dimensions, axes, and flattened entries.");146}147148// Extract the domain and image dimensions149const std::vector<std::string> dimensionTokens = StringTokenizer(tokens[0], ",").getVector();150if (dimensionTokens.size() != 2) {151throw std::runtime_error("The domain and image dimensions aren't specified"152" correctly.");153}154domainDim = std::stoi(dimensionTokens[0]);155imageDim = std::stoi(dimensionTokens[1]);156157// Create the map axes158const std::vector<std::string> axisTokens = StringTokenizer(tokens[1], ";").getVector();159if (static_cast<int>(axisTokens.size()) != domainDim) {160throw std::runtime_error("The number of axes doesn't match the specified"161" domain dimension.");162}163for (auto& ref_axisToken : axisTokens) {164std::vector<std::string> axisEntryTokens = StringTokenizer(ref_axisToken, ",").getVector();165std::vector<double> axisEntries;166for (auto& ref_axisEntryToken : axisEntryTokens) {167axisEntries.push_back(std::stod(ref_axisEntryToken));168}169axes.push_back(axisEntries);170}171172// Create the flattened map173const std::vector<std::string> flattenedMapTokens = StringTokenizer(tokens[2], ",").getVector();174int expectedEntryCnt = imageDim;175for (auto& ref_axis : axes) {176expectedEntryCnt *= (int)ref_axis.size();177}178if (static_cast<int>(flattenedMapTokens.size()) != expectedEntryCnt) {179throw std::runtime_error("The number of map entries isn't equal to the"180" product of the axes' dimensions times the image dimension.");181}182flattenedMap.reserve(expectedEntryCnt);183for (auto& ref_flattenedMapToken : flattenedMapTokens) {184flattenedMap.push_back(std::stod(ref_flattenedMapToken));185}186187determineStrides();188}189190191std::string192CharacteristicMap::toString() const {193// Write the domain and image dimensions194std::string mapString = std::to_string(domainDim) + ","195+ std::to_string(imageDim) + "|";196197// Add the axes198for (int i = 0; i < static_cast<int>(axes.size()); i++) {199for (int j = 0; j < static_cast<int>(axes[i].size()); j++) {200mapString += std::to_string(axes[i][j])201+ (j == static_cast<int>(axes[i].size()) - 1 ? "" : ",");202}203mapString += (i == static_cast<int>(axes.size()) - 1 ? "|" : ";");204}205206// Append the flattened map entries207for (int i = 0; i < static_cast<int>(flattenedMap.size()); i++) {208mapString += std::to_string(flattenedMap[i])209+ (i == static_cast<int>(flattenedMap.size()) - 1 ? "" : ",");210}211212return mapString;213}214215216int217CharacteristicMap::getDomainDim() const {218return domainDim;219}220221222int223CharacteristicMap::getImageDim() const {224return imageDim;225}226227228std::vector<double>229CharacteristicMap::eval(const std::vector<double>& ref_p, double eps) const {230if (static_cast<int>(ref_p.size()) != domainDim) {231throw std::runtime_error("The argument's size doesn't match the domain"232" dimension.");233}234235// Find the nearest neighbor and its image values236std::vector<int> nnIdxs;237if (findNearestNeighborIdxs(ref_p, nnIdxs, eps)) {238return std::vector<double>(imageDim, std::stod("nan"));239}240// Image values of the nearest neighbor241const std::vector<double> y_nn = at(nnIdxs);242// The result is based on the image values of the nearest neighbor243std::vector<double> y = y_nn;244245// Interpolate246for (int i = 0; i < domainDim; i++) {247// Depending on the configuration of the points, different neighbors will be248// used for interpolation249const double s = ref_p[i] - axes[i][nnIdxs[i]];250if (std::abs(s) <= eps) {251continue;252}253bool b_constellation1 = s < 0 && nnIdxs[i] > 0;254bool b_constellation2 = s >= 0255&& nnIdxs[i] == static_cast<int>(axes[i].size()) - 1256&& nnIdxs[i] > 0;257bool b_constellation3 = s < 0 && nnIdxs[i] == 0258&& nnIdxs[i] < static_cast<int>(axes[i].size()) - 1;259bool b_constellation4 = s >= 0260&& nnIdxs[i] < static_cast<int>(axes[i].size()) - 1;261262double dx = 1;263// Axis neighbor indices (i.e. the indices of the second support point)264std::vector<int> anIdxs = nnIdxs;265if (b_constellation1 || b_constellation2) {266anIdxs[i] -= 1;267dx = axes[i][nnIdxs[i]] - axes[i][anIdxs[i]];268} else if (b_constellation3 || b_constellation4) {269anIdxs[i] += 1;270dx = axes[i][anIdxs[i]] - axes[i][nnIdxs[i]];271} else {272continue;273}274// Image values of the axis neighbor275const std::vector<double> y_an = at(anIdxs);276277for (int j = 0; j < imageDim; j++) {278double dy = 0;279if (b_constellation1 || b_constellation2) {280dy = y_nn[j] - y_an[j];281} else {282dy = y_an[j] - y_nn[j];283}284285// Update286y[j] += s * dy / dx;287}288}289290return y;291}292293294