Skip to main content

fidgate-sdk

A standalone, stable public data-model surface for FIDgate clients: the DTOs, inter-BC contracts, configuration schemas, and value types that cross the system boundary — and nothing else (no services, ports, adapters, or domain logic).

fidgate-sdk is fully independent: it carries its own standalone copies of the data models and has no dependency on fidcore in either direction. Installing the SDK pulls in only numpy, pydantic, httpx (the HTTP client), pyyaml (config-document validation and serialization) and pyarrow (rendering the run's Parquet result tables to CSV), never core's logic. The copies are generated from the FIDgate engine's own model sources and checked for drift on every release, so they never diverge by hand.

Install

pip install fidgate-sdk
# or
uv add fidgate-sdk

Requires Python >= 3.10. The only runtime dependencies are numpy, pydantic, httpx, pyyaml and pyarrow.

Usage

from fidgate_sdk import AssetConfigSchema, ProjectResult, ProjectSnapshot
# or browse by category:
from fidgate_sdk.models import config, contracts, results, types

These schemas are independent copies, not the same classes as fidcore's (fidgate_sdk.AssetConfigSchema is fidcore...AssetConfigSchema is False). The engine's entry points validate dict payloads internally (AssetConfigSchema.model_validate(...)), so the supported path is to pass plain dicts — the SDK schemas are for typing, construction, and validation on the client side, and serialise to the dicts the engine accepts.

Version configs: typed models or raw YAML

create_version / update_version accept a config as a path / raw bytes (sent verbatim — comments survive), a wire-form dict, or a typed ProjectConfig bundle the SDK serialises to YAML itself:

from fidgate_sdk import AssetConfigSchema, FidgateClient, ProjectConfig, ProjectTimeline

config = ProjectConfig(
    timeline=ProjectTimeline(construction_start_year=2024,
                             operation_start_year=2025, operation_end_year=2044),
    asset=AssetConfigSchema.model_validate({...}),   # models or plain dicts per section
    commercial={...},                                # dicts pass through verbatim
)
with FidgateClient() as client:
    client.create_version(project_id, config, data_dir="caso_01/")

Every flavor is schema-validated locally before the request against fidgate_sdk.config_document.SECTION_SCHEMAS — the same models the API enforces — raising ConfigValidationError (no request issued) on failure; external_data file references are tolerated anywhere, since the engine resolves them before validating sections. Pass validate=False to skip the local check and let the API decide (the escape hatch when an older SDK meets a newer server schema).

Fluent builder

ProjectBuilder assembles that document step by step — each call validates its piece immediately, and the materialized document is sparse (unset optionals are truly absent, so defaults stay defaults in the stored YAML). You describe the project in the vocabulary you built it with; the builder translates that into what the API and the engine need, so no example here writes a dotted config path by hand. The builder only assembles: to_project_config() hands the result to the client as a normal ProjectConfig, and everything still travels as ONE create_version call:

from fidgate_sdk import FidgateClient, ProjectBuilder, constant_price, price_series_csv

builder = (
    ProjectBuilder("planta_b")
    .set_timeline(construction_start_year=2024,
                  operation_start_year=2025, operation_end_year=2044)
    .set_grid_connection(max_injection_mw=40.0)
    .add_solar("PV_1", rated_capacity_mw=30.0, profile_path="data/pv_profile.csv",
               capex_eur=18_000_000, opex_eur=300_000)
    .add_merchant_market("m_da", price=price_series_csv("data/da_prices.csv"))
    .add_ppa("ppa_1", price=constant_price(55.0), percent=70.0, term_years=15)
    .set_financial(discount_rate_project_pct=7.0, discount_rate_equity_pct=9.0)
)
with FidgateClient() as client:
    version = client.create_version(project_id, builder.to_project_config(),
                                    data_dir="caso_01/")

add_merchant_market declares the market and the plant's merchant exposure on it in one call (the pair add_market + add_market_exposure used to spell) — the engine never infers an exposure, so the declaration stays explicit. Call order does not matter: a valid program emits the same document however its calls are ordered, and a component added before set_timeline, or an exposure naming a market declared later, resolves at to_project_config().

Sweeps and scenarios in builder vocabulary. A project_optimization batch varies a knob by the id of the thing it belongs to and the kwarg it was built with — no config path, and a typo fails locally in to_project_config(), never after upload:

from fidgate_sdk import ProjectBuilder, debt, per_mw_cost, span

builder.add_kpis("npv_eur", "project_irr", "equity_irr", "lcoe_eur_mwh", "dscr_min")
builder.sweep("BESS_1", unit_duration_h=[2.0, 4.0], capex=[420_000, 700_000])  # one axis, zipped
builder.sweep_financial(leverage_pct=span(50.0, 70.0, 3))                       # a separate axis
builder.scenario("ppa_65", ppa_1={"price": 65.0})                               # one explicit variant

Kwargs in one sweep call are linked (zipped); separate calls multiply. The two id-less sections have typed helpers, sweep_financial and sweep_grid_connection; scenario reaches the same knobs by target. Debt, depreciation, contract commitments and maintenance windows are typed factories (debt(...), depreciation(...), baseload_commitment(...), maintenance_window(...)), and builder.check() returns a report of what is still missing, what defaulted, what a sweep auto-declared and the batch's scenario count, without ever raising. builder.sweepable() lists the sweepable knobs each declared target currently carries. lcoe_eur_mwh is the generation's LCOE only (None without generation components or generated energy), lcos_eur_mwh the storage's (None without storage or discharge, or when the battery is tolled out), and the IRRs are nan unless the cashflow has exactly one.

Every section the engine accepts is reachable. A physics-modelled PV plant sharing its site with a tolled battery reads:

from fidgate_sdk import (
    ProjectBuilder, area_sizing, cec_inverter, cec_module, constant_price,
    per_mw_cost, price_series_csv, pvgis_tmy_meteo, pvlib_generation, sat_mount,
)

builder = (
    ProjectBuilder("pv_bess_tolling")
    .set_timeline(construction_start_year=2026,
                  operation_start_year=2027, operation_end_year=2051)
    .set_site(lat=52.52, lon=13.405, tz="Europe/Berlin", area_net_ha=100.0,
              meteo=pvgis_tmy_meteo(lat=52.52, lon=13.405, year=2027))
    .set_grid_connection(max_injection_mw=100.0)
    .add_pv("PV_75", relative_area=0.75,
            module=cec_module("data/equipment/cec_modules.csv", "Trina Solar TSM-600NEG19RC.20",
                              length=2.172, width=1.303),
            inverter=cec_inverter("data/equipment/cec_inverters.csv",
                                  "Huawei Technologies Co - Ltd : SUN2000-100KTL-USH0 [800V]"),
            mount=sat_mount(axis_tilt=0.0, axis_azimuth=0.0, max_angle=60.0, backtrack=True,
                            module_nb=2, module_height=1.5, racking_model="open_rack"),
            sizing=area_sizing(modules_per_string=25, strings_per_inverter=8, gcr=0.6),
            generation=pvlib_generation(temperature_model="pvsyst",
                                        temperature_model_parameters={"u_c": 29.0, "u_v": 0.0,
                                                                      "module_efficiency": 0.21,
                                                                      "alpha_absorption": 0.9},
                                        losses_model="no_loss", inv_to_poi_loss_pct=2.0),
            capex=per_mw_cost(600_000), opex=per_mw_cost(12_000), annual_degradation_pct=0.4)
    .add_battery("BESS_1", unit_rated_power_mw=2.5, unit_duration_h=2.0, nb_units=10,
                 capex=per_mw_cost(400_000), opex=per_mw_cost(10_000), lifetime_years=15)
    .set_actors(["owner", "toller"], plant_owner="owner")
    # one call for the market + the toller's exposure; a second exposure adds on
    .add_merchant_market("m_da", price=price_series_csv("data/da_prices.csv"),
                         exposure_id="exp_toller", seller="toller",
                         effective_year=2027, term_years=10)
    .add_market_exposure("exp_owner", market_id="m_da", seller="owner")
    .add_tolling("c_tolling_bess", price=constant_price(60_000.0), ceded_components=["BESS_1"],
                 seller="owner", buyer="toller", term_years=10)
    .add_ppa("c_ppa_solar", price=constant_price(40.0), quantity_reference="PRODUCTION",
             of=["PV_75"], seller="owner", buyer="toller", term_years=10)
    .set_dispatch(seed=7)
    .set_financial(discount_rate_project_pct=7.0, discount_rate_equity_pct=9.0,
                   corporate_tax_rate_pct=20.0)
)

The helper families, all exported at the top level:

Family Helpers
price curves constant_price (a number, 12 or 8760 values), escalating_price (escalation_pct=, a percent), price_series_csv, exogenous_price_csv / exogenous_price (fill=)
cost models absolute_cost, per_mw_cost, composite_cost → a side-agnostic CostModel for capex= / opex=
site meteo pvgis_tmy_meteo, weather_csv_meteo, pvgis_tmy (site-relative) → set_site(meteo=)
PV hardware / layout cec_module, module_parameters, cec_inverter, fixed_mount, sat_mount, area_sizing, topology_sizing, pvlib_generation
ageing / availability generation_degradation, linear_degradation, soh_curves_degradation, global_availability, maintenance_windows, maintenance_window
financial pieces debt, depreciation → typed factories for set_financial(debt=…, depreciation=…)
contract commitments / axes baseload_commitment, as_produced_commitment, quantity_reference, quantity_arc, quantity_expression, per_mwh_shortfall_penalty, buyback_penalty
value lists for a sweep span, steps

Contracts have one method per type — add_ppa, add_baseload, add_baseload_buyback, add_tolling, add_custom_contract — so the rules the lenient input schema leaves to the engine (a tolling fee accrues per period, never hourly; a custom contract names its quantity; a financial settlement names a declared reference market) fail at the call that broke them. A buyback names its basis explicitly — add_baseload_buyback(..., buyback_basis=) / buyback_penalty(market_id, basis=) with "fixed_cost" (buy − strike, signed) or "replacement_cost" (max(0, buy − strike)); there is no default. Markets take market_type="DA" | "ID" | "imbalance", and a project declares at most one market for now (F-17 — a second market id raises; more exposures on it are fine); set_dispatch(...) writes the optional dispatch: section; set_financial(loss_treatment=...) qualifies the tax block. Every helper returns a plain dict or a small frozen dataclass, and every slot that takes one also takes the raw {model_name, model_parameters} node.

Assisted wizard

Don't know the builder API yet? ProjectBuilder.assisted() asks the questions instead — main setup first (solar merchant, solar + PPA, solar + battery + PPA, custom, or a physics-modelled PV plant with a battery under a tolling agreement), then only what that setup needs, with optional extras as skippable follow-ups (Enter omits the key, so engine defaults apply at run time). The custom setup additionally offers a physics PV component (with the site's weather source), a wind component, tolling and baseload contracts, an actor registry and the dispatch knobs, each behind a yes/no gate. Its optimization step builds a menu of the plant's sweepable knobs (from ProjectBuilder.sweepable()) and asks explicit scenarios by target and knob — never a dotted config path:

