Skip to content
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

Add design optimisation example #149

Merged
merged 25 commits into from
Jan 8, 2024
Merged

Add design optimisation example #149

merged 25 commits into from
Jan 8, 2024

Conversation

NicolaCourtier
Copy link
Member

No description provided.

@NicolaCourtier NicolaCourtier linked an issue Dec 15, 2023 that may be closed by this pull request
@NicolaCourtier NicolaCourtier marked this pull request as ready for review December 15, 2023 10:46
Copy link

codecov bot commented Dec 15, 2023

Codecov Report

Attention: 7 lines in your changes are missing coverage. Please review.

Comparison is base (3ba9a6a) 93.03% compared to head (2578c36) 94.10%.
Report is 11 commits behind head on develop.

❗ Current head 2578c36 differs from pull request most recent head 112139d. Consider uploading reports for the commit 112139d to get more accurate results

Files Patch % Lines
pybop/_problem.py 94.73% 2 Missing ⚠️
pybop/optimisers/scipy_optimisers.py 92.00% 2 Missing ⚠️
examples/scripts/spme_max_energy.py 98.27% 1 Missing ⚠️
pybop/optimisers/nlopt_optimize.py 66.66% 1 Missing ⚠️
pybop/plotting/quick_plot.py 88.88% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #149      +/-   ##
===========================================
+ Coverage    93.03%   94.10%   +1.06%     
===========================================
  Files           34       36       +2     
  Lines         1178     1323     +145     
===========================================
+ Hits          1096     1245     +149     
+ Misses          82       78       -4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Member

@BradyPlanden BradyPlanden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @NicolaCourtier, I've start the review but have to cut it short. I'll try to continue to it later this afternoon. Great addition, I'm really excited for it to be merged! I've added comments around areas that I think can be improved or where I didn't follow the flow.

examples/scripts/spme_max_energy.py Outdated Show resolved Hide resolved


# Define functions
def nominal_capacity(x, model):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would prefer to have this be within a pybop class. This example would be simpler with nominal_capacity(), cell_mass(), and GravimetricEnergyDensity() in a seperate pybop class.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now, they need to be defined in the example - the definitions are not fixed and have been adjusted for the purpose of maximising the gravimetric energy density within the constraints of the model. Do you mean a class within the script?

Copy link
Member

@BradyPlanden BradyPlanden Dec 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More like the GravimetricEnergyDensity class would be within pybop.costs with the nominal_capacity and cell_mass methods inside something like a _utilities.py file. They could be then be called within this example via pybop.cell_mass() andpybop.nominal_capacity(), without needing to be defined.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As below, I don't think the definitions should be taken out of context at this stage, but I think this example is still worth having as we continue to test and develop!

theoretical_energy = model._electrode_soh.calculate_theoretical_energy(
model._parameter_set
)
average_voltage = (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be the mean of the output model voltage instead of the mean on the bounds? A mean value from the OCV function is an alternative if we don't have the model output at this point.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nominal capacity is required to scale the C-rate used in the experiment, so it must be computed before the simulation. However, it is only a scaling factor on the current so, as long as the same average_voltage is used each time, I believe the energy density will be maximised.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. This implementation would result in an optimal energy density for a given applied current (scaled by the calculated nom. capacity), but this applied current wouldn't be the correct C-rate, right? I think we can get the correct mean voltage via the OCV function though.

Something like this model._parameter_set["Positive electrode OCP [V]"](mean_sto) where themean_sto variable can be calculated by taking the mean ofmodel._esoh_solver.get_min_max_stoichiometries(). More information here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I understand, there's no feasible way to compute the "correct C-rate" - this is a challenge that comes with using C-rate instead of a defined current and one that we will probably have to address more explicitly in user guidance in the future. In practice, the measured charge/discharge capacity depends on the electrode balancing as well as the overpotential dynamics. So the question becomes: how accurate does the C-rate need to be for the optimisation results to hold and by how much do we want to prioritise accuracy over speed?

The approach you suggest will certainly give a more accurate "average_voltage" but it will also increase the computational load, which is already high due to the re-building. It may also disguise the fact that we need to use an approximation here to obtain a C-rate pre-experiment. We could calculate the realistic mean once at the beginning, using the starting parameter set and inputs, but this would change the cost values from run-to-run so reproducibility could be an issue.

A fixed, easy-to-compute value avoids these issues and, while a particular voltage use-case may be more helpful, I think an average of the voltage limits is a reasonable approximation for now.

examples/scripts/spme_max_energy.py Outdated Show resolved Hide resolved
"""

# Approximations due to SPM(e) parameter set limitations
electrolyte_density = ps["Separator density [kg.m-3]"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No electrolyte density variable for the SPMe, correct? Since it's just a scalar in this example, I would probably remove it instead of substituting with the separator density variable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, we could just pull from literature for a standard EMC/DMC elecrolyte at a given molarity.



# Define cost function as a subclass
class GravimetricEnergyDensity(pybop.BaseCost):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be within _costs.py?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it should be taken out of context at this stage.

examples/scripts/spme_max_energy.py Outdated Show resolved Hide resolved
examples/scripts/spme_max_energy.py Show resolved Hide resolved
@@ -259,6 +259,12 @@ def __init__(
init_soc=self.init_soc,
)

# Add an example dataset for plotting comparison
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I follow this addition. Could this be completed outside of the DesignProblem class, maybe by grabbing the first step in the optimiser?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly? It could be left to the user to add these steps if desired. I added them here simply to ensure that there is a target dataset for the plotting comparisons.

pybop/_problem.py Show resolved Hide resolved
Copy link
Member

@BradyPlanden BradyPlanden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good @NicolaCourtier, nice job!

@BradyPlanden BradyPlanden merged commit 24429ec into develop Jan 8, 2024
15 of 16 checks passed
@BradyPlanden BradyPlanden deleted the 38b-design-example branch January 8, 2024 10:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add design optimisation example
2 participants