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

Feature: Gurobi backend interface #551

Merged
merged 40 commits into from
Jul 9, 2024
Merged

Conversation

brynpickering
Copy link
Member

@brynpickering brynpickering commented Jan 26, 2024

Fixes #349
Fixes #597 (by pinning pyomo)

Summary of changes in this pull request:

  • Duplicated pyomo backend interface as a pure gurobipy interface
  • Added basic tests of the interface methods

How to use:

  1. Install gurobipy into your calliope env: mamba install gurobi::gurobi
  2. Build optimisation problem with gurobi backend:
import calliope

model = calliope.Model(...)
model.build(backend="gurobi")
model.solve()

NOTE: without a license, one can only work with very small models. Our example models are already too big (maybe if run over ~2 timesteps it would work?).

Reviewer checklist:

  • Test(s) added to cover contribution
  • Documentation updated
  • Changelog updated
  • Coverage maintained or improved

Copy link

codecov bot commented Jan 26, 2024

Codecov Report

Attention: Patch coverage is 95.45455% with 18 lines in your changes missing coverage. Please review.

Project coverage is 95.92%. Comparing base (1e35888) to head (59d6e19).
Report is 23 commits behind head on main.

Files with missing lines Patch % Lines
src/calliope/backend/gurobi_backend_model.py 95.60% 4 Missing and 7 partials ⚠️
src/calliope/backend/backend_model.py 90.90% 0 Missing and 4 partials ⚠️
src/calliope/backend/pyomo_backend_model.py 97.29% 0 Missing and 2 partials ⚠️
src/calliope/backend/expression_parser.py 95.23% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #551      +/-   ##
==========================================
+ Coverage   95.30%   95.92%   +0.61%     
==========================================
  Files          25       26       +1     
  Lines        3664     3899     +235     
  Branches      803      768      -35     
==========================================
+ Hits         3492     3740     +248     
+ Misses         83       69      -14     
- Partials       89       90       +1     
Files with missing lines Coverage Δ
src/calliope/backend/__init__.py 100.00% <100.00%> (+12.50%) ⬆️
src/calliope/backend/latex_backend_model.py 96.26% <100.00%> (ø)
src/calliope/backend/parsing.py 96.88% <100.00%> (+0.85%) ⬆️
src/calliope/model.py 96.29% <ø> (+1.55%) ⬆️
src/calliope/backend/expression_parser.py 94.00% <95.23%> (+0.46%) ⬆️
src/calliope/backend/pyomo_backend_model.py 98.07% <97.29%> (+2.46%) ⬆️
src/calliope/backend/backend_model.py 97.66% <90.90%> (-0.43%) ⬇️
src/calliope/backend/gurobi_backend_model.py 95.60% <95.60%> (ø)

... and 3 files with indirect coverage changes

@brynpickering
Copy link
Member Author

I have added tests, but we don't have gurobipy installed in the CI builds so those tests are being skipped. Hence the massive coverage drop.

@brynpickering brynpickering force-pushed the feature-gurobi-interface branch from db4d420 to 4ff46d0 Compare February 12, 2024 16:29
@brynpickering
Copy link
Member Author

I have run the urban scale model for a full year using the pyomo (with gurobi solver) and gurobi interfaces and get the attached results.

Headline: Gurobi interface peaks at ~30% less memory than pyomo.
I know you got different results when running locally @sjpfenninger. This could be because I'm using a cloud license for Gurobi which I think means it offloads the actual solving onto a remote machine.

memray_flamegraphs.zip


gurobi run:

import calliope

calliope.set_log_verbosity("info")
m = calliope.examples.urban_scale(time_subset=None)
m.build(backend="gurobi")
m.solve(solver_options={"Threads": 1})

pyomo run:

import calliope

calliope.set_log_verbosity("info")
m = calliope.examples.urban_scale(time_subset=None)
m.build(backend="pyomo")
m.solve(
solver="gurobi", solver_options={"Threads": 1})

Profiling using memray and setting [out] and [runfile] separately for pyomo and gurobi runs:

memray run --trace-python-allocators -o [out].bin [runfile].py    
memray flamegraph [out].bin -o [out].html    

@brynpickering
Copy link
Member Author

Last week's new pyomo release has caused an issue in one of our tests (see #597). I've pinned Pyomo upper bound to avoid this issue for now.

@brynpickering brynpickering changed the title Gurobi backend interface Feature: Gurobi backend interface Jul 1, 2024
Copy link
Contributor