from fidgate_sdk import FidgateClient, ProjectBuilder

config = ProjectBuilder.assisted()          # interactive Q&A in the terminal
with FidgateClient() as client:
    version = client.create_version(project_id, config, data_dir="caso_01/")

The wizard always returns the validated ProjectConfig; choosing the "YAML file" delivery additionally writes it to a path of your choice. IO is injectable (input_fn / output_fn), so the whole flow can be scripted — see fidgate_sdk.wizard.run_wizard.

ParameterSweep is a different thing from ProjectBuilder.sweep: it is a client-side loop that stores and runs one version per value over the built document — one version, one run each, headline KPIs (feasible, irr, npv_eur, energy_to_grid_mwh) extracted into plain dict rows — whereas builder.sweep(...) declares ONE server-side project_optimization batch. As a raw client-side loop it addresses its parameter by config path:

from fidgate_sdk import ParameterSweep

sweep = ParameterSweep(
    parameter="commercial.contracts.ppa_1.pricing.model_parameters.price",
    values=[45.0, 55.0, 65.0],
)
rows = sweep.run(client, project_id, builder, into="sweep_results", data_dir="caso_01/")

Reading responses: JSON or text

Every object the client returns is a typed model (version.id keeps working) that prints as its JSON body; ask for .to_text() when you want something readable. Listings are ordinary lists with the same views:

