Skip to main content

Python client for Site-Calc operational optimization (day-ahead bidding, ANS)

Project description

Site-Calc Operational Client

Python client for Site-Calc operational optimization API - day-ahead bidding and short-term dispatch with ancillary services.

Installation

Note: the package is not yet published to PyPI. Install from the GitHub source until the first PyPI release.

# From source (recommended while v1.x is unreleased on PyPI):
pip install git+https://github.com/stranma/site-calc-operational.git

# Or, for local development inside this repo:
git clone https://github.com/stranma/site-calc-operational.git
cd site-calc-operational
pip install -e ".[dev]"

Quick Start

from datetime import date
from site_calc_operational import OperationalClient
from site_calc_operational.models import (
    TimeSpan, Resolution, Site, Battery, ElectricityImport,
    OptimalBiddingRequest, MarketForecasts, OptimizationConfig
)

# Initialize client
client = OperationalClient(
    base_url="https://api.site-calc.example.com",
    api_key="op_your_api_key_here"
)

# Create 1-day optimization at 15-minute resolution
timespan = TimeSpan.for_day(date(2025, 11, 6), Resolution.MINUTES_15)

# Define devices
battery = Battery(
    name="Battery1",
    properties={
        "capacity": 10.0,
        "max_power": 5.0,
        "efficiency": 0.90,
        "initial_soc": 0.5
    },
    ancillary_services={
        "afrr_plus": {
            "can_provide": [1, 1, 1, 1, 1, 1],
            "expected_activation_profit": [80.0, 85.0, 88.0, 90.0, 87.0, 82.0]
        }
    }
)

grid_import = ElectricityImport(
    name="GridImport",
    properties={"price": [30.0]*96, "max_import": 8.0}
)

site = Site(site_id="my_site", devices=[battery, grid_import])

# Market forecasts
forecasts = MarketForecasts(
    ancillary_services={
        "afrr_plus": {
            "max_accepted_price_forecast": [15.0, 18.0, 22.0, 25.0, 23.0, 16.0],
            "period_duration_hours": 4
        }
    }
)

# Create and submit optimization request
request = OptimalBiddingRequest(
    sites=[site],
    timespan=timespan,
    market_forecasts=forecasts,
    optimization_config=OptimizationConfig(max_bid_steps=10)
)

job = client.create_optimal_bidding_job(request)
result = client.wait_for_completion(job.job_id, timeout=600)

# Display results
print(f"Total Profit: €{result.summary.expected_profit:,.2f}")
print(f"DA Revenue: €{result.summary.total_da_revenue:,.2f}")
print(f"ANS Revenue: €{result.summary.total_ancillary_revenue:,.2f}")

Features

  • ✅ Day-ahead market bidding optimization
  • ✅ Ancillary services (aFRR, mFRR) optimization
  • ✅ Multi-step bid curve generation
  • ✅ Device scheduling with constraints
  • ✅ 15-minute or 1-hour resolution
  • ✅ Multi-site optimization
  • ✅ Type-safe Pydantic models
  • ✅ Automatic retry and error handling

Capabilities

Feature Value
Max Horizon 296 intervals (~3 days at 15-min)
Resolution 15-minute or 1-hour
ANS Support Yes (aFRR±, mFRR±)
Binary Variables Yes (CHP on/off)
Timeout 300 seconds

Supported Devices

  • Battery (with ANS capabilities)
  • CHP - Combined Heat and Power (with ANS capabilities)
  • Heat Accumulator
  • Photovoltaic
  • Heat Demand
  • Electricity Demand
  • Electricity Import/Export (market interface)
  • Gas Import (market interface)
  • Heat Export (market interface)

Documentation

Full documentation available at: https://docs.site-calc.example.com/operational-client

Examples

See examples/ directory for complete examples:

  • optimal_bidding.py - Complete optimal bidding workflow
  • device_planning.py - Device scheduling with locked ANS reservations
  • multi_site.py - Multi-site optimization

Requirements

  • Python ≥ 3.10
  • API key with op_ prefix (operational client)

Development

# Install with dev dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Format code
ruff format .

# Type check
mypy site_calc_operational

On-prem (sync) client

For self-hosted deployments running server-onprem:

from site_calc_operational import OnPremClient

with OnPremClient(base_url="https://onprem.example.com", api_key="op_...") as client:
    health = client.health()
    print(health.site_calc_version)
    result = client.device_planning(request_payload)
    print(result["summary"])

The on-prem client is sync (no polling). For the SaaS server, keep using OperationalClient (async).

MCP server (LLM-driven scenario building)

