Giter VIP home page Giter VIP logo

energy-py-linear's Introduction

energy-py-linear

Checked with mypy


Documentation: energypylinear.adgefficiency.com


A Python library for optimizing energy assets with mixed-integer linear programming:

  • electric batteries,
  • combined heat & power (CHP) generators,
  • electric vehicle smart charging,
  • heat pumps,
  • renewable (wind & solar) generators.

Assets & sites can be optimized to either maximize profit or minimize carbon emissions, or a user defined custom objective function.

Energy balances are performed on electricity, high, and low temperature heat.

Setup

Requires Python 3.10+:

$ pip install energypylinear

Quick Start

Asset API

The asset API allows optimizing a single asset at once:

import energypylinear as epl

#  2.0 MW, 4.0 MWh battery
asset = epl.Battery(
    power_mw=2,
    capacity_mwh=4,
    efficiency_pct=0.9,
  electricity_prices=[100.0, 50, 200, -100, 0, 200, 100, -100],
  export_electricity_prices=40
)

simulation = asset.optimize()

Site API

The site API allows optimizing multiple assets together:

import energypylinear as epl

assets = [
    #  2.0 MW, 4.0 MWh battery
    epl.Battery(
        power_mw=2.0,
        capacity_mwh=4.0
    ),
    #  30 MW open cycle generator
    epl.CHP(
        electric_power_max_mw=100,
        electric_power_min_mw=30,
        electric_efficiency_pct=0.4
    ),
    #  2 EV chargers & 4 charge events
    epl.EVs(
        chargers_power_mw=[100, 100],
        charge_events_capacity_mwh=[50, 100, 30, 40],
        charge_events=[
            [1, 0, 0, 0, 0],
            [0, 1, 1, 1, 0],
            [0, 0, 0, 1, 1],
            [0, 1, 0, 0, 0],
        ],
    ),
    #  natural gas boiler to generate high temperature heat
    epl.Boiler(),
    #  valve to generate low temperature heat from high temperature heat
    epl.Valve()
]

site = epl.Site(
  assets=assets,
  electricity_prices=[100, 50, 200, -100, 0],
  high_temperature_load_mwh=[105, 110, 120, 110, 105],
  low_temperature_load_mwh=[105, 110, 120, 110, 105]
)

simulation = site.optimize()

Documentation

See more asset types & use cases in the documentation.

Test

$ make test

energy-py-linear's People

Contributors

adgefficiency avatar ben-freke avatar tomharvey avatar tonyfresko avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

energy-py-linear's Issues

to add minimum SOC of battery

It would be reasonable to add minimum SOC to the battery model, at least with home batteries one is not recommended to fully discharge.

License

I am interested in using this project, but not clear what the license is. Could you add a license? Thank you.

Contourpy / Visual Basic

Hello,

I am trying to install this package and seems there is an issue with contourpy package and its build process. The error message indicates that there is a problem parsing the output of vswhere.exe.
I am on Python3.10 and have just installed Visual Basic (didn't have it before).

Thanks.

Battery efficiency?

I feed in some simple prices and simple battery setup and I'm confused about the output.

I'm using prices of [100, 200] so I expect the optimal battery profile would be to import at full capacity (1MW) in the first period and then export at full capacity in the second period.

Instead, see below, it's importing at 1MW in period[0] but exporting at 0.90909091MW in period[1]. Is there an inefficiency being modelled in the battery?

Finally, the charge state at the end is 0.0 - which is either 10% loss of efficiency??

>>> import energypylinear as epl
>>> model = epl.Battery(power=1, capacity=2)
>>> info = model.optimize([100, 200, 0])
>>> pprint(info)
[{'Actual [$/5min]': 8.333333333333334,
  'Charge [MWh]': 0.0,
  'Export [MW]': 0.0,
  'Forecast [$/5min]': 8.333333333333334,
  'Forecast [$/MWh]': 100,
  'Gross [MW]': 1.0,
  'Import [MW]': 1.0,
  'Losses [MW]': 0.0,
  'Net [MW]': 1.0,
  'Prices [$/MWh]': 100},
 {'Actual [$/5min]': -13.63636365,
  'Charge [MWh]': 0.083333333,
  'Export [MW]': 0.90909091,
  'Forecast [$/5min]': -13.63636365,
  'Forecast [$/MWh]': 200,
  'Gross [MW]': -0.90909091,
  'Import [MW]': 0.0,
  'Losses [MW]': 0.090909091,
  'Net [MW]': -0.818181819,
  'Prices [$/MWh]': 200},
 {'Actual [$/5min]': None,
  'Charge [MWh]': 0.0,
  'Export [MW]': None,
  'Forecast [$/5min]': None,
  'Forecast [$/MWh]': 0,
  'Gross [MW]': None,
  'Import [MW]': None,
  'Losses [MW]': None,
  'Net [MW]': None,
  'Prices [$/MWh]': 0}]

Error trying to use forecasts in optimisation

Following along with the README and I get:

info = model.optimize(prices, forecast=forecasts, initial_charge=4)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: optimize() got an unexpected keyword argument 'forecast'

Power constraint

I may miss something, but I feel like the power constraint are not considered in the current version.
I would add these constraints:
self.prob += imports[i] <= self.power
self.prob += export[i] - losses[i] <= self.power

Proposed enhancement to output structure

So, the current output is an ordered list of Dicts. In this list, objects [0:-2] describe the periods; the starting charge, the import/export operation throughout the period and so on. Then the final object in the list is largely null as it doesn't have import/export throughout the period, and just describes the end state of the battery.

It might be more useful if:

  1. The operations and the end state were called out into separate dictionaries
  2. The operations were indexed/keyed by the period ID

This would result in something like (some keys removed for brevity, but the structure is there):

{
    "operations": {
        "0": {
            "Import [MW]": 2.0,
            "Export [MW]": 0.0,
            "Charge [MWh]": 0.0
        },
        "1": {
            "Import [MW]": 0.0,
            "Export [MW]": 2.0,
            "Charge [MWh]": 1.0
        }

    },
    "final-state": {
        "Charge [MWh]": 0.0
    }
}

This allows for a more explicit expression of what each part of the output is, and allows for extension of the output without breaking backwards compatibility. For example, the summary could be extended to contain price $/MWh across the whole run, or each operational period could have additional values like DateTimeZone if actual times are being used. Or, the output could also be extended to output something other than a final-state and a list of operations, maybe confirmation of the Battery config, or ...

The point is, enclosing output into a named dictionary key, allows for this expansion without breaking existing setups.

Set constraint to limit number of battery cycles

Hello

My name is Andreas Slyngstad, and I am a software developer, currently working on a business case for batteries in Germany.

The business case is based around price arbitrage, and of the limits for this business case is number of battery cycles performed in a given simulation period.
Here, a battery cycle includes discharge -> charge for some predefined limit of depth of discharge.

My question is:
Is there any possible way to enforce the MILP solver constrain the problem to only be able to do
a maximum of n number of cycles, lets say max 15 for some simulation period.

Example Battery specs

  • Power/Energy 1MW/2MWh
  • Efficiency: 98%

If possible (but not critical), is there any way to set a min/max range of SOC (state of charge) ?
Lets say state of charge can be in the range of 10% - 90%.

Best regards

Andreas Slyngstad

SOC not calculated for battery asset

Hello,

I am trying to calculate battery dispatch simulations for my battery and I notice some problems with the SOC calculations. After carrying out the calculations for yearly data I see that SOC column is always empty. And when I calculate it myself I see that SOC reaches over the batteries maximum capacity. Wouldn't it be best for the battery to work in a daily format? Where the batteries capacity at the end of the day should be at 10% SOC.

And I have encountered rows where charging and discharging occurs, but if battery is charging at full capacity of the inverter then there is no room for the battery to discharge. (uploading example screenshot) The battery is 4 MWh capacity and the power is 3.2MW initial and final charge variables are set to 0) But in the battery object I input 3.6 MWh capacity, because it is stated by the producers of the battery that it should never reach below 10% SOC levels. So after the calculations I add to the SOC column 0.4 MWh thus achieving full battery charge.