version = client.get_version(project.id, version.id)
print(version)                           # indented JSON; version.to_dict() / .to_json() too
print(version.to_text())                 # one summary line + indented file table
print(client.list_projects().to_text())  # NAME  CREATED  UPDATED  ID table

Metadata

Release files for fidgate-sdk 1.15.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fidgate-sdk 1.15.1
File Size Uploaded Provenance Actions
fidgate_sdk-1.15.1.tar.gz 147.3 kB With provenance Details for fidgate_sdk-1.15.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for fidgate-sdk 1.15.1
File Interpreter ABI Platform Actions
fidgate_sdk-1.15.1-py3-none-any.whl Python 3 none any Details for fidgate_sdk-1.15.1-py3-none-any.whl

Total release size: 309.9 kB

Release files / fidgate_sdk-1.15.1.tar.gz

Download URL fidgate_sdk-1.15.1.tar.gz
Size 147.3 kB
Tags Source
SHA-256 checksum
How to use checksums
1596ffa25b1ff9c5d662cb81ea9ff3e930e22439550058dd58c4ad379b15e18e
BLAKE2b-256 checksum
How to use checksums
b8a6033ac27acbcb8f7a302a7aa19d5ef7fb4b2a4b85e1578c6a40c117ae3059
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.

Transparency log

Release files / fidgate_sdk-1.15.1-py3-none-any.whl

Download URL fidgate_sdk-1.15.1-py3-none-any.whl
Size 162.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4860b83dec8b945561f9c0c31336eb1d4185d7c944b6e9454abae853507df1c5
BLAKE2b-256 checksum
How to use checksums
34f11b6568919cdb72597b2d0fe7fb02bb69b4a2e28af6743a7289388e499ba7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.

Transparency log

Security for fidgate-sdk 1.15.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for fidgate_sdk-1.15.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page