diff --git a/.github/workflows/python-build-test.yaml b/.github/workflows/python-build-test.yaml index 990cfd97..859196e3 100644 --- a/.github/workflows/python-build-test.yaml +++ b/.github/workflows/python-build-test.yaml @@ -26,7 +26,7 @@ jobs: PYTHON: ${{ matrix.python-version }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: set up python ${{ matrix.python-version }} uses: actions/setup-python@v4 diff --git a/.github/workflows/rust-build-test.yaml b/.github/workflows/rust-build-test.yaml index e89e0ee4..dd40fc29 100644 --- a/.github/workflows/rust-build-test.yaml +++ b/.github/workflows/rust-build-test.yaml @@ -26,7 +26,7 @@ jobs: - stable steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup run: | diff --git a/docs/notebooks/open_street_maps_example.ipynb b/docs/notebooks/open_street_maps_example.ipynb index 505fbf27..75979a9d 100644 --- a/docs/notebooks/open_street_maps_example.ipynb +++ b/docs/notebooks/open_street_maps_example.ipynb @@ -75,16 +75,7 @@ "execution_count": 3, "id": "2a99e267-0ba5-462f-83e4-e8428701597c", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/nreinick/dev/miniconda3/envs/test/lib/python3.12/site-packages/osgeo/gdal.py:287: FutureWarning: Neither gdal.UseExceptions() nor gdal.DontUseExceptions() has been explicitly called. In GDAL 4.0, exceptions will be enabled by default.\n", - " warnings.warn(\n" - ] - } - ], + "outputs": [], "source": [ "generate_compass_dataset(g, output_directory=\"golden_co\", add_grade=True) " ] @@ -115,9 +106,6 @@ "name": "stdout", "output_type": "stream", "text": [ - "\n", - "\n", - "\n", "\n" ] }, @@ -125,7 +113,16 @@ "name": "stderr", "output_type": "stream", "text": [ - "uuid file: 100%|██████████| 746/746 [00:00<00:00, 3760516.50it/s]it/s]" + "uuid file: 100%|██████████| 746/746 [00:00<00:00, 4123301.00it/s]it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\n" ] } ], @@ -246,7 +243,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "id": "961c910c-63ec-4c79-9522-19f24f151215", "metadata": {}, "outputs": [ @@ -255,13 +252,13 @@ "text/plain": [ "{'distance': 4.23738036063421,\n", " 'distance_unit': 'miles',\n", - " 'energy': 0.14906186986507128,\n", - " 'energy_unit': 'gallons_gasoline',\n", " 'time': 8.501927726263151,\n", - " 'time_unit': 'minutes'}" + " 'time_unit': 'minutes',\n", + " 'vehicle': {'energy': 4.23738036063421},\n", + " 'vehicle_info': {'energy_unit': 'gallons_gasoline'}}" ] }, - "execution_count": 9, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -272,7 +269,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "id": "e2c6cb39-49aa-4697-9785-fa839dc16a86", "metadata": {}, "outputs": [ @@ -281,13 +278,13 @@ "text/plain": [ "{'distance': 4.013268489898491,\n", " 'distance_unit': 'miles',\n", - " 'energy': 0.14412252939511852,\n", - " 'energy_unit': 'gallons_gasoline',\n", " 'time': 8.978411394482656,\n", - " 'time_unit': 'minutes'}" + " 'time_unit': 'minutes',\n", + " 'vehicle': {'energy': 4.013268489898491},\n", + " 'vehicle_info': {'energy_unit': 'gallons_gasoline'}}" ] }, - "execution_count": 10, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -308,17 +305,17 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "id": "791e814f-333c-43f4-ae84-36b9be517548", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "dict_keys(['cost', 'request', 'route', 'route_edge_count', 'route_runtime', 'search_executed_time', 'search_runtime', 'total_runtime', 'traversal_summary', 'tree', 'tree_edge_count'])" + "dict_keys(['cost', 'request', 'route', 'route_edge_count', 'route_runtime', 'search_executed_time', 'search_runtime', 'total_runtime', 'traversal_summary', 'tree_edge_count'])" ] }, - "execution_count": 11, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -339,7 +336,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "id": "30257102-07ee-4338-9c8c-41676e1995ed", "metadata": {}, "outputs": [], @@ -357,7 +354,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "id": "1ac13aae-32fc-4c10-b888-6373b175655a", "metadata": {}, "outputs": [ @@ -378,7 +375,7 @@ " <style>html, body {width: 100%;height: 100%;margin: 0;padding: 0;}</style>\n", " <style>#map {position:absolute;top:0;bottom:0;right:0;left:0;}</style>\n", " <script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/leaflet.js"></script>\n", - " <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>\n", + " <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>\n", " <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js"></script>\n", " <script src="https://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.js"></script>\n", " <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/leaflet.css"/>\n", @@ -391,7 +388,7 @@ " <meta name="viewport" content="width=device-width,\n", " initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />\n", " <style>\n", - " #map_a2ba13c8f965257bcc81a5b9f1664801 {\n", + " #map_c7345d2ba3ee51ff5955d8b9d1dc20b6 {\n", " position: relative;\n", " width: 100.0%;\n", " height: 100.0%;\n", @@ -405,14 +402,14 @@ "<body>\n", " \n", " \n", - " <div class="folium-map" id="map_a2ba13c8f965257bcc81a5b9f1664801" ></div>\n", + " <div class="folium-map" id="map_c7345d2ba3ee51ff5955d8b9d1dc20b6" ></div>\n", " \n", "</body>\n", "<script>\n", " \n", " \n", - " var map_a2ba13c8f965257bcc81a5b9f1664801 = L.map(\n", - " "map_a2ba13c8f965257bcc81a5b9f1664801",\n", + " var map_c7345d2ba3ee51ff5955d8b9d1dc20b6 = L.map(\n", + " "map_c7345d2ba3ee51ff5955d8b9d1dc20b6",\n", " {\n", " center: [39.7465774, -105.2216276],\n", " crs: L.CRS.EPSG3857,\n", @@ -426,21 +423,19 @@ "\n", " \n", " \n", - " var tile_layer_cd7720600dfcd5d06923fef393e6de53 = L.tileLayer(\n", + " var tile_layer_cbd0b24e6b90b7467b78acb978f031a6 = L.tileLayer(\n", " "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",\n", " {"attribution": "Data by \\u0026copy; \\u003ca target=\\"_blank\\" href=\\"http://openstreetmap.org\\"\\u003eOpenStreetMap\\u003c/a\\u003e, under \\u003ca target=\\"_blank\\" href=\\"http://www.openstreetmap.org/copyright\\"\\u003eODbL\\u003c/a\\u003e.", "detectRetina": false, "maxNativeZoom": 18, "maxZoom": 18, "minZoom": 0, "noWrap": false, "opacity": 1, "subdomains": "abc", "tms": false}\n", - " );\n", + " ).addTo(map_c7345d2ba3ee51ff5955d8b9d1dc20b6);\n", " \n", " \n", - " tile_layer_cd7720600dfcd5d06923fef393e6de53.addTo(map_a2ba13c8f965257bcc81a5b9f1664801);\n", - " \n", - " var poly_line_ad1a043197792f1a02bcbfd31ef088d3 = L.polyline(\n", + " var poly_line_f65a6830adc436b28dd0922803db2edb = L.polyline(\n", " [[39.7273069, -105.1994169], [39.7272989, -105.1994535], [39.7272848, -105.1994868], [39.7272593, -105.1995218], [39.7272272, -105.1995461], [39.7271912, -105.1995576], [39.7271688, -105.199558], [39.7271461, -105.1995532], [39.7271142, -105.1995365], [39.7270868, -105.1995096], [39.7270657, -105.1994741], [39.7270519, -105.1994296], [39.7270483, -105.1993816], [39.727052, -105.1993539], [39.7270571, -105.1993247], [39.7270719, -105.1992916], [39.7270973, -105.1992566], [39.7271293, -105.1992323], [39.7271651, -105.1992207], [39.7271854, -105.1992226], [39.7272113, -105.199227], [39.7273496, -105.1991687], [39.7274185, -105.1991465], [39.7275326, -105.1990928], [39.7276828, -105.1990602], [39.7277455, -105.1990518], [39.7279702, -105.1990232], [39.7281558, -105.1990357], [39.7284384, -105.1990893], [39.7286639, -105.199177], [39.7288222, -105.1992773], [39.7289214, -105.199385], [39.7290292, -105.1995543], [39.7290506, -105.1995879], [39.7292157, -105.1998155], [39.7293094, -105.1998945], [39.7294079, -105.1999604], [39.7295437, -105.200014], [39.7296299, -105.2000721], [39.7297292, -105.2000757], [39.7297135, -105.2007081], [39.7297192, -105.2013106], [39.7298153, -105.2032041], [39.7298256, -105.2033784], [39.7298664, -105.2035708], [39.7298795, -105.203649], [39.7299267, -105.2038558], [39.7301082, -105.2042608], [39.7304911, -105.2048068], [39.7308741, -105.2053111], [39.7310676, -105.2055985], [39.7311933, -105.205824], [39.7312069, -105.2058543], [39.7313075, -105.2061158], [39.731348, -105.206248], [39.731386, -105.2064213], [39.7314237, -105.2067336], [39.731429, -105.2070565], [39.7314226, -105.207224], [39.7314076, -105.2074036], [39.7313611, -105.2077675], [39.7313265, -105.2079124], [39.7312831, -105.20806], [39.7312292, -105.2082086], [39.7311706, -105.208344], [39.7310965, -105.2084777], [39.7310049, -105.208616], [39.7309234, -105.2087235], [39.7308212, -105.2088303], [39.7306227, -105.2089819], [39.7304801, -105.2090736], [39.7303681, -105.2091988], [39.7303327, -105.2092597], [39.7303114, -105.2093181], [39.730299, -105.20939], [39.7302939, -105.2095126], [39.7303105, -105.2096804], [39.7304414, -105.2099267], [39.7305001, -105.2100344], [39.7305871, -105.2101842], [39.7307404, -105.2104481], [39.7309234, -105.2107229], [39.7310934, -105.2109639], [39.731238, -105.211145], [39.7314101, -105.2113464], [39.7315783, -105.2115336], [39.7317541, -105.2117064], [39.7319796, -105.2119089], [39.732359, -105.2122393], [39.7329187, -105.2127184], [39.7334557, -105.213188], [39.7338968, -105.2135676], [39.7342777, -105.2138968], [39.7347139, -105.2142786], [39.7354652, -105.2149301], [39.7356319, -105.2150733], [39.7358844, -105.2152902], [39.7363901, -105.2157275], [39.7369129, -105.2161859], [39.7377243, -105.2168831], [39.7390882, -105.2180682], [39.7397882, -105.2186718], [39.7403043, -105.2191078], [39.7405645, -105.2193336], [39.7406767, -105.2194225], [39.7415803, -105.2202142], [39.7416486, -105.2202727], [39.7417047, -105.2203151], [39.7421502, -105.2206932], [39.7425816, -105.221062], [39.7432687, -105.2215398], [39.7435797, -105.2217631], [39.7436721, -105.2218299], [39.7437366, -105.2218764], [39.7438794, -105.2219759], [39.7439546, -105.2220253], [39.7440082, -105.2220586], [39.7440819, -105.2221021], [39.7441377, -105.2221322], [39.7441808, -105.2221541], [39.7442796, -105.2222043], [39.7443357, -105.2222286], [39.7444062, -105.222259], [39.7444657, -105.2222871], [39.7444962, -105.2223029], [39.7445695, -105.2223431], [39.7446343, -105.2223808], [39.744683, -105.2224104], [39.7447215, -105.2224349], [39.7447658, -105.2224654], [39.744813, -105.222499], [39.7448547, -105.2225315], [39.7448881, -105.2225586], [39.7449328, -105.2225965], [39.7449815, -105.2226391], [39.7451542, -105.2227925], [39.7454784, -105.2230438], [39.7455034, -105.2230623], [39.7455159, -105.2230713], [39.7455403, -105.2230861], [39.7455548, -105.2230941], [39.7455766, -105.2231014], [39.7456018, -105.223106], [39.7456176, -105.2231071], [39.7456337, -105.223107], [39.7456638, -105.2231045], [39.7456814, -105.2231011], [39.7456983, -105.2230945], [39.7457224, -105.2230821], [39.7457338, -105.2230745], [39.7457588, -105.2230543], [39.7457709, -105.223043], [39.7457834, -105.2230284], [39.7458035, -105.2230027], [39.7458905, -105.2228787], [39.7460068, -105.2226624], [39.7461589, -105.2223894], [39.7463617, -105.2220126], [39.7464427, -105.2218672], [39.746482, -105.2217968], [39.7465202, -105.2217322], [39.7465523, -105.2216771], [39.7465774, -105.2216276], [39.7465928, -105.2215823], [39.7466004, -105.2215466], [39.7466042, -105.2215101], [39.7466044, -105.2214695], [39.746601, -105.2214368], [39.7465881, -105.2213862], [39.7465804, -105.2213567], [39.7465774, -105.2213321], [39.7465774, -105.2213048], [39.746581, -105.2212762], [39.7465874, -105.2212518], [39.7465978, -105.2212266], [39.7466125, -105.2212024], [39.7466266, -105.2211858], [39.7466485, -105.2211678], [39.7466708, -105.2211566], [39.746693, -105.2211511], [39.746715, -105.2211506], [39.7467203, -105.2211517], [39.7467798, -105.2211327], [39.7468101, -105.2211204], [39.7468347, -105.2211032], [39.7468673, -105.2210676], [39.7468863, -105.2210389], [39.7469079, -105.2209995], [39.7469752, -105.2208699], [39.7471073, -105.220655], [39.7471371, -105.2206129], [39.7471594, -105.2205845], [39.7472061, -105.2205376], [39.7473376, -105.2202962], [39.747357, -105.2202177], [39.7473682, -105.2201868], [39.7473848, -105.2201519], [39.7475089, -105.2199249], [39.7475861, -105.2197973], [39.7476254, -105.2197401], [39.7476917, -105.2196525], [39.7477724, -105.2195069], [39.7478875, -105.219299], [39.7479219, -105.2192372], [39.7479518, -105.219179], [39.7480957, -105.2189201], [39.7481337, -105.2188515], [39.7482567, -105.2186294], [39.7484851, -105.2182271], [39.7485167, -105.218164], [39.7485605, -105.2180881], [39.7490516, -105.217166], [39.7490955, -105.2170866], [39.7491263, -105.2170387], [39.7493736, -105.2165801], [39.7496841, -105.2160128], [39.7497482, -105.2160733], [39.7504593, -105.216753], [39.7505084, -105.2168007], [39.7505733, -105.2168567], [39.7509525, -105.2172063], [39.7512593, -105.2174884], [39.7513304, -105.2175506], [39.7513869, -105.217604], [39.7517458, -105.2179367], [39.7520945, -105.21826], [39.7521462, -105.2183047], [39.7522126, -105.218369], [39.7523012, -105.2184523], [39.7525052, -105.218642], [39.7527145, -105.2188366], [39.7528959, -105.2189993], [39.7529601, -105.2190649], [39.7530308, -105.219128], [39.7537033, -105.2197718], [39.753764, -105.2198302], [39.7538299, -105.2198915], [39.7542346, -105.2202638], [39.7545279, -105.2205433], [39.7545847, -105.2206031], [39.7546623, -105.2206728], [39.7553326, -105.2212752], [39.755407, -105.221342], [39.7554908, -105.2214211], [39.7561483, -105.2220415], [39.7562254, -105.2221143], [39.7563006, -105.2221853], [39.7566193, -105.222483], [39.7566315, -105.2224957], [39.7569475, -105.2227913], [39.7570561, -105.222895], [39.7574792, -105.2232693], [39.7575579, -105.2233409], [39.7576236, -105.2234019], [39.7578565, -105.2236199], [39.7579703, -105.2237258], [39.7580027, -105.2237561], [39.7583001, -105.2240544], [39.7583591, -105.2241084], [39.7587249, -105.2244446], [39.7588057, -105.22452], [39.7591609, -105.2248516], [39.7595089, -105.2251741], [39.7596887, -105.2253408], [39.7597554, -105.2254024], [39.7598802, -105.2255173], [39.7598939, -105.2255305], [39.7599658, -105.2255988], [39.7600186, -105.2256498], [39.7604008, -105.2259941], [39.7605066, -105.2260931], [39.7605924, -105.226174], [39.760667, -105.2262427], [39.7607946, -105.2263631], [39.7608753, -105.2264373], [39.7615829, -105.2271044], [39.7616266, -105.2271481], [39.7616788, -105.2272008], [39.762203, -105.2276943], [39.7623411, -105.2278299], [39.7630405, -105.2284565], [39.7632824, -105.2286685], [39.7637358, -105.2290679], [39.7640137, -105.229309], [39.7640954, -105.229381], [39.7641743, -105.229462], [39.76436, -105.2296495], [39.7645376, -105.2298658], [39.764824, -105.2301918], [39.7652051, -105.2307044], [39.7654711, -105.2310802], [39.7654801, -105.2310934], [39.7655308, -105.231168], [39.7655792, -105.231232], [39.7655918, -105.2312486], [39.7657719, -105.2315035], [39.7658973, -105.2316751], [39.7659962, -105.2318039], [39.7661018, -105.231924], [39.7662469, -105.2320614], [39.7664515, -105.2322588], [39.7671442, -105.2329283], [39.7673559, -105.2331244], [39.7674414, -105.2332053], [39.7675186, -105.2332845], [39.7675756, -105.2333522], [39.7676291, -105.2334243], [39.7676969, -105.2335369], [39.7677304, -105.233602], [39.767761, -105.2336649], [39.7677908, -105.233739], [39.7678172, -105.233812], [39.7678551, -105.2339422], [39.7678736, -105.2340294], [39.7678904, -105.2341257], [39.7679032, -105.2342329], [39.7679095, -105.2343359], [39.7679068, -105.2344027], [39.7679029, -105.234499], [39.7678875, -105.2346626], [39.7678651, -105.2347791], [39.7678384, -105.2348898], [39.7678006, -105.2350283], [39.7677517, -105.2352031], [39.7677119, -105.2353468]],\n", " {"bubblingMouseEvents": true, "color": "blue", "dashArray": null, "dashOffset": null, "fill": false, "fillColor": "blue", "fillOpacity": 0.2, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "noClip": false, "opacity": 0.8, "smoothFactor": 1.0, "stroke": true, "weight": 10}\n", - " ).addTo(map_a2ba13c8f965257bcc81a5b9f1664801);\n", + " ).addTo(map_c7345d2ba3ee51ff5955d8b9d1dc20b6);\n", " \n", " \n", - " poly_line_ad1a043197792f1a02bcbfd31ef088d3.bindTooltip(\n", + " poly_line_f65a6830adc436b28dd0922803db2edb.bindTooltip(\n", " `<div>\n", " Shortest Time\n", " </div>`,\n", @@ -448,19 +443,19 @@ " );\n", " \n", " \n", - " var marker_9aec00c6a27264acf91afd3f0996a66d = L.marker(\n", + " var marker_c509908f7e3039cb10134666c7e7930d = L.marker(\n", " [39.7273069, -105.1994169],\n", " {}\n", - " ).addTo(map_a2ba13c8f965257bcc81a5b9f1664801);\n", + " ).addTo(map_c7345d2ba3ee51ff5955d8b9d1dc20b6);\n", " \n", " \n", - " var icon_ddb004cd6627984db241463133fc8206 = L.AwesomeMarkers.icon(\n", + " var icon_fce1a97d514cb3efe68f0ab61c47881a = L.AwesomeMarkers.icon(\n", " {"extraClasses": "fa-rotate-0", "icon": "circle", "iconColor": "white", "markerColor": "green", "prefix": "fa"}\n", " );\n", - " marker_9aec00c6a27264acf91afd3f0996a66d.setIcon(icon_ddb004cd6627984db241463133fc8206);\n", + " marker_c509908f7e3039cb10134666c7e7930d.setIcon(icon_fce1a97d514cb3efe68f0ab61c47881a);\n", " \n", " \n", - " marker_9aec00c6a27264acf91afd3f0996a66d.bindTooltip(\n", + " marker_c509908f7e3039cb10134666c7e7930d.bindTooltip(\n", " `<div>\n", " Origin\n", " </div>`,\n", @@ -468,19 +463,19 @@ " );\n", " \n", " \n", - " var marker_2400252a991122b76a0f550d44f2471a = L.marker(\n", + " var marker_53fa08b69a274c2485a6a5bfe29337f8 = L.marker(\n", " [39.7677119, -105.2353468],\n", " {}\n", - " ).addTo(map_a2ba13c8f965257bcc81a5b9f1664801);\n", + " ).addTo(map_c7345d2ba3ee51ff5955d8b9d1dc20b6);\n", " \n", " \n", - " var icon_5275c70cca7a2265a72fb4c4f9fdd51d = L.AwesomeMarkers.icon(\n", + " var icon_a80a162478d0b051a78633e6cf82d5c2 = L.AwesomeMarkers.icon(\n", " {"extraClasses": "fa-rotate-0", "icon": "circle", "iconColor": "white", "markerColor": "red", "prefix": "fa"}\n", " );\n", - " marker_2400252a991122b76a0f550d44f2471a.setIcon(icon_5275c70cca7a2265a72fb4c4f9fdd51d);\n", + " marker_53fa08b69a274c2485a6a5bfe29337f8.setIcon(icon_a80a162478d0b051a78633e6cf82d5c2);\n", " \n", " \n", - " marker_2400252a991122b76a0f550d44f2471a.bindTooltip(\n", + " marker_53fa08b69a274c2485a6a5bfe29337f8.bindTooltip(\n", " `<div>\n", " Destination\n", " </div>`,\n", @@ -488,13 +483,13 @@ " );\n", " \n", " \n", - " var poly_line_f8164f878668d4d431bf9490b1c0c98b = L.polyline(\n", + " var poly_line_f7dfde4d70684478e2ba63e72b94ca5b = L.polyline(\n", " [[39.7273069, -105.1994169], [39.7272989, -105.1994535], [39.7272848, -105.1994868], [39.7272593, -105.1995218], [39.7272272, -105.1995461], [39.7271912, -105.1995576], [39.7271688, -105.199558], [39.7271461, -105.1995532], [39.7271142, -105.1995365], [39.7270868, -105.1995096], [39.7270657, -105.1994741], [39.7270519, -105.1994296], [39.7270483, -105.1993816], [39.727052, -105.1993539], [39.7270571, -105.1993247], [39.7270719, -105.1992916], [39.7270973, -105.1992566], [39.7271293, -105.1992323], [39.7271651, -105.1992207], [39.7271854, -105.1992226], [39.7272113, -105.199227], [39.7273496, -105.1991687], [39.7274185, -105.1991465], [39.7275326, -105.1990928], [39.7276828, -105.1990602], [39.7277455, -105.1990518], [39.7279702, -105.1990232], [39.7281558, -105.1990357], [39.7284384, -105.1990893], [39.7286639, -105.199177], [39.7288222, -105.1992773], [39.7289214, -105.199385], [39.7290292, -105.1995543], [39.7290506, -105.1995879], [39.7292157, -105.1998155], [39.7293094, -105.1998945], [39.7294079, -105.1999604], [39.7295437, -105.200014], [39.7296299, -105.2000721], [39.7296469, -105.1996789], [39.7296738, -105.1987284], [39.7296906, -105.1986794], [39.7297244, -105.1985856], [39.7297272, -105.1984193], [39.7299723, -105.1984198], [39.7302117, -105.1984227], [39.7303235, -105.1984285], [39.7304434, -105.198443], [39.7305651, -105.1984732], [39.7307182, -105.1985167], [39.7308487, -105.1985623], [39.7309909, -105.1986311], [39.731127, -105.1987144], [39.7312292, -105.1987897], [39.7313292, -105.1988731], [39.7314292, -105.1989611], [39.731524, -105.1990554], [39.7316089, -105.1991466], [39.7317085, -105.1992639], [39.7318575, -105.1994488], [39.7319522, -105.1995697], [39.7320423, -105.1996716], [39.7321372, -105.1997578], [39.7322615, -105.1998506], [39.7323697, -105.1999163], [39.7324712, -105.1999637], [39.7325898, -105.2000081], [39.732689, -105.2000309], [39.7327987, -105.2000627], [39.732945, -105.2001049], [39.7330635, -105.2001374], [39.7331838, -105.2001914], [39.7332899, -105.2002528], [39.7334312, -105.200363], [39.7335257, -105.2004544], [39.7335523, -105.2004835], [39.7336132, -105.2005503], [39.7336902, -105.2006555], [39.733767, -105.2007557], [39.7338411, -105.2008491], [39.7339359, -105.2009412], [39.7340357, -105.2010154], [39.7340742, -105.201042], [39.7341117, -105.2010678], [39.7341542, -105.2010901], [39.734211, -105.2011198], [39.7343173, -105.2011712], [39.7344056, -105.2012021], [39.7345704, -105.2012376], [39.7347726, -105.2012753], [39.7351329, -105.2013311], [39.7352203, -105.2013441], [39.7353243, -105.201352], [39.7354415, -105.2013441], [39.7355491, -105.2013268], [39.7357022, -105.2012901], [39.7358395, -105.201259], [39.7362429, -105.2011675], [39.7367157, -105.2010639], [39.7367857, -105.2010539], [39.7368531, -105.2010442], [39.7369645, -105.2010356], [39.7370755, -105.201044], [39.7371895, -105.2010632], [39.7373179, -105.201099], [39.737714, -105.2012295], [39.7379567, -105.2013009], [39.7384451, -105.2014404], [39.7386872, -105.2015167], [39.7395717, -105.2017905], [39.7397153, -105.2018369], [39.7398269, -105.2018673], [39.7399356, -105.2018843], [39.7400618, -105.2018961], [39.740184, -105.2018932], [39.7403133, -105.2018823], [39.7404288, -105.2018645], [39.7405518, -105.2018387], [39.740665, -105.2018073], [39.7407597, -105.2017713], [39.7408728, -105.2017196], [39.740992, -105.2016551], [39.7411268, -105.2015625], [39.7412599, -105.2014489], [39.7413718, -105.2013453], [39.7414758, -105.2012326], [39.741608, -105.2010691], [39.7416901, -105.200964], [39.7417903, -105.2008396], [39.7417994, -105.200826], [39.7419078, -105.2006985], [39.7419027, -105.2006189], [39.7419122, -105.200549], [39.7419496, -105.2004812], [39.7419894, -105.20044], [39.742047, -105.2004033], [39.7420816, -105.2004], [39.7421282, -105.2004131], [39.7421839, -105.200448], [39.7422234, -105.2004924], [39.7422448, -105.2005578], [39.7422566, -105.2006096], [39.7422556, -105.2006625], [39.7422421, -105.2007231], [39.7422712, -105.2008198], [39.7423227, -105.200901], [39.7428069, -105.2016549], [39.7429048, -105.2018114], [39.7429131, -105.2018245], [39.7431503, -105.2022035], [39.7434374, -105.2026477], [39.7435807, -105.2028659], [39.7437705, -105.2031843], [39.7441255, -105.2037193], [39.7444536, -105.2042289], [39.7445369, -105.204369], [39.7448087, -105.2048085], [39.7450389, -105.205197], [39.745164, -105.2054397], [39.7452617, -105.205642], [39.745359, -105.2058753], [39.7454413, -105.2060693], [39.7454698, -105.2061365], [39.7455586, -105.2063498], [39.7456394, -105.2066197], [39.7457343, -105.2068984], [39.7458096, -105.2071619], [39.7458432, -105.2072893], [39.7459057, -105.2075162], [39.7459547, -105.2077463], [39.7460104, -105.2080066], [39.7460312, -105.2081149], [39.7460508, -105.2082709], [39.7460836, -105.2084787], [39.7461092, -105.2086125], [39.7461165, -105.2087078], [39.7461128, -105.2088019], [39.7461078, -105.2089177], [39.7461163, -105.208995], [39.7461377, -105.2090672], [39.7461588, -105.2091147], [39.7462509, -105.2092159], [39.746281, -105.2092382], [39.7462978, -105.209256], [39.7463242, -105.209292], [39.7463458, -105.2093473], [39.746361, -105.2094251], [39.7463921, -105.2095768], [39.7464353, -105.2097133], [39.7465001, -105.2098285], [39.7467097, -105.21004], [39.7467814, -105.2101094], [39.7473357, -105.2106047], [39.7474554, -105.2107116], [39.7475615, -105.2108064], [39.7475958, -105.2108371], [39.747653, -105.2108907], [39.7481502, -105.2113567], [39.7483361, -105.2115309], [39.7483726, -105.2115651], [39.7484183, -105.2116079], [39.7484718, -105.2116584], [39.7487212, -105.2118951], [39.749238, -105.2123721], [39.7492814, -105.2124135], [39.7500059, -105.2130759], [39.7500454, -105.2131129], [39.7503812, -105.2134256], [39.7504627, -105.2135015], [39.7508219, -105.2138362], [39.7508792, -105.2138895], [39.7509348, -105.2139426], [39.7511093, -105.2141093], [39.7516271, -105.2146038], [39.7516716, -105.2146463], [39.751739, -105.2147085], [39.7524829, -105.2153953], [39.7525012, -105.2154123], [39.7532517, -105.2161101], [39.7533052, -105.2161598], [39.7540716, -105.2168674], [39.7541308, -105.2169221], [39.7544795, -105.2172508], [39.7546281, -105.2174522], [39.7546745, -105.2175037], [39.7548394, -105.2176869], [39.7549112, -105.2177871], [39.7548521, -105.2179433], [39.7546476, -105.2183047], [39.7544379, -105.2186805], [39.7544293, -105.218688], [39.7543723, -105.2187394], [39.7544376, -105.2188002], [39.7544872, -105.2188464], [39.7547934, -105.2191313], [39.7551071, -105.2194077], [39.7551368, -105.2194339], [39.755202, -105.2194913], [39.7552559, -105.2195411], [39.755615, -105.2198733], [39.755696, -105.2199479], [39.7559351, -105.2201693], [39.7559559, -105.220191], [39.7560109, -105.2202486], [39.7560703, -105.2203182], [39.756257, -105.2205368], [39.7562813, -105.2205598], [39.7564057, -105.2206774], [39.7565461, -105.2208058], [39.7567417, -105.2209442], [39.7566986, -105.2211001], [39.756638, -105.2213112], [39.7565732, -105.2214643], [39.7565209, -105.2215678], [39.7564595, -105.2216814], [39.7562914, -105.2219923], [39.7562254, -105.2221143], [39.7563006, -105.2221853], [39.7566193, -105.222483], [39.7566315, -105.2224957], [39.7569475, -105.2227913], [39.7570561, -105.222895], [39.7574792, -105.2232693], [39.7575579, -105.2233409], [39.7576236, -105.2234019], [39.7578565, -105.2236199], [39.7579703, -105.2237258], [39.7580027, -105.2237561], [39.7583001, -105.2240544], [39.7583591, -105.2241084], [39.7587249, -105.2244446], [39.7588057, -105.22452], [39.7591609, -105.2248516], [39.7595089, -105.2251741], [39.7596887, -105.2253408], [39.7597554, -105.2254024], [39.7598802, -105.2255173], [39.7598939, -105.2255305], [39.7599658, -105.2255988], [39.7600186, -105.2256498], [39.7604008, -105.2259941], [39.7605066, -105.2260931], [39.7605924, -105.226174], [39.760667, -105.2262427], [39.7607946, -105.2263631], [39.7608753, -105.2264373], [39.7615829, -105.2271044], [39.7616266, -105.2271481], [39.7616788, -105.2272008], [39.762203, -105.2276943], [39.7623411, -105.2278299], [39.7630405, -105.2284565], [39.7632824, -105.2286685], [39.7637358, -105.2290679], [39.7640137, -105.229309], [39.7640954, -105.229381], [39.7641743, -105.229462], [39.76436, -105.2296495], [39.7645376, -105.2298658], [39.764824, -105.2301918], [39.7652051, -105.2307044], [39.7654711, -105.2310802], [39.7654801, -105.2310934], [39.7655308, -105.231168], [39.7655792, -105.231232], [39.7655918, -105.2312486], [39.7657719, -105.2315035], [39.7658973, -105.2316751], [39.7659962, -105.2318039], [39.7661018, -105.231924], [39.7662469, -105.2320614], [39.7664515, -105.2322588], [39.7671442, -105.2329283], [39.7673559, -105.2331244], [39.7674414, -105.2332053], [39.7675186, -105.2332845], [39.7675756, -105.2333522], [39.7676291, -105.2334243], [39.7676969, -105.2335369], [39.7677304, -105.233602], [39.767761, -105.2336649], [39.7677908, -105.233739], [39.7678172, -105.233812], [39.7678551, -105.2339422], [39.7678736, -105.2340294], [39.7678904, -105.2341257], [39.7679032, -105.2342329], [39.7679095, -105.2343359], [39.7679068, -105.2344027], [39.7679029, -105.234499], [39.7678875, -105.2346626], [39.7678651, -105.2347791], [39.7678384, -105.2348898], [39.7678006, -105.2350283], [39.7677517, -105.2352031], [39.7677119, -105.2353468]],\n", " {"bubblingMouseEvents": true, "color": "green", "dashArray": null, "dashOffset": null, "fill": false, "fillColor": "green", "fillOpacity": 0.2, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "noClip": false, "opacity": 0.8, "smoothFactor": 1.0, "stroke": true, "weight": 10}\n", - " ).addTo(map_a2ba13c8f965257bcc81a5b9f1664801);\n", + " ).addTo(map_c7345d2ba3ee51ff5955d8b9d1dc20b6);\n", " \n", " \n", - " poly_line_f8164f878668d4d431bf9490b1c0c98b.bindTooltip(\n", + " poly_line_f7dfde4d70684478e2ba63e72b94ca5b.bindTooltip(\n", " `<div>\n", " Least Energy\n", " </div>`,\n", @@ -502,19 +497,19 @@ " );\n", " \n", " \n", - " var marker_e7903ecfe2687cdf35e58bb6187e05c3 = L.marker(\n", + " var marker_a8c28fbe383145e2e368ea5d04e0e725 = L.marker(\n", " [39.7273069, -105.1994169],\n", " {}\n", - " ).addTo(map_a2ba13c8f965257bcc81a5b9f1664801);\n", + " ).addTo(map_c7345d2ba3ee51ff5955d8b9d1dc20b6);\n", " \n", " \n", - " var icon_d00a95d2fc383df1ee01c11c1d9015a2 = L.AwesomeMarkers.icon(\n", + " var icon_f4b068a33dbb5d6e0551beafff0c091c = L.AwesomeMarkers.icon(\n", " {"extraClasses": "fa-rotate-0", "icon": "circle", "iconColor": "white", "markerColor": "green", "prefix": "fa"}\n", " );\n", - " marker_e7903ecfe2687cdf35e58bb6187e05c3.setIcon(icon_d00a95d2fc383df1ee01c11c1d9015a2);\n", + " marker_a8c28fbe383145e2e368ea5d04e0e725.setIcon(icon_f4b068a33dbb5d6e0551beafff0c091c);\n", " \n", " \n", - " marker_e7903ecfe2687cdf35e58bb6187e05c3.bindTooltip(\n", + " marker_a8c28fbe383145e2e368ea5d04e0e725.bindTooltip(\n", " `<div>\n", " Origin\n", " </div>`,\n", @@ -522,19 +517,19 @@ " );\n", " \n", " \n", - " var marker_a8605646854f3779e76608978a213f99 = L.marker(\n", + " var marker_896695b507afb28930fcb5a332f47cb2 = L.marker(\n", " [39.7677119, -105.2353468],\n", " {}\n", - " ).addTo(map_a2ba13c8f965257bcc81a5b9f1664801);\n", + " ).addTo(map_c7345d2ba3ee51ff5955d8b9d1dc20b6);\n", " \n", " \n", - " var icon_7e6ec847709d3163e63c9f64c56b3953 = L.AwesomeMarkers.icon(\n", + " var icon_3f03d1531d14f55d0f27e168b4b4da80 = L.AwesomeMarkers.icon(\n", " {"extraClasses": "fa-rotate-0", "icon": "circle", "iconColor": "white", "markerColor": "red", "prefix": "fa"}\n", " );\n", - " marker_a8605646854f3779e76608978a213f99.setIcon(icon_7e6ec847709d3163e63c9f64c56b3953);\n", + " marker_896695b507afb28930fcb5a332f47cb2.setIcon(icon_3f03d1531d14f55d0f27e168b4b4da80);\n", " \n", " \n", - " marker_a8605646854f3779e76608978a213f99.bindTooltip(\n", + " marker_896695b507afb28930fcb5a332f47cb2.bindTooltip(\n", " `<div>\n", " Destination\n", " </div>`,\n", @@ -545,10 +540,10 @@ "</html>\" style=\"position:absolute;width:100%;height:100%;left:0;top:0;border:none !important;\" allowfullscreen webkitallowfullscreen mozallowfullscreen>" ], "text/plain": [ - "" + "" ] }, - "execution_count": 13, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -569,7 +564,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 16, "id": "aa1e6b11", "metadata": {}, "outputs": [ @@ -590,7 +585,7 @@ " <style>html, body {width: 100%;height: 100%;margin: 0;padding: 0;}</style>\n", " <style>#map {position:absolute;top:0;bottom:0;right:0;left:0;}</style>\n", " <script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/leaflet.js"></script>\n", - " <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>\n", + " <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>\n", " <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js"></script>\n", " <script src="https://cdnjs.cloudflare.com/ajax/libs/Leaflet.awesome-markers/2.0.2/leaflet.awesome-markers.js"></script>\n", " <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.3/dist/leaflet.css"/>\n", @@ -603,7 +598,7 @@ " <meta name="viewport" content="width=device-width,\n", " initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />\n", " <style>\n", - " #map_0a69fb0ffb4fdf07d938c59697d2d5b7 {\n", + " #map_08d0a92c80cee353042d813e9d897e9a {\n", " position: relative;\n", " width: 100.0%;\n", " height: 100.0%;\n", @@ -617,14 +612,14 @@ "<body>\n", " \n", " \n", - " <div class="folium-map" id="map_0a69fb0ffb4fdf07d938c59697d2d5b7" ></div>\n", + " <div class="folium-map" id="map_08d0a92c80cee353042d813e9d897e9a" ></div>\n", " \n", "</body>\n", "<script>\n", " \n", " \n", - " var map_0a69fb0ffb4fdf07d938c59697d2d5b7 = L.map(\n", - " "map_0a69fb0ffb4fdf07d938c59697d2d5b7",\n", + " var map_08d0a92c80cee353042d813e9d897e9a = L.map(\n", + " "map_08d0a92c80cee353042d813e9d897e9a",\n", " {\n", " center: [39.7465774, -105.2216276],\n", " crs: L.CRS.EPSG3857,\n", @@ -638,41 +633,39 @@ "\n", " \n", " \n", - " var tile_layer_bf7e6951595d6bd28e928637e222904d = L.tileLayer(\n", + " var tile_layer_3d8120759aee037fb6485ddcaae7eb08 = L.tileLayer(\n", " "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",\n", " {"attribution": "Data by \\u0026copy; \\u003ca target=\\"_blank\\" href=\\"http://openstreetmap.org\\"\\u003eOpenStreetMap\\u003c/a\\u003e, under \\u003ca target=\\"_blank\\" href=\\"http://www.openstreetmap.org/copyright\\"\\u003eODbL\\u003c/a\\u003e.", "detectRetina": false, "maxNativeZoom": 18, "maxZoom": 18, "minZoom": 0, "noWrap": false, "opacity": 1, "subdomains": "abc", "tms": false}\n", - " );\n", + " ).addTo(map_08d0a92c80cee353042d813e9d897e9a);\n", " \n", " \n", - " tile_layer_bf7e6951595d6bd28e928637e222904d.addTo(map_0a69fb0ffb4fdf07d938c59697d2d5b7);\n", - " \n", - " var poly_line_b2f52dbae2df737185eacbb7f7f1814f = L.polyline(\n", + " var poly_line_fce8b3c76b03f40e0dbe8d7422abf0e5 = L.polyline(\n", " [[39.7273069, -105.1994169], [39.7272989, -105.1994535], [39.7272848, -105.1994868], [39.7272593, -105.1995218], [39.7272272, -105.1995461], [39.7271912, -105.1995576], [39.7271688, -105.199558], [39.7271461, -105.1995532], [39.7271142, -105.1995365], [39.7270868, -105.1995096], [39.7270657, -105.1994741], [39.7270519, -105.1994296], [39.7270483, -105.1993816], [39.727052, -105.1993539], [39.7270571, -105.1993247], [39.7270719, -105.1992916], [39.7270973, -105.1992566], [39.7271293, -105.1992323], [39.7271651, -105.1992207], [39.7271854, -105.1992226], [39.7272113, -105.199227], [39.7273496, -105.1991687], [39.7274185, -105.1991465], [39.7275326, -105.1990928], [39.7276828, -105.1990602], [39.7277455, -105.1990518], [39.7279702, -105.1990232], [39.7281558, -105.1990357], [39.7284384, -105.1990893], [39.7286639, -105.199177], [39.7288222, -105.1992773], [39.7289214, -105.199385], [39.7290292, -105.1995543], [39.7290506, -105.1995879], [39.7292157, -105.1998155], [39.7293094, -105.1998945], [39.7294079, -105.1999604], [39.7295437, -105.200014], [39.7296299, -105.2000721], [39.7297292, -105.2000757], [39.7297135, -105.2007081], [39.7297192, -105.2013106], [39.7298153, -105.2032041], [39.7298256, -105.2033784], [39.7298664, -105.2035708], [39.7298795, -105.203649], [39.7299267, -105.2038558], [39.7301082, -105.2042608], [39.7304911, -105.2048068], [39.7308741, -105.2053111], [39.7310676, -105.2055985], [39.7311933, -105.205824], [39.7312069, -105.2058543], [39.7313075, -105.2061158], [39.731348, -105.206248], [39.731386, -105.2064213], [39.7314237, -105.2067336], [39.731429, -105.2070565], [39.7314226, -105.207224], [39.7314076, -105.2074036], [39.7313611, -105.2077675], [39.7313265, -105.2079124], [39.7312831, -105.20806], [39.7312292, -105.2082086], [39.7311706, -105.208344], [39.7310965, -105.2084777], [39.7310049, -105.208616], [39.7309234, -105.2087235], [39.7308212, -105.2088303], [39.7306227, -105.2089819], [39.7304801, -105.2090736], [39.7303681, -105.2091988], [39.7303327, -105.2092597], [39.7303114, -105.2093181], [39.730299, -105.20939], [39.7302939, -105.2095126], [39.7303105, -105.2096804], [39.7304414, -105.2099267], [39.7305001, -105.2100344], [39.7305871, -105.2101842], [39.7307404, -105.2104481], [39.7309234, -105.2107229], [39.7310934, -105.2109639], [39.731238, -105.211145], [39.7314101, -105.2113464], [39.7315783, -105.2115336], [39.7317541, -105.2117064], [39.7319796, -105.2119089], [39.732359, -105.2122393], [39.7329187, -105.2127184], [39.7334557, -105.213188], [39.7338968, -105.2135676], [39.7342777, -105.2138968], [39.7347139, -105.2142786], [39.7354652, -105.2149301], [39.7356319, -105.2150733], [39.7358844, -105.2152902], [39.7363901, -105.2157275], [39.7369129, -105.2161859], [39.7377243, -105.2168831], [39.7390882, -105.2180682], [39.7397882, -105.2186718], [39.7403043, -105.2191078], [39.7405645, -105.2193336], [39.7406767, -105.2194225], [39.7415803, -105.2202142], [39.7416486, -105.2202727], [39.7417047, -105.2203151], [39.7421502, -105.2206932], [39.7425816, -105.221062], [39.7432687, -105.2215398], [39.7435797, -105.2217631], [39.7436721, -105.2218299], [39.7437366, -105.2218764], [39.7438794, -105.2219759], [39.7439546, -105.2220253], [39.7440082, -105.2220586], [39.7440819, -105.2221021], [39.7441377, -105.2221322], [39.7441808, -105.2221541], [39.7442796, -105.2222043], [39.7443357, -105.2222286], [39.7444062, -105.222259], [39.7444657, -105.2222871], [39.7444962, -105.2223029], [39.7445695, -105.2223431], [39.7446343, -105.2223808], [39.744683, -105.2224104], [39.7447215, -105.2224349], [39.7447658, -105.2224654], [39.744813, -105.222499], [39.7448547, -105.2225315], [39.7448881, -105.2225586], [39.7449328, -105.2225965], [39.7449815, -105.2226391], [39.7451542, -105.2227925], [39.7454784, -105.2230438], [39.7455034, -105.2230623], [39.7455159, -105.2230713], [39.7455403, -105.2230861], [39.7455548, -105.2230941], [39.7455766, -105.2231014], [39.7456018, -105.223106], [39.7456176, -105.2231071], [39.7456337, -105.223107], [39.7456638, -105.2231045], [39.7456814, -105.2231011], [39.7456983, -105.2230945], [39.7457224, -105.2230821], [39.7457338, -105.2230745], [39.7457588, -105.2230543], [39.7457709, -105.223043], [39.7457834, -105.2230284], [39.7458035, -105.2230027], [39.7458905, -105.2228787], [39.7460068, -105.2226624], [39.7461589, -105.2223894], [39.7463617, -105.2220126], [39.7464427, -105.2218672], [39.746482, -105.2217968], [39.7465202, -105.2217322], [39.7465523, -105.2216771], [39.7465774, -105.2216276], [39.7465928, -105.2215823], [39.7466004, -105.2215466], [39.7466042, -105.2215101], [39.7466044, -105.2214695], [39.746601, -105.2214368], [39.7465881, -105.2213862], [39.7465804, -105.2213567], [39.7465774, -105.2213321], [39.7465774, -105.2213048], [39.746581, -105.2212762], [39.7465874, -105.2212518], [39.7465978, -105.2212266], [39.7466125, -105.2212024], [39.7466266, -105.2211858], [39.7466485, -105.2211678], [39.7466708, -105.2211566], [39.746693, -105.2211511], [39.746715, -105.2211506], [39.7467203, -105.2211517], [39.7467798, -105.2211327], [39.7468101, -105.2211204], [39.7468347, -105.2211032], [39.7468673, -105.2210676], [39.7468863, -105.2210389], [39.7469079, -105.2209995], [39.7469752, -105.2208699], [39.7471073, -105.220655], [39.7471371, -105.2206129], [39.7471594, -105.2205845], [39.7472061, -105.2205376], [39.7473376, -105.2202962], [39.747357, -105.2202177], [39.7473682, -105.2201868], [39.7473848, -105.2201519], [39.7475089, -105.2199249], [39.7475861, -105.2197973], [39.7476254, -105.2197401], [39.7476917, -105.2196525], [39.7477724, -105.2195069], [39.7478875, -105.219299], [39.7479219, -105.2192372], [39.7479518, -105.219179], [39.7480957, -105.2189201], [39.7481337, -105.2188515], [39.7482567, -105.2186294], [39.7484851, -105.2182271], [39.7485167, -105.218164], [39.7485605, -105.2180881], [39.7490516, -105.217166], [39.7490955, -105.2170866], [39.7491263, -105.2170387], [39.7493736, -105.2165801], [39.7496841, -105.2160128], [39.7497482, -105.2160733], [39.7504593, -105.216753], [39.7505084, -105.2168007], [39.7505733, -105.2168567], [39.7509525, -105.2172063], [39.7512593, -105.2174884], [39.7513304, -105.2175506], [39.7513869, -105.217604], [39.7517458, -105.2179367], [39.7520945, -105.21826], [39.7521462, -105.2183047], [39.7522126, -105.218369], [39.7523012, -105.2184523], [39.7525052, -105.218642], [39.7527145, -105.2188366], [39.7528959, -105.2189993], [39.7529601, -105.2190649], [39.7530308, -105.219128], [39.7537033, -105.2197718], [39.753764, -105.2198302], [39.7538299, -105.2198915], [39.7542346, -105.2202638], [39.7545279, -105.2205433], [39.7545847, -105.2206031], [39.7546623, -105.2206728], [39.7553326, -105.2212752], [39.755407, -105.221342], [39.7554908, -105.2214211], [39.7561483, -105.2220415], [39.7562254, -105.2221143], [39.7563006, -105.2221853], [39.7566193, -105.222483], [39.7566315, -105.2224957], [39.7569475, -105.2227913], [39.7570561, -105.222895], [39.7574792, -105.2232693], [39.7575579, -105.2233409], [39.7576236, -105.2234019], [39.7578565, -105.2236199], [39.7579703, -105.2237258], [39.7580027, -105.2237561], [39.7583001, -105.2240544], [39.7583591, -105.2241084], [39.7587249, -105.2244446], [39.7588057, -105.22452], [39.7591609, -105.2248516], [39.7595089, -105.2251741], [39.7596887, -105.2253408], [39.7597554, -105.2254024], [39.7598802, -105.2255173], [39.7598939, -105.2255305], [39.7599658, -105.2255988], [39.7600186, -105.2256498], [39.7604008, -105.2259941], [39.7605066, -105.2260931], [39.7605924, -105.226174], [39.760667, -105.2262427], [39.7607946, -105.2263631], [39.7608753, -105.2264373], [39.7615829, -105.2271044], [39.7616266, -105.2271481], [39.7616788, -105.2272008], [39.762203, -105.2276943], [39.7623411, -105.2278299], [39.7630405, -105.2284565], [39.7632824, -105.2286685], [39.7637358, -105.2290679], [39.7640137, -105.229309], [39.7640954, -105.229381], [39.7641743, -105.229462], [39.76436, -105.2296495], [39.7645376, -105.2298658], [39.764824, -105.2301918], [39.7652051, -105.2307044], [39.7654711, -105.2310802], [39.7654801, -105.2310934], [39.7655308, -105.231168], [39.7655792, -105.231232], [39.7655918, -105.2312486], [39.7657719, -105.2315035], [39.7658973, -105.2316751], [39.7659962, -105.2318039], [39.7661018, -105.231924], [39.7662469, -105.2320614], [39.7664515, -105.2322588], [39.7671442, -105.2329283], [39.7673559, -105.2331244], [39.7674414, -105.2332053], [39.7675186, -105.2332845], [39.7675756, -105.2333522], [39.7676291, -105.2334243], [39.7676969, -105.2335369], [39.7677304, -105.233602], [39.767761, -105.2336649], [39.7677908, -105.233739], [39.7678172, -105.233812], [39.7678551, -105.2339422], [39.7678736, -105.2340294], [39.7678904, -105.2341257], [39.7679032, -105.2342329], [39.7679095, -105.2343359], [39.7679068, -105.2344027], [39.7679029, -105.234499], [39.7678875, -105.2346626], [39.7678651, -105.2347791], [39.7678384, -105.2348898], [39.7678006, -105.2350283], [39.7677517, -105.2352031], [39.7677119, -105.2353468]],\n", " {"bubblingMouseEvents": true, "color": "#eff821", "dashArray": null, "dashOffset": null, "fill": false, "fillColor": "#eff821", "fillOpacity": 0.2, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "noClip": false, "opacity": 0.8, "smoothFactor": 1.0, "stroke": true, "weight": 10}\n", - " ).addTo(map_0a69fb0ffb4fdf07d938c59697d2d5b7);\n", + " ).addTo(map_08d0a92c80cee353042d813e9d897e9a);\n", " \n", " \n", - " poly_line_b2f52dbae2df737185eacbb7f7f1814f.bindTooltip(\n", + " poly_line_fce8b3c76b03f40e0dbe8d7422abf0e5.bindTooltip(\n", " `<div>\n", - " 0.14906186986507128\n", + " 4.23738036063421\n", " </div>`,\n", " {"sticky": true}\n", " );\n", " \n", " \n", - " var marker_594f011ae899e7390103390e1b1c4e44 = L.marker(\n", + " var marker_57a9fae5fd1181475e8678e53c2e738d = L.marker(\n", " [39.7273069, -105.1994169],\n", " {}\n", - " ).addTo(map_0a69fb0ffb4fdf07d938c59697d2d5b7);\n", + " ).addTo(map_08d0a92c80cee353042d813e9d897e9a);\n", " \n", " \n", - " var icon_bc3742f29d0bbe9069e73d45bf225cb0 = L.AwesomeMarkers.icon(\n", + " var icon_b7f64c69eda0aa3787f9d028a487e2d0 = L.AwesomeMarkers.icon(\n", " {"extraClasses": "fa-rotate-0", "icon": "circle", "iconColor": "white", "markerColor": "green", "prefix": "fa"}\n", " );\n", - " marker_594f011ae899e7390103390e1b1c4e44.setIcon(icon_bc3742f29d0bbe9069e73d45bf225cb0);\n", + " marker_57a9fae5fd1181475e8678e53c2e738d.setIcon(icon_b7f64c69eda0aa3787f9d028a487e2d0);\n", " \n", " \n", - " marker_594f011ae899e7390103390e1b1c4e44.bindTooltip(\n", + " marker_57a9fae5fd1181475e8678e53c2e738d.bindTooltip(\n", " `<div>\n", " Origin\n", " </div>`,\n", @@ -680,19 +673,19 @@ " );\n", " \n", " \n", - " var marker_12b03e286e45c752ef3ec596908d4c51 = L.marker(\n", + " var marker_f2ad9efdc5dfb0e30a8de14f167f2b0e = L.marker(\n", " [39.7677119, -105.2353468],\n", " {}\n", - " ).addTo(map_0a69fb0ffb4fdf07d938c59697d2d5b7);\n", + " ).addTo(map_08d0a92c80cee353042d813e9d897e9a);\n", " \n", " \n", - " var icon_c9a062431c29df954115c641bf7beb58 = L.AwesomeMarkers.icon(\n", + " var icon_6b5edaf0623d1591b034bea1de6b720e = L.AwesomeMarkers.icon(\n", " {"extraClasses": "fa-rotate-0", "icon": "circle", "iconColor": "white", "markerColor": "red", "prefix": "fa"}\n", " );\n", - " marker_12b03e286e45c752ef3ec596908d4c51.setIcon(icon_c9a062431c29df954115c641bf7beb58);\n", + " marker_f2ad9efdc5dfb0e30a8de14f167f2b0e.setIcon(icon_6b5edaf0623d1591b034bea1de6b720e);\n", " \n", " \n", - " marker_12b03e286e45c752ef3ec596908d4c51.bindTooltip(\n", + " marker_f2ad9efdc5dfb0e30a8de14f167f2b0e.bindTooltip(\n", " `<div>\n", " Destination\n", " </div>`,\n", @@ -700,33 +693,33 @@ " );\n", " \n", " \n", - " var poly_line_a48dac49b22022311b057855def06976 = L.polyline(\n", + " var poly_line_56d979cdb52e71e5e1c0fdd7c6f9b67b = L.polyline(\n", " [[39.7273069, -105.1994169], [39.7272989, -105.1994535], [39.7272848, -105.1994868], [39.7272593, -105.1995218], [39.7272272, -105.1995461], [39.7271912, -105.1995576], [39.7271688, -105.199558], [39.7271461, -105.1995532], [39.7271142, -105.1995365], [39.7270868, -105.1995096], [39.7270657, -105.1994741], [39.7270519, -105.1994296], [39.7270483, -105.1993816], [39.727052, -105.1993539], [39.7270571, -105.1993247], [39.7270719, -105.1992916], [39.7270973, -105.1992566], [39.7271293, -105.1992323], [39.7271651, -105.1992207], [39.7271854, -105.1992226], [39.7272113, -105.199227], [39.7273496, -105.1991687], [39.7274185, -105.1991465], [39.7275326, -105.1990928], [39.7276828, -105.1990602], [39.7277455, -105.1990518], [39.7279702, -105.1990232], [39.7281558, -105.1990357], [39.7284384, -105.1990893], [39.7286639, -105.199177], [39.7288222, -105.1992773], [39.7289214, -105.199385], [39.7290292, -105.1995543], [39.7290506, -105.1995879], [39.7292157, -105.1998155], [39.7293094, -105.1998945], [39.7294079, -105.1999604], [39.7295437, -105.200014], [39.7296299, -105.2000721], [39.7296469, -105.1996789], [39.7296738, -105.1987284], [39.7296906, -105.1986794], [39.7297244, -105.1985856], [39.7297272, -105.1984193], [39.7299723, -105.1984198], [39.7302117, -105.1984227], [39.7303235, -105.1984285], [39.7304434, -105.198443], [39.7305651, -105.1984732], [39.7307182, -105.1985167], [39.7308487, -105.1985623], [39.7309909, -105.1986311], [39.731127, -105.1987144], [39.7312292, -105.1987897], [39.7313292, -105.1988731], [39.7314292, -105.1989611], [39.731524, -105.1990554], [39.7316089, -105.1991466], [39.7317085, -105.1992639], [39.7318575, -105.1994488], [39.7319522, -105.1995697], [39.7320423, -105.1996716], [39.7321372, -105.1997578], [39.7322615, -105.1998506], [39.7323697, -105.1999163], [39.7324712, -105.1999637], [39.7325898, -105.2000081], [39.732689, -105.2000309], [39.7327987, -105.2000627], [39.732945, -105.2001049], [39.7330635, -105.2001374], [39.7331838, -105.2001914], [39.7332899, -105.2002528], [39.7334312, -105.200363], [39.7335257, -105.2004544], [39.7335523, -105.2004835], [39.7336132, -105.2005503], [39.7336902, -105.2006555], [39.733767, -105.2007557], [39.7338411, -105.2008491], [39.7339359, -105.2009412], [39.7340357, -105.2010154], [39.7340742, -105.201042], [39.7341117, -105.2010678], [39.7341542, -105.2010901], [39.734211, -105.2011198], [39.7343173, -105.2011712], [39.7344056, -105.2012021], [39.7345704, -105.2012376], [39.7347726, -105.2012753], [39.7351329, -105.2013311], [39.7352203, -105.2013441], [39.7353243, -105.201352], [39.7354415, -105.2013441], [39.7355491, -105.2013268], [39.7357022, -105.2012901], [39.7358395, -105.201259], [39.7362429, -105.2011675], [39.7367157, -105.2010639], [39.7367857, -105.2010539], [39.7368531, -105.2010442], [39.7369645, -105.2010356], [39.7370755, -105.201044], [39.7371895, -105.2010632], [39.7373179, -105.201099], [39.737714, -105.2012295], [39.7379567, -105.2013009], [39.7384451, -105.2014404], [39.7386872, -105.2015167], [39.7395717, -105.2017905], [39.7397153, -105.2018369], [39.7398269, -105.2018673], [39.7399356, -105.2018843], [39.7400618, -105.2018961], [39.740184, -105.2018932], [39.7403133, -105.2018823], [39.7404288, -105.2018645], [39.7405518, -105.2018387], [39.740665, -105.2018073], [39.7407597, -105.2017713], [39.7408728, -105.2017196], [39.740992, -105.2016551], [39.7411268, -105.2015625], [39.7412599, -105.2014489], [39.7413718, -105.2013453], [39.7414758, -105.2012326], [39.741608, -105.2010691], [39.7416901, -105.200964], [39.7417903, -105.2008396], [39.7417994, -105.200826], [39.7419078, -105.2006985], [39.7419027, -105.2006189], [39.7419122, -105.200549], [39.7419496, -105.2004812], [39.7419894, -105.20044], [39.742047, -105.2004033], [39.7420816, -105.2004], [39.7421282, -105.2004131], [39.7421839, -105.200448], [39.7422234, -105.2004924], [39.7422448, -105.2005578], [39.7422566, -105.2006096], [39.7422556, -105.2006625], [39.7422421, -105.2007231], [39.7422712, -105.2008198], [39.7423227, -105.200901], [39.7428069, -105.2016549], [39.7429048, -105.2018114], [39.7429131, -105.2018245], [39.7431503, -105.2022035], [39.7434374, -105.2026477], [39.7435807, -105.2028659], [39.7437705, -105.2031843], [39.7441255, -105.2037193], [39.7444536, -105.2042289], [39.7445369, -105.204369], [39.7448087, -105.2048085], [39.7450389, -105.205197], [39.745164, -105.2054397], [39.7452617, -105.205642], [39.745359, -105.2058753], [39.7454413, -105.2060693], [39.7454698, -105.2061365], [39.7455586, -105.2063498], [39.7456394, -105.2066197], [39.7457343, -105.2068984], [39.7458096, -105.2071619], [39.7458432, -105.2072893], [39.7459057, -105.2075162], [39.7459547, -105.2077463], [39.7460104, -105.2080066], [39.7460312, -105.2081149], [39.7460508, -105.2082709], [39.7460836, -105.2084787], [39.7461092, -105.2086125], [39.7461165, -105.2087078], [39.7461128, -105.2088019], [39.7461078, -105.2089177], [39.7461163, -105.208995], [39.7461377, -105.2090672], [39.7461588, -105.2091147], [39.7462509, -105.2092159], [39.746281, -105.2092382], [39.7462978, -105.209256], [39.7463242, -105.209292], [39.7463458, -105.2093473], [39.746361, -105.2094251], [39.7463921, -105.2095768], [39.7464353, -105.2097133], [39.7465001, -105.2098285], [39.7467097, -105.21004], [39.7467814, -105.2101094], [39.7473357, -105.2106047], [39.7474554, -105.2107116], [39.7475615, -105.2108064], [39.7475958, -105.2108371], [39.747653, -105.2108907], [39.7481502, -105.2113567], [39.7483361, -105.2115309], [39.7483726, -105.2115651], [39.7484183, -105.2116079], [39.7484718, -105.2116584], [39.7487212, -105.2118951], [39.749238, -105.2123721], [39.7492814, -105.2124135], [39.7500059, -105.2130759], [39.7500454, -105.2131129], [39.7503812, -105.2134256], [39.7504627, -105.2135015], [39.7508219, -105.2138362], [39.7508792, -105.2138895], [39.7509348, -105.2139426], [39.7511093, -105.2141093], [39.7516271, -105.2146038], [39.7516716, -105.2146463], [39.751739, -105.2147085], [39.7524829, -105.2153953], [39.7525012, -105.2154123], [39.7532517, -105.2161101], [39.7533052, -105.2161598], [39.7540716, -105.2168674], [39.7541308, -105.2169221], [39.7544795, -105.2172508], [39.7546281, -105.2174522], [39.7546745, -105.2175037], [39.7548394, -105.2176869], [39.7549112, -105.2177871], [39.7548521, -105.2179433], [39.7546476, -105.2183047], [39.7544379, -105.2186805], [39.7544293, -105.218688], [39.7543723, -105.2187394], [39.7544376, -105.2188002], [39.7544872, -105.2188464], [39.7547934, -105.2191313], [39.7551071, -105.2194077], [39.7551368, -105.2194339], [39.755202, -105.2194913], [39.7552559, -105.2195411], [39.755615, -105.2198733], [39.755696, -105.2199479], [39.7559351, -105.2201693], [39.7559559, -105.220191], [39.7560109, -105.2202486], [39.7560703, -105.2203182], [39.756257, -105.2205368], [39.7562813, -105.2205598], [39.7564057, -105.2206774], [39.7565461, -105.2208058], [39.7567417, -105.2209442], [39.7566986, -105.2211001], [39.756638, -105.2213112], [39.7565732, -105.2214643], [39.7565209, -105.2215678], [39.7564595, -105.2216814], [39.7562914, -105.2219923], [39.7562254, -105.2221143], [39.7563006, -105.2221853], [39.7566193, -105.222483], [39.7566315, -105.2224957], [39.7569475, -105.2227913], [39.7570561, -105.222895], [39.7574792, -105.2232693], [39.7575579, -105.2233409], [39.7576236, -105.2234019], [39.7578565, -105.2236199], [39.7579703, -105.2237258], [39.7580027, -105.2237561], [39.7583001, -105.2240544], [39.7583591, -105.2241084], [39.7587249, -105.2244446], [39.7588057, -105.22452], [39.7591609, -105.2248516], [39.7595089, -105.2251741], [39.7596887, -105.2253408], [39.7597554, -105.2254024], [39.7598802, -105.2255173], [39.7598939, -105.2255305], [39.7599658, -105.2255988], [39.7600186, -105.2256498], [39.7604008, -105.2259941], [39.7605066, -105.2260931], [39.7605924, -105.226174], [39.760667, -105.2262427], [39.7607946, -105.2263631], [39.7608753, -105.2264373], [39.7615829, -105.2271044], [39.7616266, -105.2271481], [39.7616788, -105.2272008], [39.762203, -105.2276943], [39.7623411, -105.2278299], [39.7630405, -105.2284565], [39.7632824, -105.2286685], [39.7637358, -105.2290679], [39.7640137, -105.229309], [39.7640954, -105.229381], [39.7641743, -105.229462], [39.76436, -105.2296495], [39.7645376, -105.2298658], [39.764824, -105.2301918], [39.7652051, -105.2307044], [39.7654711, -105.2310802], [39.7654801, -105.2310934], [39.7655308, -105.231168], [39.7655792, -105.231232], [39.7655918, -105.2312486], [39.7657719, -105.2315035], [39.7658973, -105.2316751], [39.7659962, -105.2318039], [39.7661018, -105.231924], [39.7662469, -105.2320614], [39.7664515, -105.2322588], [39.7671442, -105.2329283], [39.7673559, -105.2331244], [39.7674414, -105.2332053], [39.7675186, -105.2332845], [39.7675756, -105.2333522], [39.7676291, -105.2334243], [39.7676969, -105.2335369], [39.7677304, -105.233602], [39.767761, -105.2336649], [39.7677908, -105.233739], [39.7678172, -105.233812], [39.7678551, -105.2339422], [39.7678736, -105.2340294], [39.7678904, -105.2341257], [39.7679032, -105.2342329], [39.7679095, -105.2343359], [39.7679068, -105.2344027], [39.7679029, -105.234499], [39.7678875, -105.2346626], [39.7678651, -105.2347791], [39.7678384, -105.2348898], [39.7678006, -105.2350283], [39.7677517, -105.2352031], [39.7677119, -105.2353468]],\n", " {"bubblingMouseEvents": true, "color": "#0c0786", "dashArray": null, "dashOffset": null, "fill": false, "fillColor": "#0c0786", "fillOpacity": 0.2, "fillRule": "evenodd", "lineCap": "round", "lineJoin": "round", "noClip": false, "opacity": 0.8, "smoothFactor": 1.0, "stroke": true, "weight": 10}\n", - " ).addTo(map_0a69fb0ffb4fdf07d938c59697d2d5b7);\n", + " ).addTo(map_08d0a92c80cee353042d813e9d897e9a);\n", " \n", " \n", - " poly_line_a48dac49b22022311b057855def06976.bindTooltip(\n", + " poly_line_56d979cdb52e71e5e1c0fdd7c6f9b67b.bindTooltip(\n", " `<div>\n", - " 0.14412252939511852\n", + " 4.013268489898491\n", " </div>`,\n", " {"sticky": true}\n", " );\n", " \n", " \n", - " var marker_a42455ffdb4b3173ce10e967b7fe4e0d = L.marker(\n", + " var marker_8223aabc91c0f1ea02ec2ea5181c8453 = L.marker(\n", " [39.7273069, -105.1994169],\n", " {}\n", - " ).addTo(map_0a69fb0ffb4fdf07d938c59697d2d5b7);\n", + " ).addTo(map_08d0a92c80cee353042d813e9d897e9a);\n", " \n", " \n", - " var icon_182dc1421ab527e4215256615066eba2 = L.AwesomeMarkers.icon(\n", + " var icon_efff58254cff016e5572e3aa608b8d64 = L.AwesomeMarkers.icon(\n", " {"extraClasses": "fa-rotate-0", "icon": "circle", "iconColor": "white", "markerColor": "green", "prefix": "fa"}\n", " );\n", - " marker_a42455ffdb4b3173ce10e967b7fe4e0d.setIcon(icon_182dc1421ab527e4215256615066eba2);\n", + " marker_8223aabc91c0f1ea02ec2ea5181c8453.setIcon(icon_efff58254cff016e5572e3aa608b8d64);\n", " \n", " \n", - " marker_a42455ffdb4b3173ce10e967b7fe4e0d.bindTooltip(\n", + " marker_8223aabc91c0f1ea02ec2ea5181c8453.bindTooltip(\n", " `<div>\n", " Origin\n", " </div>`,\n", @@ -734,19 +727,19 @@ " );\n", " \n", " \n", - " var marker_3f60358a3318165e8ed01991c2970fe3 = L.marker(\n", + " var marker_e3fa3a2726000cb73f319b6758aa89a8 = L.marker(\n", " [39.7677119, -105.2353468],\n", " {}\n", - " ).addTo(map_0a69fb0ffb4fdf07d938c59697d2d5b7);\n", + " ).addTo(map_08d0a92c80cee353042d813e9d897e9a);\n", " \n", " \n", - " var icon_7a556a87128e1f5ad26d35ebcac3cf6e = L.AwesomeMarkers.icon(\n", + " var icon_1d3ff4766d7167f6aae206479f62d543 = L.AwesomeMarkers.icon(\n", " {"extraClasses": "fa-rotate-0", "icon": "circle", "iconColor": "white", "markerColor": "red", "prefix": "fa"}\n", " );\n", - " marker_3f60358a3318165e8ed01991c2970fe3.setIcon(icon_7a556a87128e1f5ad26d35ebcac3cf6e);\n", + " marker_e3fa3a2726000cb73f319b6758aa89a8.setIcon(icon_1d3ff4766d7167f6aae206479f62d543);\n", " \n", " \n", - " marker_3f60358a3318165e8ed01991c2970fe3.bindTooltip(\n", + " marker_e3fa3a2726000cb73f319b6758aa89a8.bindTooltip(\n", " `<div>\n", " Destination\n", " </div>`,\n", @@ -757,16 +750,16 @@ "</html>\" style=\"position:absolute;width:100%;height:100%;left:0;top:0;border:none !important;\" allowfullscreen webkitallowfullscreen mozallowfullscreen>" ], "text/plain": [ - "" + "" ] }, - "execution_count": 14, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "m = plot_routes_folium(results, value_fn=lambda r: r[\"traversal_summary\"][\"energy\"], color_map=\"plasma\")\n", + "m = plot_routes_folium(results, value_fn=lambda r: r[\"traversal_summary\"][\"vehicle\"][\"energy\"], color_map=\"plasma\")\n", "m" ] }, diff --git a/python/nrel/routee/compass/resources/models/2016_BMW_i3_REx_PHEV_Charge_Depleting.bin b/python/nrel/routee/compass/resources/models/2016_BMW_i3_REx_PHEV_Charge_Depleting.bin new file mode 100644 index 00000000..d6d6b993 Binary files /dev/null and b/python/nrel/routee/compass/resources/models/2016_BMW_i3_REx_PHEV_Charge_Depleting.bin differ diff --git a/python/nrel/routee/compass/resources/models/2016_BMW_i3_REx_PHEV_Charge_Sustaining.bin b/python/nrel/routee/compass/resources/models/2016_BMW_i3_REx_PHEV_Charge_Sustaining.bin new file mode 100644 index 00000000..373891f8 Binary files /dev/null and b/python/nrel/routee/compass/resources/models/2016_BMW_i3_REx_PHEV_Charge_Sustaining.bin differ diff --git a/python/nrel/routee/compass/resources/models/2016_CHEVROLET_Volt_Charge_Depleting.bin b/python/nrel/routee/compass/resources/models/2016_CHEVROLET_Volt_Charge_Depleting.bin new file mode 100644 index 00000000..32927154 Binary files /dev/null and b/python/nrel/routee/compass/resources/models/2016_CHEVROLET_Volt_Charge_Depleting.bin differ diff --git a/python/nrel/routee/compass/resources/models/2016_CHEVROLET_Volt_Charge_Sustaining.bin b/python/nrel/routee/compass/resources/models/2016_CHEVROLET_Volt_Charge_Sustaining.bin new file mode 100644 index 00000000..5745af3d Binary files /dev/null and b/python/nrel/routee/compass/resources/models/2016_CHEVROLET_Volt_Charge_Sustaining.bin differ diff --git a/python/nrel/routee/compass/resources/models/2016_FORD_C-MAX_(PHEV)_Charge_Depleting.bin b/python/nrel/routee/compass/resources/models/2016_FORD_C-MAX_(PHEV)_Charge_Depleting.bin new file mode 100644 index 00000000..a7bc8059 Binary files /dev/null and b/python/nrel/routee/compass/resources/models/2016_FORD_C-MAX_(PHEV)_Charge_Depleting.bin differ diff --git a/python/nrel/routee/compass/resources/models/2016_FORD_C-MAX_(PHEV)_Charge_Sustaining.bin b/python/nrel/routee/compass/resources/models/2016_FORD_C-MAX_(PHEV)_Charge_Sustaining.bin new file mode 100644 index 00000000..e283a16d Binary files /dev/null and b/python/nrel/routee/compass/resources/models/2016_FORD_C-MAX_(PHEV)_Charge_Sustaining.bin differ diff --git a/python/nrel/routee/compass/resources/models/2016_HYUNDAI_Sonata_PHEV_Charge_Depleting.bin b/python/nrel/routee/compass/resources/models/2016_HYUNDAI_Sonata_PHEV_Charge_Depleting.bin new file mode 100644 index 00000000..91446c6e Binary files /dev/null and b/python/nrel/routee/compass/resources/models/2016_HYUNDAI_Sonata_PHEV_Charge_Depleting.bin differ diff --git a/python/nrel/routee/compass/resources/models/2016_HYUNDAI_Sonata_PHEV_Charge_Sustaining.bin b/python/nrel/routee/compass/resources/models/2016_HYUNDAI_Sonata_PHEV_Charge_Sustaining.bin new file mode 100644 index 00000000..a47c1085 Binary files /dev/null and b/python/nrel/routee/compass/resources/models/2016_HYUNDAI_Sonata_PHEV_Charge_Sustaining.bin differ diff --git a/python/nrel/routee/compass/resources/models/2017_Prius_Prime_Charge_Depleting.bin b/python/nrel/routee/compass/resources/models/2017_Prius_Prime_Charge_Depleting.bin new file mode 100644 index 00000000..a55bd3eb Binary files /dev/null and b/python/nrel/routee/compass/resources/models/2017_Prius_Prime_Charge_Depleting.bin differ diff --git a/python/nrel/routee/compass/resources/models/2017_Prius_Prime_Charge_Sustaining.bin b/python/nrel/routee/compass/resources/models/2017_Prius_Prime_Charge_Sustaining.bin new file mode 100644 index 00000000..ed388588 Binary files /dev/null and b/python/nrel/routee/compass/resources/models/2017_Prius_Prime_Charge_Sustaining.bin differ diff --git a/python/nrel/routee/compass/resources/osm_default_energy.toml b/python/nrel/routee/compass/resources/osm_default_energy.toml index 7dae2fdb..a055ce36 100644 --- a/python/nrel/routee/compass/resources/osm_default_energy.toml +++ b/python/nrel/routee/compass/resources/osm_default_energy.toml @@ -17,14 +17,15 @@ output_plugins = [ ] [traversal] -type = "speed_grade_energy_model" +type = "energy_model" speed_table_input_file = "edges-posted-speed-enumerated.txt.gz" speed_table_speed_unit = "kilometers_per_hour" output_time_unit = "minutes" output_distance_unit = "miles" -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2012_Ford_Focus" +type = "single_fuel" model_input_file = "models/2012_Ford_Focus.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -33,8 +34,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02857143 real_world_energy_adjustment = 1.166 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2012_Ford_Fusion" +type = "single_fuel" model_input_file = "models/2012_Ford_Fusion.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -43,8 +45,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02857143 real_world_energy_adjustment = 1.166 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2016_AUDI_A3_4cyl_2WD" +type = "single_fuel" model_input_file = "models/2016_AUDI_A3_4cyl_2WD.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -53,8 +56,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02857143 real_world_energy_adjustment = 1.166 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2016_BMW_328d_4cyl_2WD" +type = "single_fuel" model_input_file = "models/2016_BMW_328d_4cyl_2WD.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -63,8 +67,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02857143 real_world_energy_adjustment = 1.166 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2016_CHEVROLET_Malibu_4cyl_2WD" +type = "single_fuel" model_input_file = "models/2016_CHEVROLET_Malibu_4cyl_2WD.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -73,8 +78,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02857143 real_world_energy_adjustment = 1.166 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2016_CHEVROLET_Spark_EV" +type = "single_fuel" model_input_file = "models/2016_CHEVROLET_Spark_EV.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -83,8 +89,9 @@ energy_rate_unit = "kilowatt_hours_per_mile" ideal_energy_rate = 0.2 real_world_energy_adjustment = 1.3958 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2016_FORD_C-MAX_HEV" +type = "single_fuel" model_input_file = "models/2016_FORD_C-MAX_HEV.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -93,8 +100,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02 real_world_energy_adjustment = 1.1252 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2016_FORD_Escape_4cyl_2WD" +type = "single_fuel" model_input_file = "models/2016_FORD_Escape_4cyl_2WD.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -103,8 +111,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02857143 real_world_energy_adjustment = 1.166 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2016_FORD_Explorer_4cyl_2WD" +type = "single_fuel" model_input_file = "models/2016_FORD_Explorer_4cyl_2WD.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -113,8 +122,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02857143 real_world_energy_adjustment = 1.166 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2016_HYUNDAI_Elantra_4cyl_2WD" +type = "single_fuel" model_input_file = "models/2016_HYUNDAI_Elantra_4cyl_2WD.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -123,8 +133,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02857143 real_world_energy_adjustment = 1.166 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2016_KIA_Optima_Hybrid" +type = "single_fuel" model_input_file = "models/2016_KIA_Optima_Hybrid.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -133,8 +144,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02 real_world_energy_adjustment = 1.1252 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2016_Leaf_24_kWh" +type = "single_fuel" model_input_file = "models/2016_Leaf_24_kWh.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -143,8 +155,9 @@ energy_rate_unit = "kilowatt_hours_per_mile" ideal_energy_rate = 0.2 real_world_energy_adjustment = 1.3958 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2016_MITSUBISHI_i-MiEV" +type = "single_fuel" model_input_file = "models/2016_MITSUBISHI_i-MiEV.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -153,8 +166,9 @@ energy_rate_unit = "kilowatt_hours_per_mile" ideal_energy_rate = 0.2 real_world_energy_adjustment = 1.3958 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2016_Nissan_Leaf_30_kWh" +type = "single_fuel" model_input_file = "models/2016_Nissan_Leaf_30_kWh.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -163,8 +177,9 @@ energy_rate_unit = "kilowatt_hours_per_mile" ideal_energy_rate = 0.2 real_world_energy_adjustment = 1.3958 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2016_TESLA_Model_S60_2WD" +type = "single_fuel" model_input_file = "models/2016_TESLA_Model_S60_2WD.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -173,8 +188,9 @@ energy_rate_unit = "kilowatt_hours_per_mile" ideal_energy_rate = 0.2 real_world_energy_adjustment = 1.3958 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2016_TOYOTA_Camry_4cyl_2WD" +type = "single_fuel" model_input_file = "models/2016_TOYOTA_Camry_4cyl_2WD.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -183,8 +199,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02857143 real_world_energy_adjustment = 1.166 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2016_TOYOTA_Corolla_4cyl_2WD" +type = "single_fuel" model_input_file = "models/2016_TOYOTA_Corolla_4cyl_2WD.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -193,8 +210,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02857143 real_world_energy_adjustment = 1.166 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2016_TOYOTA_Highlander_Hybrid" +type = "single_fuel" model_input_file = "models/2016_TOYOTA_Highlander_Hybrid.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -203,8 +221,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02 real_world_energy_adjustment = 1.1252 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2016_Toyota_Prius_Two_FWD" +type = "single_fuel" model_input_file = "models/2016_Toyota_Prius_Two_FWD.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -213,8 +232,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02 real_world_energy_adjustment = 1.1252 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2017_CHEVROLET_Bolt" +type = "single_fuel" model_input_file = "models/2017_CHEVROLET_Bolt.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -223,8 +243,9 @@ energy_rate_unit = "kilowatt_hours_per_mile" ideal_energy_rate = 0.2 real_world_energy_adjustment = 1.3958 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2017_Maruti_Dzire_VDI" +type = "single_fuel" model_input_file = "models/2017_Maruti_Dzire_VDI.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -233,8 +254,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02857143 real_world_energy_adjustment = 1.166 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2017_Toyota_Highlander_3.5_L" +type = "single_fuel" model_input_file = "models/2017_Toyota_Highlander_3.5_L.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -243,8 +265,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02857143 real_world_energy_adjustment = 1.166 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2020_Chevrolet_Colorado_2WD_Diesel" +type = "single_fuel" model_input_file = "models/2020_Chevrolet_Colorado_2WD_Diesel.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -253,8 +276,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02857143 real_world_energy_adjustment = 1.166 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2020_VW_Golf_1.5TSI" +type = "single_fuel" model_input_file = "models/2020_VW_Golf_1.5TSI.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -263,8 +287,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02857143 real_world_energy_adjustment = 1.166 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2020_VW_Golf_2.0TDI" +type = "single_fuel" model_input_file = "models/2020_VW_Golf_2.0TDI.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -273,8 +298,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02857143 real_world_energy_adjustment = 1.166 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2021_Fiat_Panda_Mild_Hybrid" +type = "single_fuel" model_input_file = "models/2021_Fiat_Panda_Mild_Hybrid.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -283,8 +309,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02857143 real_world_energy_adjustment = 1.166 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2021_Peugot_3008" +type = "single_fuel" model_input_file = "models/2021_Peugot_3008.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -293,8 +320,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02857143 real_world_energy_adjustment = 1.166 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2022_Ford_F-150_Lightning_4WD" +type = "single_fuel" model_input_file = "models/2022_Ford_F-150_Lightning_4WD.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -303,8 +331,9 @@ energy_rate_unit = "kilowatt_hours_per_mile" ideal_energy_rate = 0.2 real_world_energy_adjustment = 1.3958 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2022_Renault_Zoe_ZE50_R135" +type = "single_fuel" model_input_file = "models/2022_Renault_Zoe_ZE50_R135.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -313,8 +342,9 @@ energy_rate_unit = "kilowatt_hours_per_mile" ideal_energy_rate = 0.2 real_world_energy_adjustment = 1.3958 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2022_Tesla_Model_3_RWD" +type = "single_fuel" model_input_file = "models/2022_Tesla_Model_3_RWD.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -323,8 +353,9 @@ energy_rate_unit = "kilowatt_hours_per_mile" ideal_energy_rate = 0.2 real_world_energy_adjustment = 1.3958 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2022_Tesla_Model_Y_RWD" +type = "single_fuel" model_input_file = "models/2022_Tesla_Model_Y_RWD.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -333,8 +364,9 @@ energy_rate_unit = "kilowatt_hours_per_mile" ideal_energy_rate = 0.2 real_world_energy_adjustment = 1.3958 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2022_Toyota_Yaris_Hybrid_Mid" +type = "single_fuel" model_input_file = "models/2022_Toyota_Yaris_Hybrid_Mid.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -343,8 +375,9 @@ energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02 real_world_energy_adjustment = 1.1252 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2022_Volvo_XC40_Recharge_twin" +type = "single_fuel" model_input_file = "models/2022_Volvo_XC40_Recharge_twin.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -353,8 +386,9 @@ energy_rate_unit = "kilowatt_hours_per_mile" ideal_energy_rate = 0.2 real_world_energy_adjustment = 1.3958 -[[traversal.energy_models]] +[[traversal.vehicles]] name = "2023_Mitsubishi_Pajero_Sport" +type = "single_fuel" model_input_file = "models/2023_Mitsubishi_Pajero_Sport.bin" model_type = "smartcore" speed_unit = "miles_per_hour" @@ -362,3 +396,123 @@ grade_unit = "decimal" energy_rate_unit = "gallons_gasoline_per_mile" ideal_energy_rate = 0.02857143 real_world_energy_adjustment = 1.166 + +[[traversal.vehicles]] +type = "dual_fuel" +name = "2016_BMW_i3_REx_PHEV" +battery_capacity = 12 +battery_capacity_unit = "kilowatt_hours" +[traversal.vehicles.charge_depleting] +name = "2016_BMW_i3_REx_PHEV_Charge_Depleting" +model_input_file = "models/2016_BMW_i3_REx_PHEV_Charge_Depleting.bin" +model_type = "smartcore" +speed_unit = "miles_per_hour" +grade_unit = "decimal" +energy_rate_unit = "kilowatt_hours_per_mile" +ideal_energy_rate = 0.2 +real_world_energy_adjustment = 1.3958 +[traversal.vehicles.charge_sustaining] +name = "2016_BMW_i3_REx_PHEV_Charge_Sustaining" +model_input_file = "models/2016_BMW_i3_REx_PHEV_Charge_Sustaining.bin" +model_type = "smartcore" +speed_unit = "miles_per_hour" +grade_unit = "decimal" +energy_rate_unit = "gallons_gasoline_per_mile" +ideal_energy_rate = 0.02 +real_world_energy_adjustment = 1.1252 + +[[traversal.vehicles]] +type = "dual_fuel" +name = "2016_CHEVROLET_Volt" +battery_capacity = 12 +battery_capacity_unit = "kilowatt_hours" +[traversal.vehicles.charge_depleting] +name = "2016_CHEVROLET_Volt_Charge_Depleting" +model_input_file = "models/2016_CHEVROLET_Volt_Charge_Depleting.bin" +model_type = "smartcore" +speed_unit = "miles_per_hour" +grade_unit = "decimal" +energy_rate_unit = "kilowatt_hours_per_mile" +ideal_energy_rate = 0.2 +real_world_energy_adjustment = 1.3958 +[traversal.vehicles.charge_sustaining] +name = "2016_CHEVROLET_Volt_Charge_Sustaining" +model_input_file = "models/2016_CHEVROLET_Volt_Charge_Sustaining.bin" +model_type = "smartcore" +speed_unit = "miles_per_hour" +grade_unit = "decimal" +energy_rate_unit = "gallons_gasoline_per_mile" +ideal_energy_rate = 0.02 +real_world_energy_adjustment = 1.1252 + +[[traversal.vehicles]] +type = "dual_fuel" +name = "2016_FORD_C-MAX_(PHEV)" +battery_capacity = 7.6 +battery_capacity_unit = "kilowatt_hours" +[traversal.vehicles.charge_depleting] +name = "2016_FORD_C-MAX_(PHEV)_Charge_Depleting" +model_input_file = "models/2016_FORD_C-MAX_(PHEV)_Charge_Depleting.bin" +model_type = "smartcore" +speed_unit = "miles_per_hour" +grade_unit = "decimal" +energy_rate_unit = "kilowatt_hours_per_mile" +ideal_energy_rate = 0.2 +real_world_energy_adjustment = 1.3958 +[traversal.vehicles.charge_sustaining] +name = "2016_FORD_C-MAX_(PHEV)_Charge_Sustaining" +model_input_file = "models/2016_FORD_C-MAX_(PHEV)_Charge_Sustaining.bin" +model_type = "smartcore" +speed_unit = "miles_per_hour" +grade_unit = "decimal" +energy_rate_unit = "gallons_gasoline_per_mile" +ideal_energy_rate = 0.02 +real_world_energy_adjustment = 1.1252 + +[[traversal.vehicles]] +type = "dual_fuel" +name = "2016_HYUNDAI_Sonata_PHEV" +battery_capacity = 9.8 +battery_capacity_unit = "kilowatt_hours" +[traversal.vehicles.charge_depleting] +name = "2016_HYUNDAI_Sonata_PHEV_Charge_Depleting" +model_input_file = "models/2016_HYUNDAI_Sonata_PHEV_Charge_Depleting.bin" +model_type = "smartcore" +speed_unit = "miles_per_hour" +grade_unit = "decimal" +energy_rate_unit = "kilowatt_hours_per_mile" +ideal_energy_rate = 0.2 +real_world_energy_adjustment = 1.3958 +[traversal.vehicles.charge_sustaining] +name = "2016_HYUNDAI_Sonata_PHEV_Charge_Sustaining" +model_input_file = "models/2016_HYUNDAI_Sonata_PHEV_Charge_Sustaining.bin" +model_type = "smartcore" +speed_unit = "miles_per_hour" +grade_unit = "decimal" +energy_rate_unit = "gallons_gasoline_per_mile" +ideal_energy_rate = 0.02 +real_world_energy_adjustment = 1.1252 + +[[traversal.vehicles]] +type = "dual_fuel" +name = "2017_Prius_Prime" +battery_capacity = 8 +battery_capacity_unit = "kilowatt_hours" +[traversal.vehicles.charge_depleting] +name = "2017_Prius_Prime_Charge_Depleting" +model_input_file = "models/2017_Prius_Prime_Charge_Depleting.bin" +model_type = "smartcore" +speed_unit = "miles_per_hour" +grade_unit = "decimal" +energy_rate_unit = "kilowatt_hours_per_mile" +ideal_energy_rate = 0.2 +real_world_energy_adjustment = 1.3958 +[traversal.vehicles.charge_sustaining] +name = "2017_Prius_Prime_Charge_Sustaining" +model_input_file = "models/2017_Prius_Prime_Charge_Sustaining.bin" +model_type = "smartcore" +speed_unit = "miles_per_hour" +grade_unit = "decimal" +energy_rate_unit = "gallons_gasoline_per_mile" +ideal_energy_rate = 0.02 +real_world_energy_adjustment = 1.1252 \ No newline at end of file diff --git a/rust/routee-compass-core/src/util/unit/energy_unit.rs b/rust/routee-compass-core/src/util/unit/energy_unit.rs index 3370a01e..1f718219 100644 --- a/rust/routee-compass-core/src/util/unit/energy_unit.rs +++ b/rust/routee-compass-core/src/util/unit/energy_unit.rs @@ -1,13 +1,25 @@ use serde::{Deserialize, Serialize}; -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +use super::Energy; + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)] #[serde(rename_all = "snake_case")] pub enum EnergyUnit { GallonsGasoline, KilowattHours, } -impl EnergyUnit {} +impl EnergyUnit { + pub fn convert(&self, value: Energy, target: EnergyUnit) -> Energy { + use EnergyUnit as S; + match (self, target) { + (S::GallonsGasoline, S::GallonsGasoline) => value, + (S::GallonsGasoline, S::KilowattHours) => value * 33.41, + (S::KilowattHours, S::GallonsGasoline) => value * 0.0299, + (S::KilowattHours, S::KilowattHours) => value, + } + } +} impl std::fmt::Display for EnergyUnit { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/rust/routee-compass-powertrain/src/doc.md b/rust/routee-compass-powertrain/src/doc.md index 0ea5e76c..cbd066dc 100644 --- a/rust/routee-compass-powertrain/src/doc.md +++ b/rust/routee-compass-powertrain/src/doc.md @@ -11,7 +11,7 @@ RouteE Powertrain models are trained and exported via the RouteE Powertrain util ### Model Runtimes There are two underlying model runtimes available, [smartcore](https://smartcorelib.org/) and [ort](https://github.com/pykeio/ort) (for [ONNX](https://onnx.ai/) models). -By default, this crate is loaded with ONNX deactivated. +By default, this crate is loaded with ONNX deactivated. To activate the ONNX feature, pass the `onnx` feature flag during compilation. Runtime kernels for 3 common OSs have been provided in the onnx-runtime directory within this crate. For more information on cargo features, see The Cargo Book chapter on [Features](https://doc.rust-lang.org/cargo/reference/features.html). @@ -25,29 +25,32 @@ An example traversal model configuration that uses this crate may look like this ```toml [traversal] -type = "speed_grade_energy_model" -model_type = "smartcore" +type = "energy_model" speed_table_input_file = "edges-posted-speed-enumerated.txt.gz" -energy_model_input_file = "2016_TOYOTA_Camry_4cyl_2WD.bin" -ideal_energy_rate = 0.02857142857 speed_table_speed_unit = "kilometers_per_hour" -energy_model_speed_unit = "miles_per_hour" -energy_model_grade_unit = "decimal" -energy_model_energy_rate_unit = "gallons_gasoline_per_mile" output_time_unit = "minutes" output_distance_unit = "miles" -grade_table_input_file = "edges-grade-enumerated.txt.gz" -grade_table_grade_unit = "decimal" + +[[traversal.vehicles]] +name = "2012_Ford_Focus" +type = "single_fuel" +model_input_file = "models/2012_Ford_Focus.bin" +model_type = "smartcore" +speed_unit = "miles_per_hour" +grade_unit = "decimal" +energy_rate_unit = "gallons_gasoline_per_mile" +ideal_energy_rate = 0.02857143 +real_world_energy_adjustment = 1.166 ``` -This TOML section is deserialized into JSON and passed as arguments to the SpeedGradeEnergyModelBuilder which in turn loads the [SpeedGradeModelService] in this crate. -This in turn builds the [SpeedGradeModel]. +This TOML section is deserialized into JSON and passed as arguments to the EnergyModelBuilder which in turn loads the [EnergyModelService] in this crate. +This in turn builds the [EnergyTraversalModel]. ### Search -TraversalModels in this crate will add energy estimation to road network search, and will differ in their dependencies and evaluation procedures. +TraversalModels in this crate will add energy estimation to road network search, and will differ in their dependencies and evaluation procedures. -#### SpeedGradeModel +#### EnergyTraversalModel ##### Dependencies @@ -62,7 +65,7 @@ TraversalModels in this crate will add energy estimation to road network search, 2. compute travel time as `distance / speed` for this edge 3. lookup grade for this edge in table; if missing, use `0.0` 4. perform inference to retrieve energy rate from speed and grade values -5. compute energy as `energy_rate * distance * real_world_adjustment_factor` for this edge +5. compute energy as `energy_rate * distance * real_world_adjustment_factor` for this edge 6. compute link cost as `(energy * energy_cost_coefficient) + (time * (1 - energy_cost_coefficient))` ### Real-World Adjustment Factors @@ -70,13 +73,10 @@ TraversalModels in this crate will add energy estimation to road network search, In addition to calculating the energy based on a RouteE Powertrain output, an adjustment factor should be applied to capture real-world effects of running a powertrain in an environment. As a result of NREL research, some recommended values for this adjustment are: -powertrain type | factor ---- | --- -combustion vehicle (CV) | 1.1660 -hybrid vehicle (HV) | 1.1252 -electric vehicle (EV) | 1.3958 +| powertrain type | factor | +| ----------------------- | ------ | +| combustion vehicle (CV) | 1.1660 | +| hybrid vehicle (HV) | 1.1252 | +| electric vehicle (EV) | 1.3958 | A factor of 1.0 equates to 100% of the original energy value. - -[SpeedGradeModelService]: crate::routee::speed_grade_model_service::SpeedGradeModelService -[SpeedGradeModelService]: crate::routee::speed_grade_model::SpeedGradeModel \ No newline at end of file diff --git a/rust/routee-compass-powertrain/src/routee/energy_model_ops.rs b/rust/routee-compass-powertrain/src/routee/energy_model_ops.rs new file mode 100644 index 00000000..52ddc9b4 --- /dev/null +++ b/rust/routee-compass-powertrain/src/routee/energy_model_ops.rs @@ -0,0 +1,26 @@ +use routee_compass_core::{ + model::{road_network::edge_id::EdgeId, traversal::traversal_model_error::TraversalModelError}, + util::unit::Grade, +}; + +pub const ZERO_ENERGY: f64 = 1e-9; + +/// look up the grade from the grade table +pub fn get_grade( + grade_table: &Option>, + edge_id: EdgeId, +) -> Result { + match grade_table { + None => Ok(Grade::ZERO), + Some(gt) => { + let grade: &Grade = gt.get(edge_id.as_usize()).ok_or( + TraversalModelError::MissingIdInTabularCostFunction( + format!("{}", edge_id), + String::from("EdgeId"), + String::from("grade table"), + ), + )?; + Ok(*grade) + } + } +} diff --git a/rust/routee-compass-powertrain/src/routee/speed_grade_energy_model_service.rs b/rust/routee-compass-powertrain/src/routee/energy_model_service.rs similarity index 80% rename from rust/routee-compass-powertrain/src/routee/speed_grade_energy_model_service.rs rename to rust/routee-compass-powertrain/src/routee/energy_model_service.rs index eccb29d2..fce8d601 100644 --- a/rust/routee-compass-powertrain/src/routee/speed_grade_energy_model_service.rs +++ b/rust/routee-compass-powertrain/src/routee/energy_model_service.rs @@ -1,5 +1,3 @@ -use super::prediction_model::SpeedGradePredictionModelRecord; -use super::speed_grade_energy_model::SpeedGradeEnergyModel; use routee_compass_core::model::traversal::default::speed_lookup_model::get_max_speed; use routee_compass_core::model::traversal::traversal_model::TraversalModel; use routee_compass_core::model::traversal::traversal_model_error::TraversalModelError; @@ -11,8 +9,11 @@ use std::collections::HashMap; use std::path::Path; use std::sync::Arc; +use super::energy_traversal_model::EnergyTraversalModel; +use super::vehicle::VehicleType; + #[derive(Clone)] -pub struct SpeedGradeEnergyModelService { +pub struct EnergyModelService { pub speed_table: Arc>, pub speeds_table_speed_unit: SpeedUnit, pub max_speed: Speed, @@ -20,21 +21,10 @@ pub struct SpeedGradeEnergyModelService { pub grade_table_grade_unit: GradeUnit, pub output_time_unit: TimeUnit, pub output_distance_unit: DistanceUnit, - pub energy_model_library: Arc>>, -} - -impl TraversalModelService for SpeedGradeEnergyModelService { - fn build( - &self, - query: &serde_json::Value, - ) -> Result, TraversalModelError> { - let arc_self = Arc::new(self.clone()); - let m = SpeedGradeEnergyModel::try_from((arc_self, query))?; - Ok(Arc::new(m)) - } + pub vehicle_library: HashMap>, } -impl SpeedGradeEnergyModelService { +impl EnergyModelService { pub fn new>( speed_table_path: &P, speeds_table_speed_unit: SpeedUnit, @@ -42,7 +32,7 @@ impl SpeedGradeEnergyModelService { grade_table_grade_unit_option: Option, output_time_unit_option: Option, output_distance_unit_option: Option, - energy_model_library: HashMap>, + vehicle_library: HashMap>, ) -> Result { let output_time_unit = output_time_unit_option.unwrap_or(BASE_TIME_UNIT); let output_distance_unit = output_distance_unit_option.unwrap_or(BASE_DISTANCE_UNIT); @@ -74,7 +64,7 @@ impl SpeedGradeEnergyModelService { let max_speed = get_max_speed(&speed_table)?; - Ok(SpeedGradeEnergyModelService { + Ok(EnergyModelService { speed_table, speeds_table_speed_unit, max_speed, @@ -82,7 +72,18 @@ impl SpeedGradeEnergyModelService { grade_table_grade_unit, output_time_unit, output_distance_unit, - energy_model_library: Arc::new(energy_model_library), + vehicle_library, }) } } + +impl TraversalModelService for EnergyModelService { + fn build( + &self, + parameters: &serde_json::Value, + ) -> Result, TraversalModelError> { + let arc_self = Arc::new(self.clone()); + let model = EnergyTraversalModel::try_from((arc_self, parameters))?; + Ok(Arc::new(model)) + } +} diff --git a/rust/routee-compass-powertrain/src/routee/speed_grade_energy_model.rs b/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs similarity index 69% rename from rust/routee-compass-powertrain/src/routee/speed_grade_energy_model.rs rename to rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs index 52d45757..a43dc69b 100644 --- a/rust/routee-compass-powertrain/src/routee/speed_grade_energy_model.rs +++ b/rust/routee-compass-powertrain/src/routee/energy_traversal_model.rs @@ -1,9 +1,12 @@ -use super::prediction_model::SpeedGradePredictionModelRecord; -use super::speed_grade_energy_model_service::SpeedGradeEnergyModelService; +use crate::routee::energy_model_ops::ZERO_ENERGY; + +use super::energy_model_ops::get_grade; +use super::energy_model_service::EnergyModelService; +use super::vehicle::vehicle_type::{VehicleState, VehicleType}; + use routee_compass_core::model::cost::Cost; use routee_compass_core::model::property::edge::Edge; use routee_compass_core::model::property::vertex::Vertex; -use routee_compass_core::model::road_network::edge_id::EdgeId; use routee_compass_core::model::traversal::default::speed_lookup_model::get_speed; use routee_compass_core::model::traversal::state::state_variable::StateVar; use routee_compass_core::model::traversal::state::traversal_state::TraversalState; @@ -15,18 +18,22 @@ use routee_compass_core::util::unit::as_f64::AsF64; use routee_compass_core::util::unit::*; use std::sync::Arc; -const ZERO_ENERGY: f64 = 1e-9; - -pub struct SpeedGradeEnergyModel { - pub service: Arc, - pub model_record: Arc, +pub struct EnergyTraversalModel { + pub service: Arc, + pub vehicle: Arc, pub energy_cost_coefficient: f64, } -impl TraversalModel for SpeedGradeEnergyModel { +impl TraversalModel for EnergyTraversalModel { fn initial_state(&self) -> TraversalState { - // distance, time, energy - vec![StateVar(0.0), StateVar(0.0), StateVar(0.0)] + // distance, time + let mut initial_state = vec![StateVar(0.0), StateVar(0.0)]; + + // vehicle state gets slots 2..n + let vehicle_state = self.vehicle.initial_state(); + initial_state.extend(vehicle_state); + + initial_state } /// estimate the cost of traveling between two vertices. /// given a distance estimate, @@ -47,12 +54,9 @@ impl TraversalModel for SpeedGradeEnergyModel { return Ok(Cost::ZERO); } - let (energy, _energy_unit) = Energy::create( - self.model_record.ideal_energy_rate, - self.model_record.energy_rate_unit, - distance, - self.service.output_distance_unit, - )?; + let (energy, _energy_unit) = self + .vehicle + .best_case_energy((distance, self.service.output_distance_unit))?; let time: Time = Time::create( self.service.max_speed, @@ -85,29 +89,24 @@ impl TraversalModel for SpeedGradeEnergyModel { self.service.output_time_unit.clone(), )?; - let (energy_rate, _energy_rate_unit) = self.model_record.prediction_model.predict( - speed, - self.service.speeds_table_speed_unit, - grade, - self.service.grade_table_grade_unit, + let energy_result = self.vehicle.consume_energy( + (speed, self.service.speeds_table_speed_unit), + (grade, self.service.grade_table_grade_unit), + (distance, self.service.output_distance_unit), + get_vehicle_state_from_state(state), )?; - let energy_rate_real_world = energy_rate * self.model_record.real_world_energy_adjustment; - - let (mut energy, _energy_unit) = Energy::create( - energy_rate_real_world, - self.model_record.energy_rate_unit, - distance, - self.service.output_distance_unit, - )?; + let mut energy = energy_result.energy; + // for now we need to truncate the energy at zero until we can handle these being negative if energy.as_f64() < 0.0 { energy = Energy::new(ZERO_ENERGY); log::debug!("negative energy encountered, setting to 1e-9"); } let total_cost = create_cost(energy, time, self.energy_cost_coefficient); - let updated_state = update_state(state, distance, time, energy); + + let updated_state = update_state(state, distance, time, energy_result.updated_state); let result = TraversalResult { total_cost, updated_state, @@ -118,30 +117,30 @@ impl TraversalModel for SpeedGradeEnergyModel { fn serialize_state(&self, state: &TraversalState) -> serde_json::Value { let distance = get_distance_from_state(state); let time = get_time_from_state(state); - let energy = get_energy_from_state(state); + let vehicle_state = get_vehicle_state_from_state(state); + let vehicle_state_summary = self.vehicle.serialize_state(vehicle_state); serde_json::json!({ "distance": distance, "time": time, - "energy": energy, + "vehicle": vehicle_state_summary, }) } - fn serialize_state_info(&self, _state: &TraversalState) -> serde_json::Value { - let energy_unit = self.model_record.energy_rate_unit.associated_energy_unit(); + fn serialize_state_info(&self, state: &TraversalState) -> serde_json::Value { + let vehicle_state = get_vehicle_state_from_state(state); + let vehicle_state_info = self.vehicle.serialize_state_info(vehicle_state); serde_json::json!({ "distance_unit": self.service.output_distance_unit, "time_unit": self.service.output_time_unit, - "energy_unit": energy_unit, + "vehicle_info": vehicle_state_info, }) } } -impl TryFrom<(Arc, &serde_json::Value)> for SpeedGradeEnergyModel { +impl TryFrom<(Arc, &serde_json::Value)> for EnergyTraversalModel { type Error = TraversalModelError; - fn try_from( - input: (Arc, &serde_json::Value), - ) -> Result { + fn try_from(input: (Arc, &serde_json::Value)) -> Result { let (service, conf) = input; let energy_cost_coefficient = match conf.get(String::from("energy_cost_coefficient")) { @@ -162,7 +161,6 @@ impl TryFrom<(Arc, &serde_json::Value)> for SpeedG } } }; - let prediction_model_name = conf .get("model_name".to_string()) .ok_or(TraversalModelError::BuildError( @@ -174,20 +172,21 @@ impl TryFrom<(Arc, &serde_json::Value)> for SpeedG ))? .to_string(); - let model_record = match service.energy_model_library.get(&prediction_model_name) { + let vehicle = match service.vehicle_library.get(&prediction_model_name) { None => { - let model_names: Vec<&String> = service.energy_model_library.keys().collect(); - return Err(TraversalModelError::BuildError(format!( - "No energy model found with model_name = '{}', try one of: {:?}", + let model_names: Vec<&String> = service.vehicle_library.keys().collect(); + Err(TraversalModelError::BuildError(format!( + "No vehicle found with model_name = '{}', try one of: {:?}", prediction_model_name, model_names - ))); + ))) } - Some(mr) => mr.clone(), - }; + Some(mr) => Ok(mr.clone()), + }? + .update_from_query(conf)?; - Ok(SpeedGradeEnergyModel { + Ok(EnergyTraversalModel { service, - model_record, + vehicle, energy_cost_coefficient, }) } @@ -206,12 +205,13 @@ fn update_state( state: &TraversalState, distance: Distance, time: Time, - energy: Energy, + vehicle_state: VehicleState, ) -> TraversalState { - let mut updated_state = state.clone(); - updated_state[0] = state[0] + distance.into(); - updated_state[1] = state[1] + time.into(); - updated_state[2] = state[2] + energy.into(); + let mut updated_state = Vec::new(); + + updated_state.push(state[0] + distance.into()); + updated_state.push(state[1] + time.into()); + updated_state.extend(vehicle_state); updated_state } @@ -223,33 +223,16 @@ fn get_time_from_state(state: &TraversalState) -> Time { Time::new(state[1].0) } -fn get_energy_from_state(state: &TraversalState) -> Energy { - Energy::new(state[2].0) -} - -/// look up the grade from the grade table -pub fn get_grade( - grade_table: &Option>, - edge_id: EdgeId, -) -> Result { - match grade_table { - None => Ok(Grade::ZERO), - Some(gt) => { - let grade: &Grade = gt.get(edge_id.as_usize()).ok_or( - TraversalModelError::MissingIdInTabularCostFunction( - format!("{}", edge_id), - String::from("EdgeId"), - String::from("grade table"), - ), - )?; - Ok(*grade) - } - } +fn get_vehicle_state_from_state(state: &TraversalState) -> &[StateVar] { + &state[2..] } #[cfg(test)] mod tests { - use crate::routee::model_type::ModelType; + use crate::routee::{ + prediction::load_prediction_model, prediction::model_type::ModelType, + vehicle::default::single_fuel_vehicle::SingleFuelVehicle, + }; use super::*; use geo::coord; @@ -288,7 +271,7 @@ mod tests { distance: Distance::new(100.0), } } - let model_record = SpeedGradePredictionModelRecord::new( + let model_record = load_prediction_model( "Toyota_Camry".to_string(), &model_file_path, ModelType::Smartcore, @@ -299,10 +282,13 @@ mod tests { None, ) .unwrap(); - let mut model_library = HashMap::new(); - model_library.insert("Toyota_Camry".to_string(), Arc::new(model_record)); - let service = SpeedGradeEnergyModelService::new( + let camry = SingleFuelVehicle::new("Toyota_Camry".to_string(), model_record).unwrap(); + + let mut model_library: HashMap> = HashMap::new(); + model_library.insert("Toyota_Camry".to_string(), Arc::new(camry)); + + let service = EnergyModelService::new( &speed_file_path, SpeedUnit::KilometersPerHour, &Some(grade_file_path), @@ -317,7 +303,7 @@ mod tests { "model_name": "Toyota_Camry", "energy_cost_coefficient": 0.5, }); - let model = SpeedGradeEnergyModel::try_from((arc_service, &conf)).unwrap(); + let model = EnergyTraversalModel::try_from((arc_service, &conf)).unwrap(); let initial = model.initial_state(); let e1 = mock_edge(0); // 100 meters @ 10kph should take 36 seconds ((0.1/10) * 3600) diff --git a/rust/routee-compass-powertrain/src/routee/mod.rs b/rust/routee-compass-powertrain/src/routee/mod.rs index 3b546c57..e3eb32d0 100644 --- a/rust/routee-compass-powertrain/src/routee/mod.rs +++ b/rust/routee-compass-powertrain/src/routee/mod.rs @@ -1,8 +1,5 @@ -pub mod model_type; -pub mod prediction_model; -pub mod smartcore; -pub mod speed_grade_energy_model; -pub mod speed_grade_energy_model_service; - -#[cfg(feature = "onnx")] -pub mod onnx; +pub mod energy_model_ops; +pub mod energy_model_service; +pub mod energy_traversal_model; +pub mod prediction; +pub mod vehicle; diff --git a/rust/routee-compass-powertrain/src/routee/prediction/mod.rs b/rust/routee-compass-powertrain/src/routee/prediction/mod.rs new file mode 100644 index 00000000..05a8a2a4 --- /dev/null +++ b/rust/routee-compass-powertrain/src/routee/prediction/mod.rs @@ -0,0 +1,12 @@ +pub mod model_type; +pub mod prediction_model; +pub mod prediction_model_ops; +pub mod prediction_model_record; +pub mod smartcore; + +#[cfg(feature = "onnx")] +pub mod onnx; + +pub use prediction_model::PredictionModel; +pub use prediction_model_ops::load_prediction_model; +pub use prediction_model_record::PredictionModelRecord; diff --git a/rust/routee-compass-powertrain/src/routee/model_type.rs b/rust/routee-compass-powertrain/src/routee/prediction/model_type.rs similarity index 91% rename from rust/routee-compass-powertrain/src/routee/model_type.rs rename to rust/routee-compass-powertrain/src/routee/prediction/model_type.rs index 1799c9f8..6d2bc210 100644 --- a/rust/routee-compass-powertrain/src/routee/model_type.rs +++ b/rust/routee-compass-powertrain/src/routee/prediction/model_type.rs @@ -1,5 +1,5 @@ use super::{ - prediction_model::SpeedGradePredictionModel, + prediction_model::PredictionModel, smartcore::smartcore_speed_grade_model::SmartcoreSpeedGradeModel, }; use routee_compass_core::{ @@ -34,9 +34,9 @@ impl ModelType { energy_model_speed_unit: SpeedUnit, energy_model_grade_unit: GradeUnit, energy_model_energy_rate_unit: EnergyRateUnit, - ) -> Result, TraversalModelError> { + ) -> Result, TraversalModelError> { // Load random forest binary file - let model: Arc = match self { + let model: Arc = match self { ModelType::Smartcore => Arc::new(SmartcoreSpeedGradeModel::new( energy_model_path.clone(), energy_model_speed_unit, diff --git a/rust/routee-compass-powertrain/src/routee/onnx/mod.rs b/rust/routee-compass-powertrain/src/routee/prediction/onnx/mod.rs similarity index 100% rename from rust/routee-compass-powertrain/src/routee/onnx/mod.rs rename to rust/routee-compass-powertrain/src/routee/prediction/onnx/mod.rs diff --git a/rust/routee-compass-powertrain/src/routee/onnx/onnx_speed_grade_model.rs b/rust/routee-compass-powertrain/src/routee/prediction/onnx/onnx_speed_grade_model.rs similarity index 87% rename from rust/routee-compass-powertrain/src/routee/onnx/onnx_speed_grade_model.rs rename to rust/routee-compass-powertrain/src/routee/prediction/onnx/onnx_speed_grade_model.rs index c155d925..9025914d 100644 --- a/rust/routee-compass-powertrain/src/routee/onnx/onnx_speed_grade_model.rs +++ b/rust/routee-compass-powertrain/src/routee/prediction/onnx/onnx_speed_grade_model.rs @@ -1,6 +1,6 @@ use std::path::Path; -use crate::routee::prediction_model::SpeedGradePredictionModel; +use crate::routee::prediction::prediction_model::PredictionModel; use ndarray::CowArray; use ort::{ tensor::OrtOwnedTensor, Environment, GraphOptimizationLevel, Session, SessionBuilder, Value, @@ -17,14 +17,14 @@ pub struct OnnxSpeedGradeModel { energy_rate_unit: EnergyRateUnit, } -impl SpeedGradePredictionModel for OnnxSpeedGradeModel { +impl PredictionModel for OnnxSpeedGradeModel { fn predict( &self, - speed: Speed, - speed_unit: SpeedUnit, - grade: Grade, - grade_unit: GradeUnit, + speed: (Speed, SpeedUnit), + grade: (Grade, GradeUnit), ) -> Result<(EnergyRate, EnergyRateUnit), TraversalModelError> { + let (speed, speed_unit) = speed; + let (grade, grade_unit) = grade; let speed_value: f32 = speed_unit.convert(speed, self.speed_unit).as_f64() as f32; let grade_value: f32 = grade_unit.convert(grade, self.grade_unit).as_f64() as f32; let array = ndarray::Array1::from(vec![speed_value, grade_value]) @@ -89,8 +89,7 @@ mod test { use std::path::PathBuf; use crate::routee::{ - onnx::onnx_speed_grade_model::OnnxSpeedGradeModel, - prediction_model::SpeedGradePredictionModel, + prediction::onnx::onnx_speed_grade_model::OnnxSpeedGradeModel, prediction::PredictionModel, }; use rayon::prelude::*; use routee_compass_core::{ @@ -106,13 +105,10 @@ mod test { .join("src") .join("routee") .join("test") - .join("Toyota_Camry.onnx") - .to_str() - .unwrap() - .into(); - let model: Box = Box::new( + .join("Toyota_Camry.onnx"); + let model: Box = Box::new( OnnxSpeedGradeModel::new( - model_file_path, + &model_file_path, SpeedUnit::MilesPerHour, GradeUnit::Decimal, routee_compass_core::util::unit::EnergyRateUnit::GallonsGasolinePerMile, @@ -132,7 +128,7 @@ mod test { let results = inputs .par_iter() .map(|(speed, speed_unit, grade, grade_unit)| { - model.predict(*speed, *speed_unit, *grade, *grade_unit) + model.predict((*speed, *speed_unit), (*grade, *grade_unit)) }) .collect::>>(); @@ -141,7 +137,10 @@ mod test { // assert that all the results are the same let (expected_er, expected_eru) = model - .predict(input_speed, input_speed_unit, input_grade, input_grade_unit) + .predict( + (input_speed, input_speed_unit), + (input_grade, input_grade_unit), + ) .unwrap(); assert!(results.iter().all(|r| match r { Err(e) => panic!("{}", e), diff --git a/rust/routee-compass-powertrain/src/routee/prediction/prediction_model.rs b/rust/routee-compass-powertrain/src/routee/prediction/prediction_model.rs new file mode 100644 index 00000000..166365cc --- /dev/null +++ b/rust/routee-compass-powertrain/src/routee/prediction/prediction_model.rs @@ -0,0 +1,12 @@ +use routee_compass_core::{ + model::traversal::traversal_model_error::TraversalModelError, + util::unit::{EnergyRate, EnergyRateUnit, Grade, GradeUnit, Speed, SpeedUnit}, +}; + +pub trait PredictionModel: Send + Sync { + fn predict( + &self, + speed: (Speed, SpeedUnit), + grade: (Grade, GradeUnit), + ) -> Result<(EnergyRate, EnergyRateUnit), TraversalModelError>; +} diff --git a/rust/routee-compass-powertrain/src/routee/prediction/prediction_model_ops.rs b/rust/routee-compass-powertrain/src/routee/prediction/prediction_model_ops.rs new file mode 100644 index 00000000..f825f724 --- /dev/null +++ b/rust/routee-compass-powertrain/src/routee/prediction/prediction_model_ops.rs @@ -0,0 +1,106 @@ +use std::{path::Path, sync::Arc}; + +use routee_compass_core::{ + model::traversal::traversal_model_error::TraversalModelError, + util::unit::{EnergyRate, EnergyRateUnit, Grade, GradeUnit, Speed, SpeedUnit}, +}; + +use super::{ + model_type::ModelType, smartcore::smartcore_speed_grade_model::SmartcoreSpeedGradeModel, + PredictionModel, PredictionModelRecord, +}; + +#[cfg(feature = "onnx")] +use crate::routee::prediction::onnx::onnx_speed_grade_model::OnnxSpeedGradeModel; + +#[allow(clippy::too_many_arguments)] +pub fn load_prediction_model>( + model_name: String, + model_path: &P, + model_type: ModelType, + speed_unit: SpeedUnit, + grade_unit: GradeUnit, + energy_rate_unit: EnergyRateUnit, + ideal_energy_rate_option: Option, + real_world_energy_adjustment_option: Option, +) -> Result { + let prediction_model: Arc = match model_type { + ModelType::Smartcore => { + let model = SmartcoreSpeedGradeModel::new( + model_path, + speed_unit, + grade_unit, + energy_rate_unit, + )?; + Arc::new(model) + } + ModelType::Onnx => { + #[cfg(feature = "onnx")] + { + let model = + OnnxSpeedGradeModel::new(model_path, speed_unit, grade_unit, energy_rate_unit)?; + Arc::new(model) + } + #[cfg(not(feature = "onnx"))] + { + return Err(TraversalModelError::BuildError( + "Cannot build Onnx model without `onnx` feature enabled for compass-powertrain" + .to_string(), + )); + } + } + }; + let ideal_energy_rate = match ideal_energy_rate_option { + None => find_min_energy_rate(&prediction_model, &energy_rate_unit)?, + Some(ier) => ier, + }; + + let real_world_energy_adjustment = real_world_energy_adjustment_option.unwrap_or(1.0); + + Ok(PredictionModelRecord { + name: model_name, + prediction_model, + model_type, + speed_unit, + grade_unit, + energy_rate_unit, + ideal_energy_rate, + real_world_energy_adjustment, + }) +} + +/// sweep a fixed set of speed and grade values to find the minimum energy per mile rate from the incoming rf model +pub fn find_min_energy_rate( + model: &Arc, + energy_model_energy_rate_unit: &EnergyRateUnit, +) -> Result { + // sweep a fixed set of speed and grade values to find the minimum energy per mile rate from the incoming rf model + let mut minimum_energy_rate = EnergyRate::new(f64::MAX); + let start_time = std::time::Instant::now(); + + let grade = Grade::ZERO; + for speed_i32 in 20..80 { + let speed = Speed::new(speed_i32 as f64); + let (energy_rate, _) = model + .predict( + (speed, SpeedUnit::MilesPerHour), + (grade, GradeUnit::Percent), + ) + .map_err(|e| TraversalModelError::PredictionModel(e.to_string()))?; + if energy_rate < minimum_energy_rate { + minimum_energy_rate = energy_rate; + } + } + + let end_time = std::time::Instant::now(); + let search_time = end_time - start_time; + + log::debug!( + "found minimum energy: {}/{} in {} milliseconds", + minimum_energy_rate, + energy_model_energy_rate_unit, + search_time.as_millis() + ); + + Ok(minimum_energy_rate) +} diff --git a/rust/routee-compass-powertrain/src/routee/prediction/prediction_model_record.rs b/rust/routee-compass-powertrain/src/routee/prediction/prediction_model_record.rs new file mode 100644 index 00000000..1e97490b --- /dev/null +++ b/rust/routee-compass-powertrain/src/routee/prediction/prediction_model_record.rs @@ -0,0 +1,44 @@ +use std::sync::Arc; + +use routee_compass_core::{ + model::traversal::traversal_model_error::TraversalModelError, + util::unit::{ + Distance, DistanceUnit, Energy, EnergyRate, EnergyRateUnit, EnergyUnit, Grade, GradeUnit, + Speed, SpeedUnit, + }, +}; + +use super::{model_type::ModelType, PredictionModel}; +/// A struct to hold the prediction model and associated metadata +pub struct PredictionModelRecord { + pub name: String, + pub prediction_model: Arc, + pub model_type: ModelType, + pub speed_unit: SpeedUnit, + pub grade_unit: GradeUnit, + pub energy_rate_unit: EnergyRateUnit, + pub ideal_energy_rate: EnergyRate, + pub real_world_energy_adjustment: f64, +} + +impl PredictionModelRecord { + pub fn predict( + &self, + speed: (Speed, SpeedUnit), + grade: (Grade, GradeUnit), + distance: (Distance, DistanceUnit), + ) -> Result<(Energy, EnergyUnit), TraversalModelError> { + let (distance, distance_unit) = distance; + let (energy_rate, _energy_rate_unit) = self.prediction_model.predict(speed, grade)?; + + let energy_rate_real_world = energy_rate * self.real_world_energy_adjustment; + + let (energy, energy_unit) = Energy::create( + energy_rate_real_world, + self.energy_rate_unit, + distance, + distance_unit, + )?; + Ok((energy, energy_unit)) + } +} diff --git a/rust/routee-compass-powertrain/src/routee/smartcore/mod.rs b/rust/routee-compass-powertrain/src/routee/prediction/smartcore/mod.rs similarity index 100% rename from rust/routee-compass-powertrain/src/routee/smartcore/mod.rs rename to rust/routee-compass-powertrain/src/routee/prediction/smartcore/mod.rs diff --git a/rust/routee-compass-powertrain/src/routee/smartcore/smartcore_speed_grade_model.rs b/rust/routee-compass-powertrain/src/routee/prediction/smartcore/smartcore_speed_grade_model.rs similarity index 88% rename from rust/routee-compass-powertrain/src/routee/smartcore/smartcore_speed_grade_model.rs rename to rust/routee-compass-powertrain/src/routee/prediction/smartcore/smartcore_speed_grade_model.rs index 6ce8d916..dede8ef4 100644 --- a/rust/routee-compass-powertrain/src/routee/smartcore/smartcore_speed_grade_model.rs +++ b/rust/routee-compass-powertrain/src/routee/prediction/smartcore/smartcore_speed_grade_model.rs @@ -1,6 +1,6 @@ use std::path::Path; -use crate::routee::prediction_model::SpeedGradePredictionModel; +use crate::routee::prediction::prediction_model::PredictionModel; use routee_compass_core::{ model::traversal::traversal_model_error::TraversalModelError, util::unit::{as_f64::AsF64, EnergyRate, EnergyRateUnit, Grade, GradeUnit, Speed, SpeedUnit}, @@ -16,14 +16,14 @@ pub struct SmartcoreSpeedGradeModel { energy_rate_unit: EnergyRateUnit, } -impl SpeedGradePredictionModel for SmartcoreSpeedGradeModel { +impl PredictionModel for SmartcoreSpeedGradeModel { fn predict( &self, - speed: Speed, - speed_unit: SpeedUnit, - grade: Grade, - grade_unit: GradeUnit, + speed: (Speed, SpeedUnit), + grade: (Grade, GradeUnit), ) -> Result<(EnergyRate, EnergyRateUnit), TraversalModelError> { + let (speed, speed_unit) = speed; + let (grade, grade_unit) = grade; let speed_value = speed_unit.convert(speed, self.speed_unit).as_f64(); let grade_value = grade_unit.convert(grade, self.grade_unit).as_f64(); let x = DenseMatrix::from_2d_vec(&vec![vec![speed_value, grade_value]]); diff --git a/rust/routee-compass-powertrain/src/routee/prediction_model.rs b/rust/routee-compass-powertrain/src/routee/prediction_model.rs deleted file mode 100644 index dcc7f18e..00000000 --- a/rust/routee-compass-powertrain/src/routee/prediction_model.rs +++ /dev/null @@ -1,132 +0,0 @@ -use std::{path::Path, sync::Arc}; - -use routee_compass_core::{ - model::traversal::traversal_model_error::TraversalModelError, - util::unit::{EnergyRate, EnergyRateUnit, Grade, GradeUnit, Speed, SpeedUnit}, -}; - -use super::{ - model_type::ModelType, smartcore::smartcore_speed_grade_model::SmartcoreSpeedGradeModel, -}; - -#[cfg(feature = "onnx")] -use crate::routee::onnx::onnx_speed_grade_model::OnnxSpeedGradeModel; - -pub struct SpeedGradePredictionModelRecord { - pub name: String, - pub prediction_model: Arc, - pub model_type: ModelType, - pub speed_unit: SpeedUnit, - pub grade_unit: GradeUnit, - pub energy_rate_unit: EnergyRateUnit, - pub ideal_energy_rate: EnergyRate, - pub real_world_energy_adjustment: f64, -} - -impl SpeedGradePredictionModelRecord { - #[allow(clippy::too_many_arguments)] - pub fn new>( - name: String, - model_path: &P, - model_type: ModelType, - speed_unit: SpeedUnit, - grade_unit: GradeUnit, - energy_rate_unit: EnergyRateUnit, - ideal_energy_rate_option: Option, - real_world_energy_adjustment_option: Option, - ) -> Result { - // Load random forest binary file - let prediction_model: Arc = match model_type { - ModelType::Smartcore => { - let model = SmartcoreSpeedGradeModel::new( - model_path, - speed_unit, - grade_unit, - energy_rate_unit, - )?; - Arc::new(model) - } - ModelType::Onnx => { - #[cfg(feature = "onnx")] - { - let model = OnnxSpeedGradeModel::new( - model_path, - speed_unit, - grade_unit, - energy_rate_unit, - )?; - Arc::new(model) - } - #[cfg(not(feature = "onnx"))] - { - return Err( - TraversalModelError::BuildError( - "Cannot build Onnx model without `onnx` feature enabled for compass-powertrain" - .to_string(), - ) - ); - } - } - }; - let ideal_energy_rate = match ideal_energy_rate_option { - None => find_min_energy_rate(&prediction_model, &energy_rate_unit)?, - Some(ier) => ier, - }; - - let real_world_energy_adjustment = real_world_energy_adjustment_option.unwrap_or(1.0); - - Ok(Self { - name, - prediction_model, - model_type, - speed_unit, - grade_unit, - energy_rate_unit, - ideal_energy_rate, - real_world_energy_adjustment, - }) - } -} - -pub trait SpeedGradePredictionModel: Send + Sync { - fn predict( - &self, - speed: Speed, - speed_unit: SpeedUnit, - grade: Grade, - grade_unit: GradeUnit, - ) -> Result<(EnergyRate, EnergyRateUnit), TraversalModelError>; -} - -/// sweep a fixed set of speed and grade values to find the minimum energy per mile rate from the incoming rf model -pub fn find_min_energy_rate( - model: &Arc, - energy_model_energy_rate_unit: &EnergyRateUnit, -) -> Result { - // sweep a fixed set of speed and grade values to find the minimum energy per mile rate from the incoming rf model - let mut minimum_energy_rate = EnergyRate::new(f64::MAX); - let start_time = std::time::Instant::now(); - - let grade = Grade::ZERO; - for speed_i32 in 20..80 { - let speed = Speed::new(speed_i32 as f64); - let (energy_rate, _) = model - .predict(speed, SpeedUnit::MilesPerHour, grade, GradeUnit::Percent) - .map_err(|e| TraversalModelError::PredictionModel(e.to_string()))?; - if energy_rate < minimum_energy_rate { - minimum_energy_rate = energy_rate; - } - } - - let end_time = std::time::Instant::now(); - let search_time = end_time - start_time; - - log::debug!( - "found minimum energy: {}/{} in {} milliseconds", - minimum_energy_rate, - energy_model_energy_rate_unit, - search_time.as_millis() - ); - - Ok(minimum_energy_rate) -} diff --git a/rust/routee-compass-powertrain/src/routee/test/2016_CHEVROLET_Volt_Charge_Depleting.bin b/rust/routee-compass-powertrain/src/routee/test/2016_CHEVROLET_Volt_Charge_Depleting.bin new file mode 100644 index 00000000..32927154 Binary files /dev/null and b/rust/routee-compass-powertrain/src/routee/test/2016_CHEVROLET_Volt_Charge_Depleting.bin differ diff --git a/rust/routee-compass-powertrain/src/routee/test/2016_CHEVROLET_Volt_Charge_Sustaining.bin b/rust/routee-compass-powertrain/src/routee/test/2016_CHEVROLET_Volt_Charge_Sustaining.bin new file mode 100644 index 00000000..5745af3d Binary files /dev/null and b/rust/routee-compass-powertrain/src/routee/test/2016_CHEVROLET_Volt_Charge_Sustaining.bin differ diff --git a/rust/routee-compass-powertrain/src/routee/vehicle/default/dual_fuel_vehicle.rs b/rust/routee-compass-powertrain/src/routee/vehicle/default/dual_fuel_vehicle.rs new file mode 100644 index 00000000..33b41057 --- /dev/null +++ b/rust/routee-compass-powertrain/src/routee/vehicle/default/dual_fuel_vehicle.rs @@ -0,0 +1,365 @@ +use std::sync::Arc; + +use routee_compass_core::{ + model::traversal::{ + state::{state_variable::StateVar, traversal_state::TraversalState}, + traversal_model_error::TraversalModelError, + }, + util::unit::{ + as_f64::AsF64, Distance, DistanceUnit, Energy, EnergyUnit, Grade, GradeUnit, Speed, + SpeedUnit, + }, +}; + +use crate::routee::{ + prediction::PredictionModelRecord, + vehicle::{vehicle_type::VehicleType, VehicleEnergyResult}, +}; + +pub struct DualFuelVehicle { + pub name: String, + pub charge_sustain_model: Arc, + pub charge_depleting_model: Arc, + pub battery_capacity: Energy, + pub starting_battery_energy: Energy, + pub battery_energy_unit: EnergyUnit, +} + +impl DualFuelVehicle { + pub fn new( + name: String, + charge_sustain_model: PredictionModelRecord, + charge_depleting_model: PredictionModelRecord, + battery_capacity: Energy, + starting_battery_energy: Energy, + battery_energy_unit: EnergyUnit, + ) -> Result { + Ok(Self { + name, + charge_sustain_model: Arc::new(charge_sustain_model), + charge_depleting_model: Arc::new(charge_depleting_model), + battery_capacity, + starting_battery_energy, + battery_energy_unit, + }) + } +} + +impl VehicleType for DualFuelVehicle { + fn name(&self) -> String { + self.name.clone() + } + fn initial_state(&self) -> TraversalState { + vec![ + StateVar(0.0), // accumulated electrical energy + StateVar(0.0), // accumulated gasoline energy + StateVar(self.starting_battery_energy.as_f64()), // battery energy remaining + ] + } + fn best_case_energy( + &self, + distance: (Distance, DistanceUnit), + ) -> Result<(Energy, EnergyUnit), TraversalModelError> { + let (distance, distance_unit) = distance; + + // assume lowest energy cost scenario for a PHEV is to just use the battery + let energy = Energy::create( + self.charge_depleting_model.ideal_energy_rate, + self.charge_depleting_model.energy_rate_unit, + distance, + distance_unit, + )?; + Ok(energy) + } + fn consume_energy( + &self, + speed: (Speed, SpeedUnit), + grade: (Grade, GradeUnit), + distance: (Distance, DistanceUnit), + state: &[StateVar], + ) -> Result { + let battery_soc_percentage = get_battery_soc_percent(self, state); + + let (electrical_energy, electrical_energy_unit, gasoline_energy, gasoline_energy_unit) = + get_phev_energy(self, battery_soc_percentage, speed, grade, distance)?; + + // convert both energy sources to kWh + let electrical_energy_kwh = + electrical_energy_unit.convert(electrical_energy, EnergyUnit::KilowattHours); + let gasoline_energy_kwh = + gasoline_energy_unit.convert(gasoline_energy, EnergyUnit::KilowattHours); + let total_energy_kwh = electrical_energy_kwh + gasoline_energy_kwh; + + let updated_state = update_state(state, electrical_energy, gasoline_energy); + + Ok(VehicleEnergyResult { + energy: total_energy_kwh, + energy_unit: EnergyUnit::KilowattHours, + updated_state, + }) + } + fn serialize_state(&self, state: &[StateVar]) -> serde_json::Value { + let battery_energy = get_electrical_energy_from_state(state); + let gasoline_energy = get_gasoline_energy_from_state(state); + let battery_soc_percent = get_battery_soc_percent(self, state); + serde_json::json!({ + "battery_energy": battery_energy.as_f64(), + "fuel_energy": gasoline_energy.as_f64(), + "battery_soc_percent": battery_soc_percent, + }) + } + + fn serialize_state_info(&self, _state: &[StateVar]) -> serde_json::Value { + let battery_energy_unit = self.battery_energy_unit; + let fuel_energy_unit = self + .charge_sustain_model + .energy_rate_unit + .associated_energy_unit(); + serde_json::json!({ + "battery_energy_unit": battery_energy_unit.to_string(), + "fuel_energy_unit": fuel_energy_unit.to_string(), + }) + } + + fn update_from_query( + &self, + query: &serde_json::Value, + ) -> Result, TraversalModelError> { + let starting_soc_percent = query + .get("starting_soc_percent".to_string()) + .ok_or(TraversalModelError::BuildError( + "No 'starting_soc_percent' key provided in query".to_string(), + ))? + .as_f64() + .ok_or(TraversalModelError::BuildError( + "Expected 'starting_soc_percent' value to be numeric".to_string(), + ))?; + if !(0.0..=100.0).contains(&starting_soc_percent) { + return Err(TraversalModelError::BuildError( + "Expected 'starting_soc_percent' value to be between 0 and 100".to_string(), + )); + } + let starting_battery_energy = self.battery_capacity * (starting_soc_percent / 100.0); + + let new_phev = DualFuelVehicle { + name: self.name.clone(), + charge_sustain_model: self.charge_sustain_model.clone(), + charge_depleting_model: self.charge_depleting_model.clone(), + battery_capacity: self.battery_capacity, + starting_battery_energy, + battery_energy_unit: self.battery_energy_unit, + }; + + Ok(Arc::new(new_phev)) + } +} + +fn update_state( + state: &[StateVar], + electrical_energy: Energy, + gasoline_energy: Energy, +) -> TraversalState { + let mut updated_state = Vec::with_capacity(state.len()); + + // accumulated electrical energy + updated_state.push(state[0] + electrical_energy.into()); + + // accumulated fuel energy + updated_state.push(state[1] + gasoline_energy.into()); + + // remaining battery energy + let current_battery_energy = get_remaining_battery_energy_from_state(state); + let new_battery_energy = (current_battery_energy - electrical_energy).max(Energy::new(0.0)); + updated_state.push(new_battery_energy.into()); + + updated_state +} +fn get_electrical_energy_from_state(state: &[StateVar]) -> Energy { + Energy::new(state[0].0) +} + +fn get_gasoline_energy_from_state(state: &[StateVar]) -> Energy { + Energy::new(state[1].0) +} + +fn get_remaining_battery_energy_from_state(state: &[StateVar]) -> Energy { + Energy::new(state[2].0) +} + +fn get_battery_soc_percent(vehicle: &DualFuelVehicle, state: &[StateVar]) -> f64 { + let battery_energy_unit = vehicle.battery_energy_unit; + + let battery_capacity_kwh = + battery_energy_unit.convert(vehicle.battery_capacity, EnergyUnit::KilowattHours); + + let remaining_battery_energy = get_remaining_battery_energy_from_state(state); + + let remaining_battery_energy_kwh = + battery_energy_unit.convert(remaining_battery_energy, EnergyUnit::KilowattHours); + + (remaining_battery_energy_kwh.as_f64() / battery_capacity_kwh.as_f64()) * 100.0 +} + +/// Compute the energy for the PHEV by converting gasoline to kWh. +/// This uses a simplified operation in which we assume that if the battery +/// SOC is greater than zero we can just operate on battery to traverse a link. +/// This is not entirely realistic as it's possible to arrive at a link with +/// 0.001% SOC and still need to use gasoline to traverse the link. +/// +/// In the future we could make this more sophisticated by calculating +/// the energy required to traverse the link using the battery and then +/// finding the point at which we would have to switch to gasoline +/// +/// Returns a tuple of (electrical_energy, electrical_energy_unit, gasoline_energy, gasoline_energy_unit) +fn get_phev_energy( + vehicle: &DualFuelVehicle, + battery_soc_percent: f64, + speed: (Speed, SpeedUnit), + grade: (Grade, GradeUnit), + distance: (Distance, DistanceUnit), +) -> Result<(Energy, EnergyUnit, Energy, EnergyUnit), TraversalModelError> { + let electrical_energy_unit = vehicle + .charge_depleting_model + .energy_rate_unit + .associated_energy_unit(); + let gasoline_energy_unit = vehicle + .charge_sustain_model + .energy_rate_unit + .associated_energy_unit(); + + if battery_soc_percent > 0.0 { + // assume we can just use the battery + let (electrical_energy, electrical_energy_unit) = vehicle + .charge_depleting_model + .predict(speed, grade, distance)?; + Ok(( + electrical_energy, + electrical_energy_unit, + Energy::new(0.0), + gasoline_energy_unit, + )) + } else { + // just use the gasoline engine + let (gasoline_energy, gasoline_energy_unit) = vehicle + .charge_sustain_model + .predict(speed, grade, distance)?; + Ok(( + Energy::new(0.0), + electrical_energy_unit, + gasoline_energy, + gasoline_energy_unit, + )) + } +} + +#[cfg(test)] +mod tests { + use routee_compass_core::util::unit::{EnergyRate, EnergyRateUnit}; + + use crate::routee::{prediction::load_prediction_model, prediction::model_type::ModelType}; + + use super::*; + + use std::path::PathBuf; + + fn mock_vehicle() -> DualFuelVehicle { + let charge_sustain_model_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("src") + .join("routee") + .join("test") + .join("2016_CHEVROLET_Volt_Charge_Sustaining.bin"); + let charge_depleting_model_file_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("src") + .join("routee") + .join("test") + .join("2016_CHEVROLET_Volt_Charge_Depleting.bin"); + + let charge_sustain_model_record = load_prediction_model( + "Chevy_Volt_Charge_Sustaining".to_string(), + &charge_sustain_model_file_path, + ModelType::Smartcore, + SpeedUnit::MilesPerHour, + GradeUnit::Decimal, + EnergyRateUnit::GallonsGasolinePerMile, + Some(EnergyRate::new(0.02)), + Some(1.1252), + ) + .unwrap(); + let charge_depleting_model_record = load_prediction_model( + "Chevy_Volt_Charge_Depleting".to_string(), + &charge_depleting_model_file_path, + ModelType::Smartcore, + SpeedUnit::MilesPerHour, + GradeUnit::Decimal, + EnergyRateUnit::KilowattHoursPerMile, + Some(EnergyRate::new(0.2)), + Some(1.3958), + ) + .unwrap(); + + DualFuelVehicle::new( + "Chevy_Volt".to_string(), + charge_sustain_model_record, + charge_depleting_model_record, + Energy::new(12.0), + Energy::new(12.0), + EnergyUnit::KilowattHours, + ) + .unwrap() + } + + #[test] + fn test_phev_energy_model_just_electric() { + let vehicle = mock_vehicle(); + let initial = vehicle.initial_state(); + + // starting at 100% SOC, we should be able to traverse 1000 meters + // without using any gasoline + let distance = (Distance::new(1000.0), DistanceUnit::Meters); + let speed = (Speed::new(60.0), SpeedUnit::MilesPerHour); + let grade = (Grade::new(0.0), GradeUnit::Decimal); + + let result = vehicle + .consume_energy(speed, grade, distance, &initial) + .unwrap(); + + let gasoline_energy = get_gasoline_energy_from_state(&result.updated_state); + assert!(gasoline_energy.as_f64() < 1e-9); + + let electrical_energy = get_electrical_energy_from_state(&result.updated_state); + assert!(electrical_energy.as_f64() > 0.0); + + let battery_percent_soc = get_battery_soc_percent(&vehicle, &result.updated_state); + assert!(battery_percent_soc < 100.0); + } + + #[test] + fn test_phev_energy_model_gas_and_electric() { + let vehicle = mock_vehicle(); + let initial = vehicle.initial_state(); + + // now let's traverse a really long link to deplete the battery + let distance = (Distance::new(100.0), DistanceUnit::Miles); + let speed = (Speed::new(60.0), SpeedUnit::MilesPerHour); + let grade = (Grade::new(0.0), GradeUnit::Decimal); + + let result = vehicle + .consume_energy(speed, grade, distance, &initial) + .unwrap(); + + let electrical_energy = get_electrical_energy_from_state(&result.updated_state); + let battery_percent_soc = get_battery_soc_percent(&vehicle, &result.updated_state); + + assert!(electrical_energy.as_f64() > 0.0); + assert!(battery_percent_soc < 1e-9); + + // and then traverse the same distance but this time we should only use gasoline energy + let result2 = vehicle + .consume_energy(speed, grade, distance, &result.updated_state) + .unwrap(); + + let gasoline_energy = get_gasoline_energy_from_state(&result2.updated_state); + + assert!(gasoline_energy.as_f64() > 0.0); + } +} diff --git a/rust/routee-compass-powertrain/src/routee/vehicle/default/mod.rs b/rust/routee-compass-powertrain/src/routee/vehicle/default/mod.rs new file mode 100644 index 00000000..daa93c3e --- /dev/null +++ b/rust/routee-compass-powertrain/src/routee/vehicle/default/mod.rs @@ -0,0 +1,2 @@ +pub mod dual_fuel_vehicle; +pub mod single_fuel_vehicle; diff --git a/rust/routee-compass-powertrain/src/routee/vehicle/default/single_fuel_vehicle.rs b/rust/routee-compass-powertrain/src/routee/vehicle/default/single_fuel_vehicle.rs new file mode 100644 index 00000000..d270e8a7 --- /dev/null +++ b/rust/routee-compass-powertrain/src/routee/vehicle/default/single_fuel_vehicle.rs @@ -0,0 +1,113 @@ +use std::sync::Arc; + +use routee_compass_core::{ + model::traversal::{ + state::state_variable::StateVar, traversal_model_error::TraversalModelError, + }, + util::unit::{ + as_f64::AsF64, Distance, DistanceUnit, Energy, EnergyUnit, Grade, GradeUnit, Speed, + SpeedUnit, + }, +}; + +use crate::routee::{ + prediction::PredictionModelRecord, + vehicle::{VehicleEnergyResult, VehicleState, VehicleType}, +}; + +pub struct SingleFuelVehicle { + pub name: String, + pub prediction_model_record: Arc, +} + +impl SingleFuelVehicle { + pub fn new( + name: String, + prediction_model_record: PredictionModelRecord, + ) -> Result { + Ok(Self { + name, + prediction_model_record: Arc::new(prediction_model_record), + }) + } +} + +impl VehicleType for SingleFuelVehicle { + fn name(&self) -> String { + self.name.clone() + } + fn initial_state(&self) -> VehicleState { + // accumulated energy + vec![StateVar(0.0)] + } + fn best_case_energy( + &self, + distance: (Distance, DistanceUnit), + ) -> Result<(Energy, EnergyUnit), TraversalModelError> { + let (distance, distance_unit) = distance; + let energy = Energy::create( + self.prediction_model_record.ideal_energy_rate, + self.prediction_model_record.energy_rate_unit, + distance, + distance_unit, + )?; + Ok(energy) + } + fn consume_energy( + &self, + speed: (Speed, SpeedUnit), + grade: (Grade, GradeUnit), + distance: (Distance, DistanceUnit), + state: &[StateVar], + ) -> Result { + let (energy, energy_unit) = self + .prediction_model_record + .predict(speed, grade, distance)?; + + let updated_state = update_state(state, energy); + + Ok(VehicleEnergyResult { + energy, + energy_unit, + updated_state, + }) + } + fn serialize_state(&self, state: &[StateVar]) -> serde_json::Value { + let energy = get_energy_from_state(state); + serde_json::json!({ + "energy": energy.as_f64(), + }) + } + + fn serialize_state_info(&self, _state: &[StateVar]) -> serde_json::Value { + let energy_unit = self + .prediction_model_record + .energy_rate_unit + .associated_energy_unit(); + serde_json::json!({ + "energy_unit": energy_unit.to_string(), + }) + } + + fn update_from_query( + &self, + _query: &serde_json::Value, + ) -> Result, TraversalModelError> { + // just return a clone of self + Ok(Arc::new(SingleFuelVehicle { + name: self.name.clone(), + prediction_model_record: self.prediction_model_record.clone(), + })) + } +} + +fn update_state(state: &[StateVar], energy: Energy) -> VehicleState { + let mut new_state = Vec::with_capacity(state.len()); + new_state.push(state[0] + energy.into()); + new_state +} + +fn get_energy_from_state(state: &[StateVar]) -> Energy { + let energy = state[0].0; + Energy::new(energy) +} diff --git a/rust/routee-compass-powertrain/src/routee/vehicle/mod.rs b/rust/routee-compass-powertrain/src/routee/vehicle/mod.rs new file mode 100644 index 00000000..ef315eec --- /dev/null +++ b/rust/routee-compass-powertrain/src/routee/vehicle/mod.rs @@ -0,0 +1,6 @@ +pub mod default; +pub mod vehicle_energy_result; +pub mod vehicle_type; + +pub use vehicle_energy_result::VehicleEnergyResult; +pub use vehicle_type::{VehicleState, VehicleType}; diff --git a/rust/routee-compass-powertrain/src/routee/vehicle/vehicle_energy_result.rs b/rust/routee-compass-powertrain/src/routee/vehicle/vehicle_energy_result.rs new file mode 100644 index 00000000..f4fcb9f1 --- /dev/null +++ b/rust/routee-compass-powertrain/src/routee/vehicle/vehicle_energy_result.rs @@ -0,0 +1,9 @@ +use routee_compass_core::util::unit::{Energy, EnergyUnit}; + +use super::VehicleState; + +pub struct VehicleEnergyResult { + pub energy: Energy, + pub energy_unit: EnergyUnit, + pub updated_state: VehicleState, +} diff --git a/rust/routee-compass-powertrain/src/routee/vehicle/vehicle_type.rs b/rust/routee-compass-powertrain/src/routee/vehicle/vehicle_type.rs new file mode 100644 index 00000000..4d2ec107 --- /dev/null +++ b/rust/routee-compass-powertrain/src/routee/vehicle/vehicle_type.rs @@ -0,0 +1,82 @@ +use std::sync::Arc; + +use routee_compass_core::{ + model::traversal::{ + state::state_variable::StateVar, traversal_model_error::TraversalModelError, + }, + util::unit::{Distance, DistanceUnit, Energy, EnergyUnit, Grade, GradeUnit, Speed, SpeedUnit}, +}; + +use super::VehicleEnergyResult; + +pub type VehicleState = Vec; + +/// A Vehicle Type represents a class of vehicles with a specific operating model. +pub trait VehicleType: Send + Sync { + /// Return the name of the vehicle type + fn name(&self) -> String; + + /// Return the energy required to travel a certain distance at a certain speed and grade. + /// + /// Arguments: + /// * `speed` - The speed at which the vehicle is traveling + /// * `grade` - The grade of the road + /// * `distance` - The distance traveled + /// * `state` - The state of the vehicle + /// + /// Returns: + /// * `VehicleEnergyResult` - The energy required + fn consume_energy( + &self, + speed: (Speed, SpeedUnit), + grade: (Grade, GradeUnit), + distance: (Distance, DistanceUnit), + state: &[StateVar], + ) -> Result; + + /// Return the best case scenario for traveling a certain distance. + /// This is used in the a-star algorithm as a distance heuristic. + /// + /// Arguments: + /// * `distance` - The distance traveled + /// + /// Returns: + /// * `Energy` - The 'best case' energy required to travel the distance + fn best_case_energy( + &self, + distance: (Distance, DistanceUnit), + ) -> Result<(Energy, EnergyUnit), TraversalModelError>; + + /// Return the initial state of the vehicle + fn initial_state(&self) -> VehicleState; + + /// Serialize the state of the vehicle into JSON + /// + /// Arguments: + /// * `state` - The state of the vehicle + /// + /// Returns: + /// * `serde_json::Value` - The serialized state + fn serialize_state(&self, state: &[StateVar]) -> serde_json::Value; + + /// Serialize any supplemental state information (like units) into JSON + /// + /// Arguments: + /// * `state` - The state of the vehicle + /// + /// Returns: + /// * `serde_json::Value` - The serialized state information + fn serialize_state_info(&self, state: &[StateVar]) -> serde_json::Value; + + /// Give the vehicle a chance to update itself from the incoming query + /// + /// Arguments: + /// * `query` - The incoming query + /// + /// Returns: + /// * `Arc` - The updated vehicle type + fn update_from_query( + &self, + query: &serde_json::Value, + ) -> Result, TraversalModelError>; +} diff --git a/rust/routee-compass/src/app/compass/config/compass_app_builder.rs b/rust/routee-compass/src/app/compass/config/compass_app_builder.rs index bbe894c3..329f3a7c 100644 --- a/rust/routee-compass/src/app/compass/config/compass_app_builder.rs +++ b/rust/routee-compass/src/app/compass/config/compass_app_builder.rs @@ -7,8 +7,7 @@ use super::{ no_restriction_builder::NoRestrictionBuilder, road_class_builder::RoadClassBuilder, }, traversal_model::{ - distance_builder::DistanceBuilder, - speed_grade_energy_model_builder::SpeedGradeEnergyModelBuilder, + distance_builder::DistanceBuilder, energy_model_builder::EnergyModelBuilder, speed_lookup_builder::SpeedLookupBuilder, }, }; @@ -113,11 +112,11 @@ impl CompassAppBuilder { // Traversal model builders let dist: Box = Box::new(DistanceBuilder {}); let velo: Box = Box::new(SpeedLookupBuilder {}); - let smartcore: Box = Box::new(SpeedGradeEnergyModelBuilder {}); + let energy_model: Box = Box::new(EnergyModelBuilder {}); let tm_builders: HashMap> = HashMap::from([ (String::from("distance"), dist), (String::from("speed_table"), velo), - (String::from("speed_grade_energy_model"), smartcore), + (String::from("energy_model"), energy_model), ]); // Frontier model builders diff --git a/rust/routee-compass/src/app/compass/config/compass_configuration_field.rs b/rust/routee-compass/src/app/compass/config/compass_configuration_field.rs index e0d0a80d..09c248ec 100644 --- a/rust/routee-compass/src/app/compass/config/compass_configuration_field.rs +++ b/rust/routee-compass/src/app/compass/config/compass_configuration_field.rs @@ -13,6 +13,8 @@ pub enum CompassConfigurationField { Parallelism, QueryTimeoutMs, IncludeTree, + ChargeDepleting, + ChargeSustaining, } impl CompassConfigurationField { @@ -29,6 +31,8 @@ impl CompassConfigurationField { CompassConfigurationField::Plugins => "plugin", CompassConfigurationField::InputPlugins => "input_plugins", CompassConfigurationField::OutputPlugins => "output_plugins", + CompassConfigurationField::ChargeDepleting => "charge_depleting", + CompassConfigurationField::ChargeSustaining => "charge_sustaining", } } } diff --git a/rust/routee-compass/src/app/compass/config/traversal_model/energy_model_builder.rs b/rust/routee-compass/src/app/compass/config/traversal_model/energy_model_builder.rs new file mode 100644 index 00000000..c20ba155 --- /dev/null +++ b/rust/routee-compass/src/app/compass/config/traversal_model/energy_model_builder.rs @@ -0,0 +1,96 @@ +use std::collections::HashMap; +use std::sync::Arc; + +use crate::app::compass::config::compass_configuration_field::CompassConfigurationField; +use crate::app::compass::config::config_json_extension::ConfigJsonExtensions; +use routee_compass_core::model::traversal::traversal_model_builder::TraversalModelBuilder; +use routee_compass_core::model::traversal::traversal_model_error::TraversalModelError; +use routee_compass_core::model::traversal::traversal_model_service::TraversalModelService; +use routee_compass_core::util::unit::{DistanceUnit, GradeUnit, SpeedUnit, TimeUnit}; +use routee_compass_powertrain::routee::energy_model_service::EnergyModelService; + +use super::energy_model_vehicle_builders::VehicleBuilder; + +pub struct EnergyModelBuilder {} + +impl TraversalModelBuilder for EnergyModelBuilder { + fn build( + &self, + params: &serde_json::Value, + ) -> Result, TraversalModelError> { + let traversal_key = CompassConfigurationField::Traversal.to_string(); + + let speed_table_path = params + .get_config_path( + String::from("speed_table_input_file"), + traversal_key.clone(), + ) + .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; + let speed_table_speed_unit = params + .get_config_serde::( + String::from("speed_table_speed_unit"), + traversal_key.clone(), + ) + .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; + + let grade_table_path = params + .get_config_path_optional( + String::from("grade_table_input_file"), + traversal_key.clone(), + ) + .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; + let grade_table_grade_unit = params + .get_config_serde_optional::( + String::from("graph_grade_unit"), + traversal_key.clone(), + ) + .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; + + let vehicle_configs = params + .get_config_array("vehicles".to_string(), traversal_key.clone()) + .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; + + let mut vehicle_library = HashMap::new(); + + for vehicle_config in vehicle_configs { + let vehicle_type = vehicle_config + .get_config_string(String::from("type"), traversal_key.clone()) + .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; + let vehicle_builder = VehicleBuilder::from_string(vehicle_type).map_err(|e| { + TraversalModelError::BuildError(format!( + "Error building vehicle builder: {}", + e.to_string() + )) + })?; + let vehicle = vehicle_builder + .build(&vehicle_config) + .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; + vehicle_library.insert(vehicle.name(), vehicle); + } + + let output_time_unit_option = params + .get_config_serde_optional::( + String::from("output_time_unit"), + traversal_key.clone(), + ) + .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; + let output_distance_unit_option = params + .get_config_serde_optional::( + String::from("output_distance_unit"), + traversal_key.clone(), + ) + .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; + + let service = EnergyModelService::new( + &speed_table_path, + speed_table_speed_unit, + &grade_table_path, + grade_table_grade_unit, + output_time_unit_option, + output_distance_unit_option, + vehicle_library, + )?; + + Ok(Arc::new(service)) + } +} diff --git a/rust/routee-compass/src/app/compass/config/traversal_model/energy_model_vehicle_builders.rs b/rust/routee-compass/src/app/compass/config/traversal_model/energy_model_vehicle_builders.rs new file mode 100644 index 00000000..d199502c --- /dev/null +++ b/rust/routee-compass/src/app/compass/config/traversal_model/energy_model_vehicle_builders.rs @@ -0,0 +1,136 @@ +use std::sync::Arc; + +use routee_compass_core::util::unit::{ + Energy, EnergyRate, EnergyRateUnit, EnergyUnit, GradeUnit, SpeedUnit, +}; +use routee_compass_powertrain::routee::{ + prediction::{load_prediction_model, model_type::ModelType, PredictionModelRecord}, + vehicle::{ + default::{dual_fuel_vehicle::DualFuelVehicle, single_fuel_vehicle::SingleFuelVehicle}, + VehicleType, + }, +}; + +use crate::app::compass::config::{ + compass_configuration_error::CompassConfigurationError, + compass_configuration_field::CompassConfigurationField, + config_json_extension::ConfigJsonExtensions, +}; + +pub enum VehicleBuilder { + SingleFuel, + DualFuel, +} + +impl VehicleBuilder { + pub fn from_string(vehicle_type: String) -> Result { + match vehicle_type.as_str() { + "single_fuel" => Ok(VehicleBuilder::SingleFuel), + "dual_fuel" => Ok(VehicleBuilder::DualFuel), + _ => Err(CompassConfigurationError::ExpectedFieldWithType( + "vehicle.type".to_string(), + "string".to_string(), + )), + } + } + pub fn build( + &self, + parameters: &serde_json::Value, + ) -> Result, CompassConfigurationError> { + match self { + VehicleBuilder::SingleFuel => build_conventional(parameters), + VehicleBuilder::DualFuel => build_plugin_hybrid(parameters), + } + } +} + +fn build_conventional( + parameters: &serde_json::Value, +) -> Result, CompassConfigurationError> { + let vehicle_key = String::from("single_fuel"); + let name = parameters.get_config_string(String::from("name"), vehicle_key.clone())?; + + let model_record = get_model_record_from_params(parameters, name.clone())?; + + let vehicle = SingleFuelVehicle::new(name, model_record)?; + + Ok(Arc::new(vehicle)) +} + +fn build_plugin_hybrid( + parameters: &serde_json::Value, +) -> Result, CompassConfigurationError> { + let vehicle_key = String::from("dual_fuel"); + let name = parameters.get_config_string(String::from("name"), vehicle_key.clone())?; + + let charge_depleting_params = + parameters.get_config_section(CompassConfigurationField::ChargeDepleting)?; + + let charge_depleting_record = get_model_record_from_params( + &charge_depleting_params, + format!("charge_depleting: {}", name.clone()), + )?; + let charge_sustain_params = + parameters.get_config_section(CompassConfigurationField::ChargeSustaining)?; + + let charge_sustain_record = get_model_record_from_params( + &charge_sustain_params, + format!("charge_sustain: {}", name.clone()), + )?; + + let battery_capacity = parameters + .get_config_serde::(String::from("battery_capacity"), vehicle_key.clone())?; + let battery_energy_unit = parameters.get_config_serde::( + String::from("battery_capacity_unit"), + vehicle_key.clone(), + )?; + let starting_battery_energy = battery_capacity; + let phev = DualFuelVehicle::new( + name, + charge_sustain_record, + charge_depleting_record, + battery_capacity, + starting_battery_energy, + battery_energy_unit, + )?; + Ok(Arc::new(phev)) +} + +fn get_model_record_from_params( + parameters: &serde_json::Value, + parent_key: String, +) -> Result { + let name = parameters.get_config_string(String::from("name"), parent_key.clone())?; + let model_path = + parameters.get_config_path(String::from("model_input_file"), parent_key.clone())?; + let model_type = + parameters.get_config_serde::(String::from("model_type"), parent_key.clone())?; + let speed_unit = + parameters.get_config_serde::(String::from("speed_unit"), parent_key.clone())?; + let ideal_energy_rate_option = parameters.get_config_serde_optional::( + String::from("ideal_energy_rate"), + parent_key.clone(), + )?; + let grade_unit = + parameters.get_config_serde::(String::from("grade_unit"), parent_key.clone())?; + + let energy_rate_unit = parameters + .get_config_serde::(String::from("energy_rate_unit"), parent_key.clone())?; + let real_world_energy_adjustment_option = parameters.get_config_serde_optional::( + String::from("real_world_energy_adjustment"), + parent_key.clone(), + )?; + + let model_record = load_prediction_model( + name.clone(), + &model_path, + model_type, + speed_unit, + grade_unit, + energy_rate_unit, + ideal_energy_rate_option, + real_world_energy_adjustment_option, + )?; + + Ok(model_record) +} diff --git a/rust/routee-compass/src/app/compass/config/traversal_model/mod.rs b/rust/routee-compass/src/app/compass/config/traversal_model/mod.rs index 857fe071..4eace7aa 100644 --- a/rust/routee-compass/src/app/compass/config/traversal_model/mod.rs +++ b/rust/routee-compass/src/app/compass/config/traversal_model/mod.rs @@ -1,3 +1,4 @@ pub mod distance_builder; -pub mod speed_grade_energy_model_builder; +pub mod energy_model_builder; +pub mod energy_model_vehicle_builders; pub mod speed_lookup_builder; diff --git a/rust/routee-compass/src/app/compass/config/traversal_model/speed_grade_energy_model_builder.rs b/rust/routee-compass/src/app/compass/config/traversal_model/speed_grade_energy_model_builder.rs deleted file mode 100644 index d5b1c33b..00000000 --- a/rust/routee-compass/src/app/compass/config/traversal_model/speed_grade_energy_model_builder.rs +++ /dev/null @@ -1,138 +0,0 @@ -use std::collections::HashMap; -use std::sync::Arc; - -use crate::app::compass::config::compass_configuration_error::CompassConfigurationError; -use crate::app::compass::config::compass_configuration_field::CompassConfigurationField; -use crate::app::compass::config::config_json_extension::ConfigJsonExtensions; -use routee_compass_core::model::traversal::traversal_model_builder::TraversalModelBuilder; -use routee_compass_core::model::traversal::traversal_model_error::TraversalModelError; -use routee_compass_core::model::traversal::traversal_model_service::TraversalModelService; -use routee_compass_core::util::unit::{ - DistanceUnit, EnergyRate, EnergyRateUnit, GradeUnit, SpeedUnit, TimeUnit, -}; -use routee_compass_powertrain::routee::model_type::ModelType; -use routee_compass_powertrain::routee::prediction_model::SpeedGradePredictionModelRecord; -use routee_compass_powertrain::routee::speed_grade_energy_model_service::SpeedGradeEnergyModelService; - -pub struct SpeedGradeEnergyModelBuilder {} - -impl TraversalModelBuilder for SpeedGradeEnergyModelBuilder { - fn build( - &self, - params: &serde_json::Value, - ) -> Result, TraversalModelError> { - let traversal_key = CompassConfigurationField::Traversal.to_string(); - - let speed_table_path = params - .get_config_path( - String::from("speed_table_input_file"), - traversal_key.clone(), - ) - .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - let speed_table_speed_unit = params - .get_config_serde::( - String::from("speed_table_speed_unit"), - traversal_key.clone(), - ) - .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - - let grade_table_path = params - .get_config_path_optional( - String::from("grade_table_input_file"), - traversal_key.clone(), - ) - .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - let grade_table_grade_unit = params - .get_config_serde_optional::( - String::from("graph_grade_unit"), - traversal_key.clone(), - ) - .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - - let energy_model_configs = params - .get_config_array("energy_models".to_string(), traversal_key.clone()) - .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - - let mut energy_model_library = HashMap::new(); - - for energy_model_config in energy_model_configs { - let name = energy_model_config - .get_config_string(String::from("name"), traversal_key.clone()) - .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - let model_path = energy_model_config - .get_config_path(String::from("model_input_file"), traversal_key.clone()) - .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - let model_type = energy_model_config - .get_config_serde::(String::from("model_type"), traversal_key.clone()) - .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - let speed_unit = energy_model_config - .get_config_serde::(String::from("speed_unit"), traversal_key.clone()) - .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - let ideal_energy_rate_option = energy_model_config - .get_config_serde_optional::( - String::from("ideal_energy_rate"), - traversal_key.clone(), - ) - .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - let grade_unit = energy_model_config - .get_config_serde::(String::from("grade_unit"), traversal_key.clone()) - .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - - let energy_rate_unit = energy_model_config - .get_config_serde::( - String::from("energy_rate_unit"), - traversal_key.clone(), - ) - .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - let real_world_energy_adjustment_option = params - .get_config_serde_optional::( - String::from("real_world_energy_adjustment"), - traversal_key.clone(), - ) - .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - - let model_record = SpeedGradePredictionModelRecord::new( - name.clone(), - &model_path, - model_type, - speed_unit, - grade_unit, - energy_rate_unit, - ideal_energy_rate_option, - real_world_energy_adjustment_option, - ) - .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - energy_model_library.insert(name, Arc::new(model_record)); - } - - let output_time_unit_option = params - .get_config_serde_optional::( - String::from("output_time_unit"), - traversal_key.clone(), - ) - .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - let output_distance_unit_option = params - .get_config_serde_optional::( - String::from("output_distance_unit"), - traversal_key.clone(), - ) - .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - - let service = SpeedGradeEnergyModelService::new( - &speed_table_path, - speed_table_speed_unit, - &grade_table_path, - grade_table_grade_unit, - output_time_unit_option, - output_distance_unit_option, - energy_model_library, - ) - .map_err(CompassConfigurationError::TraversalModelError) - .map_err(|e| TraversalModelError::BuildError(e.to_string()))?; - // let service = SpeedGradeEnergyModelService { - // service: inner_service, - // }; - let result: Arc = Arc::new(service); - Ok(result) - } -}