The package ships an optional Model Context Protocol server that exposes 17 tools for building and submitting operational device-planning scenarios from an LLM (Claude Desktop, ChatGPT, Cursor, etc.). It wraps OnPremClient and runs locally on the user's machine, so the LLM can persist generated profile data to disk and reference it in subsequent tool calls.

Install

# From source with the MCP extra:
pip install "site-calc-operational[mcp] @ git+https://github.com/stranma/site-calc-operational.git"

# Or in this checkout:
pip install -e ".[mcp]"

The extra pulls fastmcp>=2.0. Once installed, the package exposes both a console script (site-calc-operational-mcp) and a module entry point (python -m site_calc_operational.mcp).

Configure the MCP client

Set environment variables so the server can reach your on-prem deployment:

export SITE_CALC_OPERATIONAL_API_URL="http://localhost:8000"   # or your on-prem URL
export SITE_CALC_OPERATIONAL_API_KEY="op_..."                  # mint with: site-calc-op create-user
export SITE_CALC_OPERATIONAL_DATA_DIR="$HOME/.site-calc/data"  # optional; for save_data_file output

Then register the server with your MCP-aware client. For Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows):

{
  "mcpServers": {
    "site-calc-operational": {
      "command": "site-calc-operational-mcp",
      "env": {
        "SITE_CALC_OPERATIONAL_API_URL": "http://localhost:8000",
        "SITE_CALC_OPERATIONAL_API_KEY": "op_..."
      }
    }
  }
}

If your environment requires python -m, swap command for python and add "args": ["-m", "site_calc_operational.mcp"].

Tools exposed

Category Tools
Server info health, get_version
Scenario assembly create_scenario, add_device, remove_device, set_timespan, set_optimization_config, review_scenario, delete_scenario, list_scenarios
Solving solve (synchronous), cancel_active
Run inspection get_run, list_runs
Schema / data get_device_schema, save_data_file, fetch_url

Workflow

The LLM is expected to follow this loop:

  1. create_scenario(name, site_id) – starts a fresh draft.
  2. add_device(...) – one call per device. Use get_device_schema(device_type) first to discover required properties.
  3. set_timespan(period_start, period_end, resolution) – ISO datetimes; resolution is "15min" | "30min" | "1h".
  4. (optional) set_optimization_config(...) – override solver / objective defaults.
  5. review_scenario(scenario_id) – inspect validation.valid; fix any reported errors before submitting.
  6. solve(scenario_id, idempotency_key=...) – synchronous solve; returns run_id plus the server-side summary.
  7. get_run(run_id) – fetch full per-device schedules from the on-prem server when needed.

Profile-shaped properties (price, demand_profile, generation_profile, …) accept three forms:

  • a scalar (broadcast to the timespan), e.g. "price": 50.0;
  • an explicit list (validated against the interval count), e.g. "price": [55, 50, 48, ...];
  • a file reference, e.g. "price": {"file": "C:/data/prices.csv", "column": "price_eur_mwh"}.

save_data_file and fetch_url exist so the LLM can persist arrays it has generated, or download remote CSVs, and then reference them in the file form above.

License

MIT License

Support

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

site_calc_operational-0.1.0.tar.gz (163.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

site_calc_operational-0.1.0-py3-none-any.whl (38.7 kB view details)

Uploaded Python 3

File details

Details for the file site_calc_operational-0.1.0.tar.gz.

File metadata

  • Download URL: site_calc_operational-0.1.0.tar.gz
  • Upload date:
  • Size: 163.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for site_calc_operational-0.1.0.tar.gz
Algorithm Hash digest
SHA256 74631cd73041bf2d637e7e7e5712146cec0f4b285e9e0821e21a19fc5728fc50
MD5 8945f52baff96f5a878e68d1dcc819ef
BLAKE2b-256 1f07d2a83cf4a4427162c13cf02b9dce6bf52cd3297207b7a0d29c1dcb3f5888

See more details on using hashes here.

Provenance

The following attestation bundles were made for site_calc_operational-0.1.0.tar.gz:

Publisher: publish.yml on stranma/site-calc-operational

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file site_calc_operational-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for site_calc_operational-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 270319dfc4c510ecb4552d4586246ab9315d66dc6c97ea7e8d8f223f25e88a02
MD5 7ca2628a25e9baf970b1b3a557f96c1b
BLAKE2b-256 55fba378a97db63b59130da95f83b9547cc76f0919074044710f485749944ecd

See more details on using hashes here.

Provenance

The following attestation bundles were made for site_calc_operational-0.1.0-py3-none-any.whl:

Publisher: publish.yml on stranma/site-calc-operational

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page