Line data Source code
1 : #include <spdlog/spdlog.h>
2 :
3 : #include <cstdlib>
4 : #include <nlohmann/json.hpp>
5 : #include <stdexcept>
6 :
7 : #include "Com/HTTPServer.hpp"
8 : #include "GlobalState.hpp"
9 : #include "Log/ProgressLoggerIgnore.hpp"
10 : #include "Log/ProgressLoggerJsonOStream.hpp"
11 : #include "Log/ProgressLoggerTableOStream.hpp"
12 : #include "Opt/QuadraticGuessSolver.hpp"
13 : #include "Opt/QuadraticSolver.hpp"
14 : #include "Static/Solution.hpp"
15 : #include "Static/algos/ConjugateFrankWolfe.hpp"
16 : #include "Static/algos/DijkstraAoN.hpp"
17 : #include "Static/algos/IterativeEquilibration.hpp"
18 : #include "Static/supply/BPRConvexNetwork.hpp"
19 : #include "Static/supply/BPRNetwork.hpp"
20 : #include "Static/supply/BPRNotConvexNetwork.hpp"
21 : #include "data/SUMO/EdgeData.hpp"
22 : #include "data/SUMO/NetworkTAZ.hpp"
23 : #include "data/SUMO/Routes.hpp"
24 : #include "utils/invertMap.hpp"
25 : #include "utils/require_env.hpp"
26 :
27 : using namespace std;
28 : using namespace Com;
29 : using namespace utils::stringify;
30 :
31 : using json = nlohmann::json;
32 :
33 : enum class StaticSimulationType : int {
34 : CONVEX,
35 : NONCONVEX
36 : };
37 :
38 : namespace utils::stringify {
39 : template<>
40 : class stringify<StaticSimulationType> {
41 : public:
42 : static StaticSimulationType fromString(const string &s);
43 :
44 : static string toString(const StaticSimulationType &t);
45 : };
46 : } // namespace utils::stringify
47 :
48 : // clang-format off
49 : const unordered_map<string, StaticSimulationType> str2staticSimulationType = {
50 : {"convex" , StaticSimulationType::CONVEX },
51 : {"nonconvex", StaticSimulationType::NONCONVEX }
52 : };
53 : const unordered_map<StaticSimulationType, string> staticSimulationType2str = utils::invertMap(str2staticSimulationType);
54 : // clang-format on
55 :
56 0 : StaticSimulationType stringify<StaticSimulationType>::fromString(const string &s) {
57 0 : return str2staticSimulationType.at(s);
58 : }
59 :
60 0 : string stringify<StaticSimulationType>::toString(const StaticSimulationType &t) {
61 0 : return staticSimulationType2str.at(t);
62 : }
63 :
64 : /**yaml POST /static/simulation/{id}
65 : * summary: Run static simulation.
66 : * tags:
67 : * - Static
68 : * consumes:
69 : * - application/json
70 : * parameters:
71 : * - name: id
72 : * in: path
73 : * required: true
74 : * description: ID of simulation
75 : * schema:
76 : * type: string
77 : * pattern: '^[\w\-]+$'
78 : * requestBody:
79 : * description: Configuration of simulation to run.
80 : * content:
81 : * application/json:
82 : * schema:
83 : * $ref: '#/components/schemas/StaticSimulation'
84 : * responses:
85 : * '200':
86 : * description: Simulation executed successfully
87 : */
88 0 : void HTTPServer::staticSimulationPost(const httplib::Request &req, httplib::Response &res) {
89 0 : const string WS_HOST = utils::require_env("WS_HOST");
90 :
91 0 : json data = json::parse(req.body);
92 :
93 0 : const string &resourceID = req.matches[0];
94 :
95 0 : string netPath, tazPath, demandPath, outEdgesPath, outRoutesPath;
96 0 : try {
97 0 : netPath = data.at("netPath");
98 0 : tazPath = data.at("tazPath");
99 0 : demandPath = data.at("demandPath");
100 0 : outEdgesPath = data.at("outEdgesPath");
101 0 : outRoutesPath = data.at("outRoutesPath");
102 0 : } catch(const json::out_of_range &e) {
103 0 : res.status = 400;
104 0 : return;
105 : }
106 :
107 0 : StaticSimulationType type = StaticSimulationType::NONCONVEX;
108 0 : if(data.contains("type")) {
109 0 : type = stringify<StaticSimulationType>::fromString(data.at("type"));
110 : }
111 :
112 0 : try {
113 0 : GlobalState::ResourceID taskID = "task://"s + resourceID;
114 :
115 : // Create stringstream resource
116 0 : GlobalState::ResourceID streamID = "stream://"s + resourceID;
117 0 : utils::pipestream &ios = GlobalState::streams.create(streamID);
118 :
119 : // Create task
120 : // clang-format off
121 0 : GlobalState::tasks.create(
122 : taskID,
123 0 : [
124 : netPath,
125 : tazPath,
126 : demandPath,
127 : type,
128 : outEdgesPath,
129 : outRoutesPath,
130 : taskID,
131 : streamID,
132 : &ios
133 0 : ]() -> GlobalState::TaskReturn {
134 0 : try {
135 0 : Log::ProgressLoggerJsonOStream logger(ios.o());
136 :
137 : // Supply
138 0 : shared_ptr<SUMO::Network> sumoNetwork = SUMO::Network::loadFromFile(netPath);
139 0 : SUMO::TAZs sumoTAZs = SUMO::TAZ::loadFromFile(tazPath);
140 0 : SUMO::NetworkTAZs sumo{*sumoNetwork, sumoTAZs};
141 :
142 : // Demand
143 0 : VISUM::OFormatDemand oDemand = VISUM::OFormatDemand::loadFromFile(demandPath);
144 0 : Static::Demand::Loader<const VISUM::OFormatDemand &, const Static::SUMOAdapter &> demandLoader;
145 :
146 : // Model
147 0 : Static::BPRNetwork::Loader<SUMO::NetworkTAZs> *loader = nullptr;
148 0 : switch(type) {
149 0 : case StaticSimulationType::CONVEX:
150 0 : loader = new Static::BPRNetwork::Loader<SUMO::NetworkTAZs>();
151 0 : break;
152 0 : case StaticSimulationType::NONCONVEX:
153 0 : loader = new Static::BPRNotConvexNetwork::Loader<SUMO::NetworkTAZs>();
154 0 : break;
155 0 : default:
156 0 : throw out_of_range("Invalid static simulation type");
157 : }
158 :
159 0 : Static::BPRNetwork *network = loader->load(sumo);
160 :
161 0 : Static::Demand demand = demandLoader.load(oDemand, loader->adapter);
162 :
163 : // Solve
164 :
165 : // All or Nothing
166 0 : Static::DijkstraAoN aon;
167 0 : Static::Solution x0 = aon.solve(*network, demand);
168 :
169 : // Solver
170 0 : Opt::QuadraticSolver innerSolver;
171 0 : Opt::QuadraticGuessSolver solver(
172 : innerSolver,
173 : 0.5,
174 : 0.2,
175 : 0.845,
176 : 0.365
177 0 : );
178 0 : solver.setStopCriteria(0.01);
179 :
180 0 : Static::Solution x;
181 0 : switch(type){
182 0 : case StaticSimulationType::CONVEX: {
183 : // Frank-Wolfe
184 0 : Static::ConjugateFrankWolfe fw(aon, solver, logger);
185 0 : fw.setStopCriteria(1.0);
186 0 : x = fw.solve(*network, demand, x0);
187 :
188 0 : break;
189 : }
190 0 : case StaticSimulationType::NONCONVEX: {
191 : // Iterative Equilibration
192 0 : Static::ConjugateFrankWolfe fw(aon, solver, Log::ProgressLoggerIgnore::INSTANCE);
193 :
194 0 : fw.setStopCriteria(0.0);
195 0 : fw.setIterations(5);
196 :
197 0 : Static::IterativeEquilibration<Static::BPRNotConvexNetwork, Static::ConjugateFrankWolfe> ie(fw, logger);
198 0 : ie.setIterations(10);
199 :
200 0 : Static::BPRNotConvexNetwork *networkNotConvex = dynamic_cast<Static::BPRNotConvexNetwork *>(network);
201 0 : if(networkNotConvex == nullptr)
202 0 : throw logic_error("Network is not BPRNotConvexNetwork");
203 :
204 0 : x = ie.solve(*networkNotConvex, demand, x0);
205 :
206 0 : break;
207 : }
208 0 : default:
209 0 : throw out_of_range("Invalid static simulation type " + to_string(static_cast<int>(type)));
210 : }
211 :
212 : // Save edgeData
213 : // clang-format off
214 0 : SUMO::EdgeData::Loader<
215 : const SUMO::NetworkTAZs &,
216 : const Static::BPRNetwork &,
217 : const Static::Solution &,
218 : const Static::SUMOAdapter &
219 : > edgeDataLoader;
220 : // clang-format on
221 0 : SUMO::EdgeData edgeData = edgeDataLoader.load(sumo, *network, x, loader->adapter);
222 0 : edgeData.saveToFile(outEdgesPath);
223 :
224 : // Save routes
225 : // clang-format off
226 0 : SUMO::Routes::Loader<
227 : const Static::Network &,
228 : const Static::Solution &,
229 : const Static::SUMOAdapter &
230 : > routesLoader;
231 : // clang-format on
232 0 : SUMO::Routes routes = routesLoader.load(*network, x, loader->adapter);
233 0 : routes.saveToFile(outRoutesPath);
234 :
235 0 : delete network;
236 0 : delete loader;
237 :
238 0 : ios.closeWrite();
239 0 : GlobalState::streams.erase(streamID);
240 :
241 0 : } catch(const GlobalState::ResourceException &e) {
242 0 : spdlog::error("Task {} aborted, what(): {}", taskID, e.what());
243 0 : return {400, "what(): "s + e.what()};
244 0 : } catch(const ios_base::failure &e) {
245 0 : spdlog::error("Task {} aborted, what(): {}", taskID, e.what());
246 0 : return {400, "what(): "s + e.what()};
247 : }
248 :
249 0 : return {200, ""};
250 : }
251 0 : );
252 : // clang-format on
253 :
254 : // clang-format off
255 0 : json resData = {
256 : {"log", {
257 : {"resourceID", resourceID},
258 0 : {"url", "ws://" + WS_HOST + resourceID + "/log"}
259 : }}
260 0 : };
261 : // clang-format on
262 0 : res.set_content(resData.dump(), "application/json");
263 :
264 0 : } catch(const exception &e) {
265 0 : res.status = 500;
266 0 : res.set_content("what(): "s + e.what(), "text/plain");
267 : }
268 : }
|