image

Forecast length does not match the Price length

When feeding in a forecast as well as a price we get an assertion error:

>>> import energypylinear as epl
>>> model = epl.Battery(power=2, capacity=4)
>>> prices = [10, 50, 10, 50, 10]
>>> forecasts = [10, 50, 10, 50, 10]
>>> model.optimize(prices, timestep='30min', forecasts=forecasts)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/thomasharvey/.pyenv/versions/linear_value/lib/python3.6/site-packages/energypylinear/battery/battery.py", line 87, in optimize
    assert len(forecasts) == len(prices)
AssertionError

Wrapt and meson-python Intallation issue

At download and install with pip git+, I get two errors, I think related to contourpy (I work on Python 3.10):

  1. ERROR: could not register module: No module named 'wrapt', even though I have wrapt==0.15.0 installed
  2. meson-python: error: Could not find meson version 0.63.3 or newer, found pip_system_certs: ERROR: could not register module: No module named 'wrapt'
    meson-python: error: 1.2.1
    meson-python: error:

, even though I have meson-python==0.12.0 and meson==1.2.1 installed

Do you have any suggestions?

Failing tests

I'm now seeing failures in test_power_capacity_initial_charge():

When input params are [2, 4, 0]:

(Pdb) gross
[2.0, 2.0, 1.4545455, 2.0, 2.0, 2.0]
(Pdb) net
[2.0, 2.0, 1.60000005, 2.2, 2.0, 2.2]

And in test_batt_efficiency.

E       First has 1, Second has 0:  -1.0
E       First has 0, Second has 1:  -0.33333334000000003

Are you seeing failures as well - these are fairly new tests. I'd dig into it, but I think it's in the workings of the solution.

Operation count does not match price periods

If I input prices of [100, 200] I expect the results to inform me of how the battery should operate in each of these 2 price periods.

Instead, it shows one period of valid import/export instruction and then the end state with summary of end charge and null import/export.

If I add a third price, I get the expected results for the 2 periods and the results are the same regardless of the 3rd price fed into the solver.

To add cost of battery charge/discharge cycle

I am trying to apply the library for home battery optimization scenario. To take hourly prices of electricity and get charge/discharge schedule.

It would be reasonable to include cost of charge/discharge cycle to optimization model. So the model would not charge/discharge if the cost of cycle would be higher than the profit from charge.discharge.

Logger attempts to make directory even if file logging set to false

I'm running energy-py-linear in a container with a read-only file system. I've noticed that the library fails in a read-only file system because of this section of code:

pathlib.Path("logs").mkdir(exist_ok=True)

Current Behaviour

When using a read-only file system, the library fails as it attemps to create a directory called logs even when enable_file_logging is set to false.

Suggested Fix

Move the pathlib.Path("logs").mkdir(exist_ok=True) line into the conditional for enable_file_logging.

Add Custom Objective Function Terms into `epl.Accounts`

@johnfeldhausen - I've started work on bringing the custom objective function terms into epl.Accounts here #65.

The initial idea is something like:

simulation = site.optimize(verbose=4, objective={"terms": terms})
accounts = epl.get_accounts(simulation.results, terms=terms)
print(accounts.custom.cost)

Will see how it goes!

Python 3.11 compatibility

pip install throws with Python 3.11 throws an error:

0.1.0 Requires-Python >=3.10, <3.11

Is the code really dependent on such narrow Python versioning?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.