-
Notifications
You must be signed in to change notification settings - Fork 5
Piecewise linear basin profile approx in allocation #2108
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: allocation_feature
Are you sure you want to change the base?
Piecewise linear basin profile approx in allocation #2108
Conversation
Here's a bit of theoretical background to the goal of this PR. For each basin, a table is given with levels and associated areas: Then to get the volume (storage) of water at some level giving a piecewise quadratic storage - level relationship. This is the relationship we want to approximate linearly. In the code we actually use the inverse of this relationship ( The level can also increase above the last level |
Hi @SouthEndMusic, Originally, the objective function of this problem is quadratic, and this pull request introduces binary decision variables, so your problem becomes a mixed-integer quadratic one. Unfortunately, there are no very good open source solvers for this type of discrete nonlinear optimization problem. In particular, HiGHS cannot solve QP models where some of the variables must take integer values. You could try other free solvers like bonmin, but the solution will probably not be very fast. Other alternatives, as discussed in our meeting last week, would be 1) to convert the objective function into a linear one (this is probably a no regret action), 2) to try to exploit the problem structure to avoid the use of binary variables (this will require a bit more investigation, and is not guaranteed to be achievable), or 3) try to solve the problem in stages, e.g. by first solving a simplified version with linear storage level tables, and then using that initial solution to solve a more detailed model with the piecewise storage curve representation (this staged approach will require some effort to set up). I would suggest trying first action 1), implemented as an option, so that the user can choose between the quadratic or linear objective. We could discuss this approach further and refine what has been suggested here. |
It makes sense to try to use a linear objective function. I'd prefer to not support multiple objective functions unless there is a good reason for it, to keep the maintenance lower. |
@jarsarasty thanks for your reply. I'll start implementing the linear objective function you suggested. |
The current implementation of the storage level relationship is like this: Say for basin
Then we define the constraints:
Now that I write this out, I doesn't make that much sense to me anymore. It seems that I only need 1 or 2 auxiliary variables to define the linear combination of the successive storage and level data. But then still I end up with the product of the binary variable and auxiliary variable. Asking Claude about this, it seems that what I should do is simply have and then make sure that only 2 consecutive auxiliary variables are non-zero by and |
Hi @SouthEndMusic, Indeed, the product of decision variables in the optimization model should be avoided because it introduces nonlinearity, which can significantly complicate the problem and make it harder to solve. |
@jarsarasty this package looks very interesting for constructing linear approximations: |
Fixes #2221 once and for all 🤞🏻
Bumps [prefix-dev/setup-pixi](https://github.com/prefix-dev/setup-pixi) from 0.8.4 to 0.8.5. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/prefix-dev/setup-pixi/releases">prefix-dev/setup-pixi's releases</a>.</em></p> <blockquote> <h2>v0.8.5</h2> <!-- raw HTML omitted --> <h2>What's Changed</h2> <h3>🐛 Bug fixes</h3> <ul> <li>fix: Support TOML 1.0 for pyproject.toml parsing by <a href="https://github.com/pavelzw"><code>@pavelzw</code></a> in <a href="https://redirect.github.com/prefix-dev/setup-pixi/pull/191">prefix-dev/setup-pixi#191</a></li> </ul> <h3>⬆️ Dependency updates</h3> <ul> <li>Bump the gh-actions group with 3 updates by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/prefix-dev/setup-pixi/pull/188">prefix-dev/setup-pixi#188</a></li> <li>Bump the nodejs group with 6 updates by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/prefix-dev/setup-pixi/pull/187">prefix-dev/setup-pixi#187</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/prefix-dev/setup-pixi/compare/v0.8.4...v0.8.5">https://github.com/prefix-dev/setup-pixi/compare/v0.8.4...v0.8.5</a></p> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/prefix-dev/setup-pixi/commit/dbaed5efa0bd33d6bd6d162bec8a06d8fc43c807"><code>dbaed5e</code></a> fix: Support TOML 1.0 for pyproject.toml parsing (<a href="https://redirect.github.com/prefix-dev/setup-pixi/issues/191">#191</a>)</li> <li><a href="https://github.com/prefix-dev/setup-pixi/commit/30d4899438de90430e40752205f9bb6f67939dab"><code>30d4899</code></a> Bump the nodejs group with 6 updates (<a href="https://redirect.github.com/prefix-dev/setup-pixi/issues/187">#187</a>)</li> <li><a href="https://github.com/prefix-dev/setup-pixi/commit/00ff9955aafc3069179be56a98a299a34332d59b"><code>00ff995</code></a> Bump the gh-actions group with 3 updates (<a href="https://redirect.github.com/prefix-dev/setup-pixi/issues/188">#188</a>)</li> <li>See full diff in <a href="https://github.com/prefix-dev/setup-pixi/compare/v0.8.4...v0.8.5">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Continuing from #2224: ``` 18.086399 seconds (4.01 M allocations: 3.255 GiB, 2.81% gc time) 17.879659 seconds (2.95 M allocations: 2.949 GiB, 1.69% gc time) ``` Adding the type assert on the function does the trick. I also go rid of the `isdae` check because it shouldn't be needed, and I noticed `get_dae_uprev` leads to runtime dispatch since it is using `isdefined(integrator, ...)` There are still more optimizations to be made to backtracking, but good to bag this one already. I also noticed many extra allocations if `concentration = true`, we should also look at that.
As it's currently [broken](https://ribasim.org/reference/node/outlet.html) showing `$^3/$ `
Fixes #2060.
An additional complexity here is that because we change the way we represent basins in the allocation problems, level demand has to be reformulated as well. The previous approach was to have a 'buffer' attached to basins with a level demand which acted as a source when the level in the basin was too high, and as a
UserDemand
when the level in the basin was too low. This source or demand was computed with data from the physical layer.This PR introduces the level of each basin explicitly as a variable in the optimization problems. Therefore we can optimize for the level directly. The error for other demand nodes are still formulated in terms of flow though, so the error for level demand should be formulated that way too, so all terms are treated the same way in the loss function. Therefore we formulate the error term for the level demand as
where$L_\text{end}$ is the level of a particular basin at the end of the allocation timestep.
One thing to properly think trough here is what the behavior of a basin with a level demand should be when optimizing for a different priority than the priority of the level demand. I would say intuitively: for priorities higher than the level demand, all storage of the basin should be available as a source, and for the priority of the level demand and lower priorities only the disk of water above the maximum tolerated level should be available, if applicable.