Python library for PV and battery energy-system simulation and optimization
Project description
BREOS - Building Renewable Energy Optimization Software
A Python library for PV and battery energy-system simulation and optimization, designed for research and engineering applications.
Features
- Weather data: Fetch TMY data from PVGIS/NSRDB and historical data from Open-Meteo. Support for hourly and 15-minute resolutions with Makima interpolation.
- PV production: DC and AC power calculations using pvlib, with built-in module database and inverter presets.
- Battery simulation: Energy balance with calendar and cycle aging models (Naumann 2020, Lam 2025) and field-calibrated LFP parameters. Optional approximate Numba kernels for fast standalone screening studies.
- Economics: NPV, LCOE, breakeven analysis, and cost projections with configurable tariffs and inflation.
- Optimization: Multi-objective (grid independence, NPV, ZEB ratio) system sizing using pymoo (NSGA-II). Tilt/azimuth optimization via grid search or Brent's method.
- Emissions: CO2 savings calculations and projections.
- Visualization: Publication-ready plots for energy balances, degradation, breakeven, Pareto fronts, and more.
- Load profiles: Bundled demandlib-derived H0 examples, plus support for user-supplied BDEW, E-REDES, REE, and custom profiles.
Installation
pip install breos
Or with uv:
uv add breos # as a project dependency
uvx breos --version # run the CLI without installing
Optional feature groups keep the default install focused on core PV + battery simulation:
pip install "breos[plots]" # publication plots
pip install "breos[optimization]" # pymoo multi-objective sizing
pip install "breos[weather]" # Open-Meteo historical weather fetching
pip install "breos[fast]" # approximate Numba screening kernels (not used by App)
pip install "breos[validation]" # Excel / Arrow validation workflows
To install from source instead:
git clone https://github.com/Str4vinci/breos.git
cd breos
pip install -e .
Quick Start
import breos
app = breos.App({
"location": "porto", # preset or {"latitude": ..., "longitude": ..., "timezone": ...}
"n_modules": 10,
"annual_consumption_kwh": 4000,
"battery_kwh": 5.0, # 0 for no battery
"cost_preset": "residential_pt",
"emissions_country": "PT",
})
app.simulate()
result = app.result()
print(f"Grid independence: {result['grid_independence_pct']:.1f}%")
print(f"Payback: {result['payback_year']} years")
print(f"NPV savings: {result['npv_savings_eur']:,.0f} EUR")
print(f"CO2 avoided: {result['co2_avoided_total_kg']:,.0f} kg")
result() returns a plain Python dict (JSON-serializable, no pandas). See Configuration for all options.
For real studies, bring your own weather/API access where required, licensed load profiles, PV module/system data, and cost/tariff assumptions. The packaged defaults are intended to make the tool runnable, not to certify a project.
Command Line
Run a simulation without writing Python:
breos run \
--location porto \
--n-modules 10 \
--annual-consumption-kwh 4000 \
--battery-kwh 5.0 \
--cost-preset residential-pt \
--emissions-country pt \
--output result.json
The CLI writes the same JSON-serializable result returned by App.result().
You can also pass a TOML or JSON config file:
breos run --config configs/examples/quickstart.toml --output result.json
Inspect a config before running the full simulation:
breos validate-config configs/examples/quickstart.toml
breos run --config configs/examples/quickstart.toml --dry-run
Discover packaged option keys:
breos list locations
breos list modules
breos list cost-presets
breos list emissions
breos list load-profiles
For non-bundled RLPs, put licensed CSVs in a local directory and pass it through config or flags:
breos run --config configs/examples/external-rlp.toml --rlp-directory external_rlp
Configuration
All keys except location, annual_consumption_kwh, and either n_modules or pv_arrays are optional with sensible defaults.
| Key | Default | Description |
|---|---|---|
location |
required | Preset key ("porto", "berlin", ...) or dict with latitude, longitude, timezone |
n_modules |
required unless pv_arrays is set |
Number of PV modules |
pv_arrays |
None |
Optional list of arrays with modules, module, tilt, and azimuth; when present, the array module total overrides n_modules |
annual_consumption_kwh |
required | Annual electricity demand (kWh) |
battery_kwh |
0.0 |
Battery capacity (0 = no battery) |
pv_module |
None |
Module name from catalogue (None = default) |
load_profile |
"1" |
Bundled demandlib-derived H0 profile. Other standard profiles require caller-supplied CSVs |
rlp_directory |
None |
Directory containing licensed external RLP CSVs for non-bundled load profiles |
tilt |
auto | Tilt angle in degrees (auto-estimated from latitude) |
azimuth |
auto | Surface azimuth (auto: 180 for northern hemisphere) |
tracking |
"fixed" |
Tracking mode ("fixed", "single_axis", or "dual_axis") |
axis_tilt |
0.0 |
Single-axis tracker axis tilt |
axis_azimuth |
auto | Tracker axis azimuth (auto from latitude) |
max_angle |
60.0 |
Single-axis tracker maximum rotation angle |
backtrack |
True |
Whether single-axis trackers backtrack to avoid row shading |
gcr |
0.35 |
Ground coverage ratio for single-axis tracking |
cross_axis_tilt |
0.0 |
Cross-axis terrain slope for single-axis tracking |
dual_axis_max_tilt |
90.0 |
Maximum panel tilt for dual-axis tracking |
resolution |
"h" |
Time resolution ("h" or "15min") |
projection_years |
20 |
Economic projection horizon |
cost_preset |
None |
Cost preset key from packaged defaults; editable examples live in configs/base/ |
inflation_rate |
0.02 |
Annual electricity price inflation |
discount_rate |
0.03 |
Discount rate for NPV calculations |
emissions_country |
None |
Country code for CO2 calculations ("PT", "DE", "ES", ...) |
pv_degradation_rate |
0.005 |
Annual PV degradation (0.5%) |
calendar_model |
"naumann_lam_field_calibrated" |
Battery calendar aging model |
battery_min_soc |
0.10 |
Battery SOC floor (fraction of usable capacity) |
battery_max_soc |
0.90 |
Battery SOC ceiling |
battery_eol_percentage |
0.70 |
SOH fraction that triggers battery replacement |
battery_rte |
None |
Battery round-trip efficiency, split evenly across charge/discharge (None = 0.95) |
dc_coupled |
True |
DC-coupled / hybrid inverter |
inverter_efficiency |
0.96 |
Inverter efficiency |
inverter_loading_ratio |
1.25 |
DC/AC oversizing ratio; also sets the inverter AC rating that clips production |
pv_loss_overrides |
None |
Per-component overrides (percent) for the fixed PVWatts system losses, e.g. {"shading": 0.0} |
start_date |
"2023-01-01" |
First simulation date |
Modeling conventions
- System losses: every DC production calculation applies pvlib's PVWatts
losses with BREOS defaults of soiling 2%, shading 3%, mismatch 2%, wiring 2%,
connections 0.5%, LID 1.5%, nameplate 1%, and availability 3% — about 14.1%
combined (
breos.solar.DEFAULT_PVWATTS_LOSSES). Age-based degradation is added separately per simulation year. Override individual components withpv_loss_overrides(App) orloss_overrides(solar functions). - Inverter: the energy balance applies a flat
inverter_efficiencyand clips AC output (PV and battery discharge combined) at the inverter rating implied byinverter_loading_ratio— the same rating used for inverter CAPEX. DC surplus above the rating can still charge a DC-coupled battery.
Result
app.result() returns a dict with:
| Key | Description |
|---|---|
pv_production_kwh |
Year 1 PV production |
grid_independence_pct |
Year 1 grid independence (%) |
self_consumption_pct |
Year 1 self-consumption ratio (%) |
total_investment_eur |
Total CAPEX |
payback_year |
Payback year (None if not reached) |
npv_savings_eur |
NPV savings over projection period |
lcoe_eur_kwh |
Levelized cost of electricity |
co2_avoided_year1_kg |
Year 1 CO2 avoided |
co2_avoided_total_kg |
Lifetime CO2 avoided |
battery_soh_end_pct |
Battery state of health at end (if battery) |
monthly |
Year 1 monthly balance rows for PV, load, imports, exports, and self-consumption |
financial |
Yearly financial projection rows, including year 0 investment |
yearly |
List of per-year dicts with detailed breakdown |
Multi-array PV systems
Use pv_arrays when a roof has panels on different faces or orientations:
app = breos.App({
"location": "porto",
"annual_consumption_kwh": 4000,
"pv_arrays": [
{"modules": 8, "module": "Erlangen_445W", "tilt": 10, "azimuth": 90},
{"modules": 8, "module": "Erlangen_445W", "tilt": 10, "azimuth": 270},
],
})
app.simulate()
BREOS calculates production per array and combines the DC output before the energy balance, so east-west and pitched-roof layouts are not collapsed into a single representative tilt/azimuth.
Advanced Usage
For full control over individual simulation steps, use the lower-level modules directly:
from breos.weather import fetch_tmy_weather_data
from breos.solar import calculate_pv_production_dc, PVModuleParams
from breos.battery import simulate_energy_balance, BatteryConfig
from breos.load_profiles import load_profile
from breos.economics import calculate_costs, cost_analysis_projection
from pvlib.location import Location
# Each module can be used independently
weather, metadata = fetch_tmy_weather_data(41.15, -8.63)
location = Location(41.15, -8.63, tz='Europe/Lisbon')
pv_dc = calculate_pv_production_dc(weather, location, tilt=35, surface_azimuth=180, n_modules=10)
# ...
Additional Capabilities
BREOS is the open-source core of a broader simulation platform developed as part of PhD research. Additional features not included in this release:
- Time-of-Use (TOU) tariff optimization with multi-period pricing and strategy comparison
- Vehicle-to-Home (V2H) simulation with EV scheduling and bidirectional charging
- Multi-chemistry battery support — Sodium-ion (SIB), Vanadium Redox Flow (VRFB), Solid-State (SSB)
- Thermal energy storage (TES) with phase-change material modeling
- Heat pump integration with COP modeling and coupled electro-thermal energy balance
- Community Self-Consumption (CSC) modeling for multi-building scenarios
These modules may be released in the future or are available for academic collaboration upon request.
Weather Data Note
BREOS uses Open-Meteo for historical weather data. Open-Meteo is free for non-commercial use. For commercial applications, please review their pricing and terms.
Two working-directory conventions to be aware of:
- A
weather/directory in the current working directory is scanned before any PVGIS fetch — a file matching the location preset name is used silently instead of fetching. Remove or rename it to force a fresh fetch. - Historical Open-Meteo fetches cache responses in a
.cache.sqlitefile in the current working directory (30-day expiry).
Library modules report progress (file discovery, saved files, conversions)
through the standard logging module under the breos.* logger names —
enable them with logging.basicConfig(level=logging.INFO) or silence them
per module. Functions with a verbose flag still print to stdout when asked.
Load Profile Data Note
The public package bundles only demandlib-derived H0 example profiles. E-REDES, REE, and direct BDEW CSVs are supported as user-provided files through rlp_directory, but are not redistributed in this repository because their public source terms do not clearly grant package redistribution rights. See ATTRIBUTIONS.md and docs/legal/load-profile-data.md.
Resources
See docs/resources.md for links to PV modelling references, RLP sources, weather/solar-resource APIs, and input assumptions to record.
Citation
If you use BREOS in your research, please cite:
@software{breos,
author = {Rodrigues, Leonardo},
title = {BREOS: Building Renewable Energy Optimization Software},
year = {2026},
url = {https://github.com/Str4vinci/breos}
}
Roadmap
See ROADMAP.md for planned architectural work and capability extensions.
Contributing
See CONTRIBUTING.md for guidelines.
BREOS uses develop as the default development branch. Feature work should be
done on separate branches and opened as pull requests into develop.
The main branch tracks stable releases only. Use main or the GitHub Releases
page when you want the latest stable version.
Contact
For questions, collaboration, or access to additional modules, reach out at lrodrigues@fe.up.pt.
License
BSD 3-Clause License. See LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file breos-0.3.0rc1.tar.gz.
File metadata
- Download URL: breos-0.3.0rc1.tar.gz
- Upload date:
- Size: 854.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9e9af218532cb9acbdedca78fa0f62b497baac579686335487ebf74296698e27
|
|
| MD5 |
c0b1b7a3838a0f4c9445a31c6cad5b4b
|
|
| BLAKE2b-256 |
fe080ad3b988c86d46c0d5cded4466576a3a64bbfdfe7a39b78fa44f08dfc93f
|
File details
Details for the file breos-0.3.0rc1-py3-none-any.whl.
File metadata
- Download URL: breos-0.3.0rc1-py3-none-any.whl
- Upload date:
- Size: 644.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
76e05a55dc35030fcc8983cfded729e3448502df34d7581aa63086511b30a836
|
|
| MD5 |
3a6f54a43e783a2444c9d428aaf1e363
|
|
| BLAKE2b-256 |
5e4cfb28db42a5ce60daf206923a92729eea6711e2613f48d8e3b7fa1260a9ce
|