@irm-codebase irm-codebase left a comment

Choose a reason for hiding this comment

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

Fairly big review. I tried to be thorough since this is an important change.
"Nice to haves" are just that, consider ignoring them if they result in too much work.

Also, I expect that the recent changes to the backend handling and the linter will trigger errors once you try to merge this into main.

docs/contributing.md Outdated Show resolved Hide resolved
docs/migrating.md Outdated Show resolved Hide resolved
requirements/base.txt Outdated Show resolved Hide resolved
src/calliope/backend/backend_model.py Outdated Show resolved Hide resolved
src/calliope/backend/backend_model.py Outdated Show resolved Hide resolved
tests/test_backend_general.py Outdated Show resolved Hide resolved
tests/test_backend_general.py Show resolved Hide resolved
from .common.util import build_test_model as build_model
from .common.util import check_error_or_warning

if importlib.util.find_spec("gurobipy") is not None:
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't we just add the solver to our dev requirements to avoid this one?

Avoiding it for the base requirements makes sense. For dev, not so much, imo.

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 tried, but it won't work. Our dependencies are currently pip and conda cross-compatible. On PyPI, this dependency is gurobipy, on Anaconda it is gurobi... So, this breaks our compatibility. I've still removed the check in the tests for gurobipy. I would prefer to alert the developer to the need to install gurobipy!

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm... in the future we might want to switch to pixi to avoid this kind of stuff. It prefers conda-forge by default, but you can redirect dependencies in a case-by-case basis if needed...
I'm ok with leaving this as is for now though!

tests/test_backend_gurobi.py Outdated Show resolved Hide resolved
tests/test_core_model.py Outdated Show resolved Hide resolved
@brynpickering
Copy link
Member Author

Thanks for the review @irm-codebase - updated!

Copy link
Contributor

@irm-codebase irm-codebase left a comment

Choose a reason for hiding this comment

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

Very happy with the changes!
I've added a couple of things to clarify. After that, I can approve.

@@ -39,6 +39,16 @@ To test your model pipeline, `config.init.time_subset` is a good way to limit yo

## Deep-dive into some key configuration options

### `config.build.backend`

By default, the optimisation problem is built using the [pyomo][] library.
Copy link
Contributor

Choose a reason for hiding this comment

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

Broken pyomo link?

src/calliope/backend/backend_model.py Show resolved Hide resolved
Copy link
Contributor

@irm-codebase irm-codebase left a comment

Choose a reason for hiding this comment

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

Great, thank you for the changes @brynpickering!

@irm-codebase irm-codebase self-requested a review July 9, 2024 09:17
irm-codebase
irm-codebase previously approved these changes Jul 9, 2024
Copy link
Contributor

@irm-codebase irm-codebase left a comment

Choose a reason for hiding this comment

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

I was going to send this PR back because pytest failed for some tests (due to some duplicated warning messages)... but re-making my mamba environment fixed this.

I think we should re-think our approach to dependencies, though, because following your instructions also leads to failures due to the lack of cbc solver, but that's been there for a while so it does not relate to this PR.

@brynpickering
Copy link
Member Author

We can't provide generalised instructions for cbc in the installation guide due to Windows users not being able to install it directly (and pixi wouldn't solve that). I would rather move to using HiGHs as our default open source solver, but I haven't got it to work with the pyomo kernel library...

sjpfenninger
sjpfenninger previously approved these changes Jul 9, 2024
Copy link
Member

@sjpfenninger sjpfenninger left a comment

Choose a reason for hiding this comment

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

Since I had looked at this previously and Ivan did a detailed review I'm only making some minor suggestions for the documentation. Good to go!

docs/advanced/backend_choice.md Outdated Show resolved Hide resolved
docs/advanced/backend_choice.md Outdated Show resolved Hide resolved
docs/advanced/backend_choice.md Outdated Show resolved Hide resolved
@brynpickering brynpickering dismissed stale reviews from sjpfenninger and irm-codebase via 59d6e19 July 9, 2024 15:39
Co-authored-by: Stefan Pfenninger <stefan@pfenninger.org>
@brynpickering brynpickering merged commit 61e1afa into main Jul 9, 2024
9 of 10 checks passed
@brynpickering brynpickering deleted the feature-gurobi-interface branch July 9, 2024 15:41
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.

Pyomo v6.7.2 regression: Gurobi Python interface not found Gurobi solver interfaces
3 participants