-
Notifications
You must be signed in to change notification settings - Fork 0
Motor losses #36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Motor losses #36
Conversation
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Summary of ChangesHello @Harsha-Samavedam, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces significant advancements to the vehicle simulation framework by integrating detailed electrical motor and battery loss models, refining existing energy models for solar array and rolling resistance, and establishing a robust data logging and visualization pipeline. It culminates in the creation of an interactive Streamlit UI, making the simulation more accessible and user-friendly for analysis and parameter tuning. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request significantly enhances the vehicle simulation by adding a command-line interface, data logging to CSV, and graph generation. It also introduces new models for battery and motor losses, and refactors the existing model structure. The changes are extensive and add a lot of value. However, there are several critical issues that will prevent the code from running, mainly related to incorrect imports after file renaming and missing model initializations that will lead to runtime errors. There are also some medium-severity issues regarding incorrect type hints and magic numbers. I've provided detailed comments and suggestions to address these problems.
I am having trouble creating individual review comments. Click here to see my feedback.
main.py (178-180)
A BatteryModel is not being added to the VehicleModel. The updated VehicleModel.update method expects a battery model to be present to calculate battery losses. To include these calculations in the simulation, you should add the BatteryModel. This will also require importing it at the top of the file: from models.battery import BatteryModel.
models/array.py (69)
This line contains an indented comment, which is a SyntaxError in Python and will cause the program to crash if this code path is executed. Please unindent the comment.
# energy produced this timestep (Power × time)
models/motor.py (1)
The import from models.base import ModelPlugin is incorrect. The file models/base.py has been renamed to models/vehicle_model.py, and it does not contain a ModelPlugin class. This will cause an ImportError. This file also appears to be incomplete.
models/vehicle_model.py (23)
This line will raise an AttributeError if no BatteryModel has been added to the VehicleModel, as self.battery_model will be None. You should add a check to ensure self.battery_model exists before calling its update method to make the model more robust.
if self.battery_model:
self.params['total_energy'] += self.battery_model.update(self.params, self.params['timestep'])
ui.py (7)
The import from models.base import VehicleModel is incorrect because models/base.py was renamed to models/vehicle_model.py. This will cause an ImportError when running the UI.
from models.vehicle_model import VehicleModel
ui.py (71-73)
A BatteryModel is not being added to the VehicleModel. The updated VehicleModel.update method expects a battery model to be present to calculate battery losses. To include these calculations in the simulation, you should add the BatteryModel. This will also require importing it at the top of the file: from models.battery import BatteryModel.
main.py (25)
The function's return type hint dict[str, Quantity[float]] does not match the implementation. The result dictionary can contain datetime objects, as handled in the if block. The type hint should be updated to reflect this to improve code clarity and help static analysis tools.
def parse_yaml(yaml_path: str) -> dict[str, datetime | Quantity[float]]:
models/battery.py (10)
The type hint for the params dictionary is dict[str, float], but the VehicleModel passes a dictionary of pint.Quantity objects (dict[str, Quantity[float]]). Using the correct type hint will improve code clarity and allow static analysis tools to catch potential errors.
def update(self, params: dict[str, Quantity[float]], timestep: Quantity[float]) -> Quantity[float]:
models/battery.py (27)
The type hint for the params dictionary is dict[str, float], but it should be dict[str, Quantity[float]] to match the data passed by VehicleModel. This will improve type safety and code clarity.
def update(self, params: dict[str, Quantity[float]], timestep:Quantity[float]):
models/motor_losses.py (116)
The magic number 9.549 is used for converting power from (N·m * RPM) to Watts. It would be better to define this as a named constant with a comment explaining its origin (60 / (2 * pi)) to improve readability and maintainability.
models/motor_losses.py (242)
The value 3000 appears to be a reference RPM and is hardcoded. This makes the calculation less flexible and harder to understand. Consider defining it as a named constant or passing it as a parameter to the function.
|
Currently the UI stuff is adding too much to this PR. I'd like you to move everything UI related to a different branch, then I'll start taking a look at the motor loss stuff to verify it. Additionally, I don't think ModelPlugin exists as a class. Is this a hallucination perhaps? |
- Resolved import conflict in main.py - Added LVDrawModel import - Kept BatteryModel initialization
0c1ac7c to
5136d99
Compare
IshDeshpa
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tsk tsk tsk
IshDeshpa
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tsk tsk tsk 2
IshDeshpa
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getting closer...
models/motor_losses.py
Outdated
| ) -> PlainQuantity[float]: | ||
| # Get current operating conditions from params | ||
| # These should be set by the simulation based on current state | ||
| I = params.get('motor_current', Q_(0, 'A')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the drag_power and rr_power added together to derive the current here. Should be:
drag_power + rr_power / battery voltage
models/motor_losses.py
Outdated
| # Get current operating conditions from params | ||
| # These should be set by the simulation based on current state | ||
| I = params.get('motor_current', Q_(0, 'A')) | ||
| N_rpm = params.get('motor_speed', Q_(0, 'rpm')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calculate this from the velocity parameter (divide by wheel circumference)
IshDeshpa
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getting better...
models/motor_losses.py
Outdated
| ) -> PlainQuantity[float]: | ||
| # Calculate motor speed from velocity and wheel circumference | ||
| # N_rpm = (velocity / wheel_circumference) * 60 | ||
| import numpy as np |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't need to import this; already imported at the top of the file
models/motor_losses.py
Outdated
| velocity = params.get('velocity', Q_(0, 'm/s')) | ||
| wheel_diameter = params.get('wheel_diameter', Q_(0.5, 'm')) | ||
| wheel_circumference = np.pi * wheel_diameter | ||
| # Convert velocity to rotational speed | ||
| N_rpm = (velocity / wheel_circumference * Q_(60, 's/min')).to('rpm') | ||
|
|
||
| # Calculate motor current from power demands | ||
| # I = (drag_power + rr_power) / battery_voltage | ||
| drag_power = params.get('drag_power', Q_(0, 'W')) | ||
| rr_power = params.get('rr_power', Q_(0, 'W')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of using params.get here, I would probably actually recommend using the typical params['key'] method of getting values from a dictionary so that it errors out if the key doesn't exist. This way we don't keep running the sim with a bogus value if the key doesn't exist in params.yaml.
params.yaml
Outdated
| name: motor_eta_C_default | ||
| unit: dimensionless | ||
| value: 0.97 | ||
| - | ||
| name: motor_T_ref_default | ||
| unit: °C | ||
| value: 25.0 | ||
| - | ||
| name: motor_T_operating_default | ||
| unit: °C | ||
| value: 70.0 | ||
| - | ||
| name: motor_k_H_default | ||
| unit: W/rpm | ||
| value: 0.0 | ||
| - | ||
| name: motor_k_E_default | ||
| unit: W/rpm^2 | ||
| value: 0.0 | ||
| - | ||
| name: motor_k_B1_default | ||
| unit: N·m | ||
| value: 0.0 | ||
| - | ||
| name: motor_k_B2_default | ||
| unit: N·m·min/rad | ||
| value: 0.0 | ||
| - | ||
| name: motor_k_D_default | ||
| unit: W/rpm^3 | ||
| value: 0.0 | ||
| - | ||
| name: motor_k_SW_default | ||
| unit: W/rpm | ||
| value: 0.0 | ||
| - | ||
| name: motor_k_1_default | ||
| unit: W/rpm | ||
| value: 0.0 | ||
| - | ||
| name: motor_k_2_default | ||
| unit: W/rpm^2 | ||
| value: 0.0 | ||
| - | ||
| name: motor_k_3_default | ||
| unit: W/rpm^3 | ||
| value: 0.0 | ||
|
|
||
| # Motor parameters - general configuration for the vehicle's motor | ||
| - | ||
| name: motor_k_T | ||
| unit: N·m/A | ||
| value: 0.1910 | ||
| - | ||
| name: motor_k_C | ||
| unit: V·min/rad | ||
| value: 0.020 | ||
| - | ||
| name: motor_k_S | ||
| unit: N·m/A | ||
| value: 0.1910 | ||
| - | ||
| name: motor_R_A | ||
| unit: ohm | ||
| value: 0.0931 | ||
| - | ||
| name: motor_R_B | ||
| unit: ohm | ||
| value: 0.0233 | ||
| - | ||
| name: motor_eta_C | ||
| unit: dimensionless | ||
| value: 0.97 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i would put some comments here explaining what these constants are (maybe grab some info from the confluence doc you made).
IshDeshpa
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like the only function you actually need is total_motor_loss. I would remove anything that's unnecessary.
| def R_equivalent_battery_side(self, params: dict[str, PlainQuantity[float]]) -> PlainQuantity[float]: | ||
| eta_C = params['motor_eta_C'] | ||
| return 1.5 * self.R_total(params) / eta_C |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't know if this function is actually being used.
| wheel_diameter = params['wheel_diameter'] | ||
| wheel_circumference = np.pi * wheel_diameter | ||
| # Convert velocity to rotational speed | ||
| N_rpm = (velocity / wheel_circumference * Q_(60, 's/min')).to('rpm') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can remove the * Q_(60, 's/min') since you're using .to('rpm') anyways.
| N_rpm = (velocity / wheel_circumference * Q_(60, 's/min')).to('rpm') | ||
|
|
||
| # Calculate motor current from power demands | ||
| # I = (drag_power + rr_power) / battery_voltage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove comment
| # Convert (N·m × rpm) to Watts: 9.549 = 60/(2π) for rpm to rad/s conversion | ||
| P_shaft = tau_S * N_rpm / Q_(9.549, 'dimensionless') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you not just do .to('Watts') here?
| 'eta_controller': eta_C | ||
| } | ||
|
|
||
| def system_power_analysis(self, tau_S: PlainQuantity[float], N_rpm: PlainQuantity[float], V_bus: PlainQuantity[float], params: dict[str, PlainQuantity[float]]) -> Dict[str, PlainQuantity[float]]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When is this actually called?
| 'P_motor_total': P_total | ||
| } | ||
|
|
||
| def motor_efficiency(self, tau_S: PlainQuantity[float], N_rpm: PlainQuantity[float], params: dict[str, PlainQuantity[float]]) -> Dict[str, PlainQuantity[float]]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if system power analysis is never called then this function is not needed either
calculating electrical motor losses