Temperature Models Example#
This example demonstrates how to use models with temperature as a feature in the Routee Powertrain library.
import routee.powertrain as pt
import pandas as pd
import matplotlib.pyplot as plt
To demonstrate the use of temperature models, we will load a standard Tesla Model 3 RWD model (without temperature as a feature) and two temperature models (steady-state and transient) for the same vehicle. We will then predict energy consumption over a sample route at different ambient temperatures (72°F and 32°F) and compare the results.
It's important to understand the distinction between steady-state and transient temperature models:
Steady-State Temperature Models: These models assume that the vehicle's thermal conditions have stabilized to a constant state. They are typically used for longer trips where the vehicle has had sufficient time for the control systems to have stabilized the thermal environment. In this example, we will use the steady-state model for the portion of the trip after the first 5 minutes at 32°F.
Transient Temperature Models: These models account for the period when the vehicle is still adjusting to the ambient temperature. For example, when a vehicle starts a trip in cold weather and has been sitting outside, it takes some time for the battery and cabin to warm up.
tesla = pt.load_model("tesla/model_3_rwd/2022/rf_c3326385/v1")
tesla_with_temp_steady = pt.load_model(
"tesla/model_3_rwd/2022/rf_steady_thermal_ab1db342/v1"
)
tesla_with_temp_transient = pt.load_model(
"tesla/model_3_rwd/2022/rf_transient_thermal_ab1db342/v1"
)
/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
from .autonotebook import tqdm as notebook_tqdm
---------------------------------------------------------------------------
HTTPError Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/huggingface_hub/utils/_http.py:403, in hf_raise_for_status(response, endpoint_name)
402 try:
--> 403 response.raise_for_status()
404 except HTTPError as e:
File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/requests/models.py:1167, in Response.raise_for_status(self)
1166 if http_error_msg:
-> 1167 raise HTTPError(http_error_msg, response=self)
HTTPError: 404 Client Error: Not Found for url: https://huggingface.co/NatLabRockies/routee-powertrain-model-library/resolve/main/v2/tesla/model_3_rwd/2022/rf_c3326385/v1/metadata.json
The above exception was the direct cause of the following exception:
EntryNotFoundError Traceback (most recent call last)
Cell In[2], line 1
----> 1 tesla = pt.load_model("tesla/model_3_rwd/2022/rf_c3326385/v1")
2 tesla_with_temp_steady = pt.load_model(
3 "tesla/model_3_rwd/2022/rf_steady_thermal_ab1db342/v1"
4 )
5 tesla_with_temp_transient = pt.load_model(
6 "tesla/model_3_rwd/2022/rf_transient_thermal_ab1db342/v1"
7 )
File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/routee/powertrain/io/load.py:272, in load_model(name_or_path, registry)
270 if isinstance(name_or_path, (str, Path)):
271 mid = _resolve_load_target(str(name_or_path), registry)
--> 272 return registry.load(mid)
274 raise ValueError(
275 f"Could not load model: {name_or_path}. "
276 "Provide a valid local file/directory or a valid ModelId/string with a registry."
277 )
File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/routee/powertrain/registry/hf.py:230, in HFRegistry.load(self, model_id)
228 # Fetch metadata to learn the model filename
229 meta_path = f"{dir_path}/{METADATA_FILENAME}"
--> 230 meta_bytes = self._fetch_bytes(meta_path)
231 metadata_dict = json.loads(meta_bytes)
233 model_filename = _model_filename(metadata_dict)
File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/routee/powertrain/registry/hf.py:123, in HFRegistry._fetch_bytes(self, path)
117 """Download one file from the repository and return its bytes.
118
119 The file is fetched into the local HuggingFace cache, so repeat calls
120 for the same revision do not re-download.
121 """
122 client = self._get_client()
--> 123 local_path = client.hf_hub_download(
124 repo_id=self.repo_id,
125 filename=path,
126 repo_type=self.repo_type,
127 revision=self.revision,
128 )
129 return Path(local_path).read_bytes()
File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/huggingface_hub/utils/_validators.py:114, in validate_hf_hub_args.<locals>._inner_fn(*args, **kwargs)
111 if check_use_auth_token:
112 kwargs = smoothly_deprecate_use_auth_token(fn_name=fn.__name__, has_token=has_token, kwargs=kwargs)
--> 114 return fn(*args, **kwargs)
File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/huggingface_hub/hf_api.py:5467, in HfApi.hf_hub_download(self, repo_id, filename, subfolder, repo_type, revision, cache_dir, local_dir, force_download, proxies, etag_timeout, token, local_files_only, resume_download, force_filename, local_dir_use_symlinks)
5463 if token is None:
5464 # Cannot do `token = token or self.token` as token can be `False`.
5465 token = self.token
-> 5467 return hf_hub_download(
5468 repo_id=repo_id,
5469 filename=filename,
5470 subfolder=subfolder,
5471 repo_type=repo_type,
5472 revision=revision,
5473 endpoint=self.endpoint,
5474 library_name=self.library_name,
5475 library_version=self.library_version,
5476 cache_dir=cache_dir,
5477 local_dir=local_dir,
5478 local_dir_use_symlinks=local_dir_use_symlinks,
5479 user_agent=self.user_agent,
5480 force_download=force_download,
5481 force_filename=force_filename,
5482 proxies=proxies,
5483 etag_timeout=etag_timeout,
5484 resume_download=resume_download,
5485 token=token,
5486 headers=self.headers,
5487 local_files_only=local_files_only,
5488 )
File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/huggingface_hub/utils/_validators.py:114, in validate_hf_hub_args.<locals>._inner_fn(*args, **kwargs)
111 if check_use_auth_token:
112 kwargs = smoothly_deprecate_use_auth_token(fn_name=fn.__name__, has_token=has_token, kwargs=kwargs)
--> 114 return fn(*args, **kwargs)
File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/huggingface_hub/file_download.py:1014, in hf_hub_download(repo_id, filename, subfolder, repo_type, revision, library_name, library_version, cache_dir, local_dir, user_agent, force_download, proxies, etag_timeout, token, local_files_only, headers, endpoint, resume_download, force_filename, local_dir_use_symlinks)
994 return _hf_hub_download_to_local_dir(
995 # Destination
996 local_dir=local_dir,
(...)
1011 local_files_only=local_files_only,
1012 )
1013 else:
-> 1014 return _hf_hub_download_to_cache_dir(
1015 # Destination
1016 cache_dir=cache_dir,
1017 # File info
1018 repo_id=repo_id,
1019 filename=filename,
1020 repo_type=repo_type,
1021 revision=revision,
1022 # HTTP info
1023 endpoint=endpoint,
1024 etag_timeout=etag_timeout,
1025 headers=hf_headers,
1026 proxies=proxies,
1027 token=token,
1028 # Additional options
1029 local_files_only=local_files_only,
1030 force_download=force_download,
1031 )
File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/huggingface_hub/file_download.py:1077, in _hf_hub_download_to_cache_dir(cache_dir, repo_id, filename, repo_type, revision, endpoint, etag_timeout, headers, proxies, token, local_files_only, force_download)
1073 return pointer_path
1075 # Try to get metadata (etag, commit_hash, url, size) from the server.
1076 # If we can't, a HEAD request error is returned.
-> 1077 (url_to_download, etag, commit_hash, expected_size, xet_file_data, head_call_error) = _get_metadata_or_catch_error(
1078 repo_id=repo_id,
1079 filename=filename,
1080 repo_type=repo_type,
1081 revision=revision,
1082 endpoint=endpoint,
1083 proxies=proxies,
1084 etag_timeout=etag_timeout,
1085 headers=headers,
1086 token=token,
1087 local_files_only=local_files_only,
1088 storage_folder=storage_folder,
1089 relative_filename=relative_filename,
1090 )
1092 # etag can be None for several reasons:
1093 # 1. we passed local_files_only.
1094 # 2. we don't have a connection
(...)
1100 # If the specified revision is a commit hash, look inside "snapshots".
1101 # If the specified revision is a branch or tag, look inside "refs".
1102 if head_call_error is not None:
1103 # Couldn't make a HEAD call => let's try to find a local file
File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/huggingface_hub/file_download.py:1550, in _get_metadata_or_catch_error(repo_id, filename, repo_type, revision, endpoint, proxies, etag_timeout, headers, token, local_files_only, relative_filename, storage_folder)
1548 try:
1549 try:
-> 1550 metadata = get_hf_file_metadata(
1551 url=url, proxies=proxies, timeout=etag_timeout, headers=headers, token=token, endpoint=endpoint
1552 )
1553 except EntryNotFoundError as http_error:
1554 if storage_folder is not None and relative_filename is not None:
1555 # Cache the non-existence of the file
File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/huggingface_hub/utils/_validators.py:114, in validate_hf_hub_args.<locals>._inner_fn(*args, **kwargs)
111 if check_use_auth_token:
112 kwargs = smoothly_deprecate_use_auth_token(fn_name=fn.__name__, has_token=has_token, kwargs=kwargs)
--> 114 return fn(*args, **kwargs)
File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/huggingface_hub/file_download.py:1467, in get_hf_file_metadata(url, token, proxies, timeout, library_name, library_version, user_agent, headers, endpoint)
1464 hf_headers["Accept-Encoding"] = "identity" # prevent any compression => we want to know the real size of the file
1466 # Retrieve metadata
-> 1467 r = _request_wrapper(
1468 method="HEAD",
1469 url=url,
1470 headers=hf_headers,
1471 allow_redirects=False,
1472 follow_relative_redirects=True,
1473 proxies=proxies,
1474 timeout=timeout,
1475 )
1476 hf_raise_for_status(r)
1478 # Return
File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/huggingface_hub/file_download.py:283, in _request_wrapper(method, url, follow_relative_redirects, **params)
281 # Recursively follow relative redirects
282 if follow_relative_redirects:
--> 283 response = _request_wrapper(
284 method=method,
285 url=url,
286 follow_relative_redirects=False,
287 **params,
288 )
290 # If redirection, we redirect only relative paths.
291 # This is useful in case of a renamed repository.
292 if 300 <= response.status_code <= 399:
File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/huggingface_hub/file_download.py:307, in _request_wrapper(method, url, follow_relative_redirects, **params)
305 # Perform request and return if status_code is not in the retry list.
306 response = http_backoff(method=method, url=url, **params)
--> 307 hf_raise_for_status(response)
308 return response
File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/huggingface_hub/utils/_http.py:414, in hf_raise_for_status(response, endpoint_name)
412 elif error_code == "EntryNotFound":
413 message = f"{response.status_code} Client Error." + "\n\n" + f"Entry Not Found for url: {response.url}."
--> 414 raise _format(EntryNotFoundError, message, response) from e
416 elif error_code == "GatedRepo":
417 message = (
418 f"{response.status_code} Client Error." + "\n\n" + f"Cannot access gated repo for url {response.url}."
419 )
EntryNotFoundError: 404 Client Error. (Request ID: Root=1-6a6d0a47-34b9fea614ccac8641994961;a9fe20df-06ce-4a12-b969-b784cac7c073)
Entry Not Found for url: https://huggingface.co/NatLabRockies/routee-powertrain-model-library/resolve/main/v2/tesla/model_3_rwd/2022/rf_c3326385/v1/metadata.json.
Load a sample route and prepare it for prediction.
sample_route = pt.load_sample_route()
sample_route["time_minutes"] = (
sample_route["distance"] / sample_route["speed_mph"]
) * 60
sample_route["cummulative_time_minutes"] = sample_route["time_minutes"].cumsum()
sample_route["cummulative_distance"] = sample_route["distance"].cumsum()
Set the ambient temperature for the route.
sample_route_72F = sample_route.copy()
sample_route_72F["ambient_temp_f"] = 72
sample_route_32F = sample_route.copy()
sample_route_32F["ambient_temp_f"] = 32
For the 32°F route, we will use the transient model for the first 5 minutes and the steady-state model for the remainder of the trip.
sample_route_32F_transient = sample_route_32F[
sample_route_32F["cummulative_time_minutes"] <= 5
]
sample_route_32F_steady = sample_route_32F[
sample_route_32F["cummulative_time_minutes"] > 5
]
Predict energy consumption using the different models and ambient temperatures. Each model uses its own configured feature set, so we just need to make sure the input DataFrame contains the columns the model expects.
energy = tesla.predict(sample_route)
energy_with_temp_72F = tesla_with_temp_steady.predict(sample_route_72F)
energy_with_temp_32F_transient = tesla_with_temp_transient.predict(
sample_route_32F_transient
)
energy_with_temp_32F_steady = tesla_with_temp_steady.predict(sample_route_32F_steady)
energy_with_temp_32F = pd.concat(
[energy_with_temp_32F_transient, energy_with_temp_32F_steady]
)
Now, we can compare the energy consumption results.
energy["cummulative_energy_kwh"] = energy["kwh"].cumsum()
energy_with_temp_72F["cummulative_energy_kwh"] = energy_with_temp_72F["kwh"].cumsum()
energy_with_temp_32F["cummulative_energy_kwh"] = energy_with_temp_32F["kwh"].cumsum()
plt.plot(
sample_route["cummulative_distance"],
energy["cummulative_energy_kwh"],
label="Tesla without Temperature",
)
plt.plot(
sample_route["cummulative_distance"],
energy_with_temp_72F["cummulative_energy_kwh"],
label="Tesla with Temperature 72F",
)
plt.plot(
sample_route["cummulative_distance"],
energy_with_temp_32F["cummulative_energy_kwh"],
label="Tesla with Temperature 32F",
)
plt.xlabel("Cumulative Distance (miles)")
plt.ylabel("Cumulative Energy (kWh)")
plt.legend()
Notice how the energy consumption for the 32°F route is higher than the other two scenarios, reflecting the increased energy demand in colder temperatures.
Something else to note is that the model that doesn't consider temperature explicitly includes a "real world correction factor" to account for things like temperature on average. This explains why the energy consumption for the 72°F route is slightly lower than the other two scenarios since the model without temperature adjustment is effectively averaging out the impact of temperature. The 72°F condition would be considered the "ideal" case since the vehicle does not have to expand any extra effort to maintain the cabin temperature.
Multi-Vehicle Comparison#
Now let's compare the Tesla Model 3 with other electric vehicles to see how different EVs perform under various temperature conditions. We'll load the Nissan Leaf and Chevrolet Bolt models and compare their energy consumption across different temperatures.
nissan_leaf_steady = pt.load_model(
"nissan/leaf_30_kwh/2016/rf_steady_thermal_ab1db342/v1"
)
nissan_leaf_transient = pt.load_model(
"nissan/leaf_30_kwh/2016/rf_transient_thermal_ab1db342/v1"
)
chevy_bolt_steady = pt.load_model(
"chevrolet/bolt_ev/2020/rf_steady_thermal_ab1db342/v1"
)
chevy_bolt_transient = pt.load_model(
"chevrolet/bolt_ev/2020/rf_transient_thermal_ab1db342/v1"
)
Temperature Sensitivity Comparison#
Let's compare how each vehicle's energy consumption changes across a range of temperatures (0°F, 15°F, 32°F, 50°F, 72°F, 90°F, 110°F). We'll use steady-state models for this comparison.
# Predict energy for all vehicles at different temperatures
temperatures = [0, 15, 32, 50, 72, 90, 110]
vehicles_data = {
"Tesla Model 3 RWD": tesla_with_temp_steady,
"Nissan Leaf 30 kWh": nissan_leaf_steady,
"Chevrolet Bolt EV": chevy_bolt_steady,
}
total_energy_by_temp = {vehicle: [] for vehicle in vehicles_data.keys()}
for temp in temperatures:
route_temp = sample_route.copy()
route_temp["ambient_temp_f"] = temp
for vehicle_name, model in vehicles_data.items():
energy_pred = model.predict(route_temp)
total_energy_by_temp[vehicle_name].append(energy_pred["kwh"].sum())
# Create temperature sensitivity comparison plot
plt.figure(figsize=(10, 6))
for vehicle_name, energies in total_energy_by_temp.items():
plt.plot(
temperatures,
energies,
marker="o",
linewidth=2,
markersize=8,
label=vehicle_name,
)
plt.xlabel("Ambient Temperature (°F)")
plt.ylabel("Total Energy Consumption (kWh)")
plt.title("Temperature Sensitivity Comparison Across Vehicles")
plt.legend()
plt.grid(True, alpha=0.3)