Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Recent Feature Additions

Version 2.3.6

Tap positions on transformer-based components

Added tap_positions: list[list[float]] | None to DistributionTransformerBase, inherited by both DistributionTransformer and DistributionRegulator:

  • Stores the current per-unit tap position for each phase in each winding, matching the shape of winding_phases.

  • Defaults to None; the model validator auto-populates [[1.0, ...], ...] when not provided, ensuring backward compatibility with serialized data.

  • Includes shape validation: number of entries must match windings, and each entry’s length must match its corresponding winding_phases.

Capacitor state on DistributionCapacitor

Added state: list[bool] and a computed num_banks property to DistributionCapacitor:

  • state — indicates whether each bank is on (True) or off (False).

  • num_banks — computed field returning len(self.state).

Aggregate parallel single-phase transformers

New utility aggregate_single_phase_transformers(system) in gdm.distribution.utils:

  • Finds groups of 3 single-phase DistributionTransformer or DistributionRegulator components connected to the same buses.

  • Merges each group into a single three-phase equivalent (combining winding phases, tap positions, rated power, and controllers).

  • Removes originals and adds the merged component to the system.

Migration handler

  • Added from__2_3_5__to__2_3_6 upgrade handler that populates tap_positions (default 1.0) on existing transformer/regulator data and state (default all True) on existing capacitor data.

from gdm.distribution.components import DistributionTransformer, DistributionRegulator, DistributionCapacitor

# Transformer with explicit tap positions
xfmr = DistributionTransformer.example()
print("Transformer tap_positions:", xfmr.tap_positions)

# Regulator with explicit tap positions
reg = DistributionRegulator.example()
print("Regulator tap_positions:", reg.tap_positions)

# Capacitor with state and computed num_banks
cap = DistributionCapacitor.example()
print("Capacitor state:", cap.state)
print("Capacitor num_banks:", cap.num_banks)

Version 2.3.5

New GeometrySwitch component

A new distribution component for switchable geometry-based branches:

  • GeometrySwitch: Extends DistributionSwitchBase — represents a geometry-defined branch with switch (open/close) capability. Includes is_closed: list[bool] per phase and to_matrix_representation() which returns a MatrixImpedanceSwitch.

  • GeometryBranch remains on DistributionBranchBase for non-switchable lines, providing clean separation between switchable and non-switchable geometry branches.

Dependency updates

  • Updated infrasys requirement from ~=1.1 to ~=1.2.

  • Updated pandas requirement from ~=3.0.2 to ~=3.0.3.

  • Updated jupyter-book requirement from >=2.1.4,<3 to >=2.1.5,<3.

from gdm.distribution.components import GeometrySwitch

GeometrySwitch.example().pprint()

Version 2.3.4

Wholesale market tariff models

New data models for wholesale electricity market tariff structures, enabling representation of ISO/RTO market settlement:

  • LMPRate: Locational Marginal Price broken into energy, congestion, and loss components with a total_lmp property.

  • PricingNode: Represents a pricing node (PNode) — supports load zone, generator node, hub, and interface types.

  • AncillaryServiceRate: Rate for ancillary service products (regulation, spinning reserve, frequency response, etc.) scoped to day-ahead or real-time markets.

  • CapacityPayment: Capacity market payment structure (forward capacity, reliability must-run, resource adequacy) with month-scoped commitments.

  • TransmissionServiceCharge: Charge for transmission network service.

  • WholesaleMarketTariff: Top-level tariff combining LMP, ancillary services, capacity payments, and transmission charges at a specific pricing node. Includes a validator to prevent duplicate ancillary service entries.

Aggregator models

New data models for resource aggregation in wholesale and demand response markets:

  • AggregatedDER: Wraps a DistributionBattery or DistributionSolar with its committed capacity.

  • AggregatedLoad: Wraps a DistributionLoad with curtailable capacity and demand response program type.

  • DERAggregator: Bundles DERs for wholesale market participation — tracks offered ancillary services, dispatch limits, and an optional WholesaleMarketTariff.

  • LoadAggregator: Bundles loads for demand response programs — tracks curtailable capacity, notification lead time, max curtailment duration, and an optional DistributionTariff.

  • Aggregator: General-purpose aggregator that can hold both DER and load resources with optional wholesale and retail tariffs.

New enums

  • WholesaleMarketType — day-ahead, real-time

  • AncillaryServiceType — regulation up/down, spinning/non-spinning reserve, frequency response, reactive supply

  • CapacityMarketType — forward capacity, reliability must-run, resource adequacy

  • PricingNodeType — load zone, generator node, hub, interface

  • AggregatorType — DER, load, generic

  • DemandResponseType — economic, emergency, direct load control, interruptible

Bug fixes

  • Issue #179: Fixed load phase power distribution in _get_load_power_per_phase — when using actual time series data, total power is now correctly divided by the number of phases instead of being assigned to each phase at full value.

  • Issue #179: Added input validation in _get_combined_time_series_df — raises ValueError when aggregate_phases=False is set without providing a per_phase_function.

from gdm.distribution.market import WholesaleMarketTariff

WholesaleMarketTariff.example().pprint()
from gdm.distribution.market.aggregator import DERAggregator

DERAggregator.example().pprint()

Version 2.3.3

Tariff model improvements

Several enhancements to tariff models for more flexible rate and charge definitions:

  • SeasonalTOURates: season renamed to months and now accepts List[Month], allowing a single entry to cover multiple months (e.g., summer = [JUNE, JULY, AUGUST]). Includes a duplicate-month validator.

  • DemandCharge: Added months: List[Month] field so demand charges can be scoped to specific months for seasonal variation. Includes a duplicate-month validator.

  • FixedCharge: amount constraint relaxed from gt=0 to ge=0, allowing $0 fixed charges.

  • DistributionTariff: fixed_charge is now optional (defaults to None). Added overlapping-month validator across seasonal_tou entries to ensure no month appears in more than one SeasonalTOURates.

Dependency updates

  • Updated pandas requirement from ~=2.2.3 to ~=3.0.2.

  • Updated mcp requirement from >=1.0.0 to >=1.27.0.

  • Updated jupyter-book requirement from >=2,<3 to >=2.1.4,<3.

Version 2.3.2

MCP server enhancements

The MCP integration includes expanded tooling and better runtime control:

  • Added reduce_system and save_system operations for model reduction and export workflows.

  • Added runtime tool gating with set_tool_calls_enabled and get_tool_calls_enabled.

  • Added model_ref interoperability for path-based tools (direct path fields or registry-backed lookup).

Tariff model update

Time-of-use tariff season tagging uses month-based tagging:

  • SeasonalTOURates.season now uses Month enum values.

  • Examples now use explicit month values (for example, Month.JULY, Month.JANUARY).

Minor bug fixes

This patch release also includes targeted stability fixes:

  • Issue #163: Adjusted bus/branch phase validation to avoid false errors in multigraph cases (for example, parallel single-phase branches between three-phase buses) and neutral-handling edge cases.

  • Issue #112: Reduced repeated reducer log spam by avoiding repeated graph/warning emissions during reduction workflows.

  • Issue #114: Standardized naming from timeseries to time_series across APIs and docs for consistency with infrasys conventions.

  • Issue #157: Fixed capacitor aggregation edge cases to prevent division-by-zero when all resistance or reactance inputs are zero.

Documentation updates

MCP docs and release notes were refreshed to reflect the current tooling and bug-fix behavior.

Version 2.3.0

Introduction of MCP (Model Context Protocol) server

This release marks the introduction of a new Model Context Protocol (MCP) server for GDM, enabling AI agents and language models to interact with grid-data-models through standardized tools:

  • gdm-mcp-server: A new CLI entry point that exposes GDM functionality as 20+ MCP tools.

  • Core tool categories:

    • System diagnosis and fixing (analyze, diagnose, apply fixes, suggest improvements)

    • System operations (merge, split by feeder/substation, reduce complexity)

    • System inspection (query components, get relationships, analyze topology)

    • Time-series and subsystem management (export by buses, time series summary)

    • Documentation and knowledge access (search GDM docs, get code examples, API references)

User impact

  • GDM can now be integrated with AI tools and agents that support the MCP protocol.

  • Claude Desktop and other MCP-compatible clients can connect to the server for enhanced grid analysis workflows.

  • Useful for automated system diagnostics, batch model processing, and programmatic access via language models.

Version 2.2.0

Infrastructure and dependency migration

This release focused on migration and compatibility work required for the next feature cycle:

  • Upgraded to infrasys~=1.0 and aligned core distribution-system code paths with the new InfraSys interfaces.

  • Added/updated upgrade-handler migration support from 2.1.5 to 2.2.0 (migrate_component_metadata) to preserve component counts while normalizing metadata.

  • Applied dependency compatibility fixes for Pydantic and related packaging/versioning updates.

  • Updated test matrix and release/deploy workflow pieces used by CI.

  • Included docs/build fixes (including Jupyter Book deployment/link corrections).

User impact

  • Existing models can still be loaded through upgrade handlers when moving from 2.1.x to 2.2.x.

  • Most changes are internal compatibility and release engineering updates rather than new end-user APIs.

Version 2.1.6

Release and packaging update

This release was a lightweight patch release focused on release automation:

  • Updated deployment workflow configuration (.github/workflows/deploy.yml).

User impact

  • No major model/API behavior changes were introduced in this tag.

  • Primarily improves packaging/release pipeline reliability.

Version 2.2.1

Deterministic directed graph construction

Recent fixes make the DistributionSystem.get_directed_graph behaviour deterministic:

  • DFS traversal is now deterministic: neighbors and parallel edges are iterated in sorted order.

  • Cycle pruning is deterministic: when producing a radial network the edge removed from each cycle is chosen by lexicographic edge-name ordering (with tie-breakers).

These changes remove flakiness from tests that assert exact node/edge counts or specific pruned edges.

pytest tests/test_distribution_graph.py::test_directed_graph_multiple_loops_pruning -q

If you rely on a specific choice of which parallel edge is used in analysis, prefer to inspect the undirected graph (get_undirected_graph) or explicitly set component names to control pruning selection.

Matrix impedance calculation fixes

Correct unit handling and length scaling

Fixed issues in GeometryBranch.to_matrix_representation and convert_geometry_to_matrix_representation where unit conversion and length scaling could produce incorrect impedance magnitudes. The conversion now consistently uses the system’s unit definitions and converts lengths before computing per-unit matrices.

Kron reduction stability improvements

Improved numerical stability in MatrixImpedanceBranch.kron_reduce() to avoid introducing small imaginary/real artifacts during network reduction.

Notes for users

  • If you previously converted geometry branches, re-run convert_geometry_to_matrix_representation() to pick up corrected impedance values.

  • See tests/test_impedence_calc.py for unit tests covering these fixes.

Version 2.1.5

Graph bug fix

The graphs returned from GDM are type MultiGraph and MultiDiGraph. This allows user to represent multiple single-phase components as parallel edges in the graph.

GDF Bug Fix

DistributionSystem now uses system CRS when exporting to GeoDataFrame format.

Three-phase Model Reduction Algorithm Bug Fix

Bug fixes in the three-phase model reduction algorithm

from gdm.distribution.market import DistributionTariff

DistributionTariff.example().pprint()
Loading...

Version 2.1.3

Plotting bug fix

Plotting was broken after the last release. The bug has now been fixed.

Tariff model added

The tariff model can now be imported using the code snippet below. In a future release, we will add model representations for aggregators and other market models.

Version 2.1.2

Support to line impedance calculations

\quad GeometryBranch models can now be converted to MatrixImpedanceBranch model representation using the to_matrix_representation method.

from gdm.distribution.components import GeometryBranch, MatrixImpedanceBranch

g = GeometryBranch.example()
h: MatrixImpedanceBranch = g.to_matrix_representation()

\quad All GeometryBranch models in a given system can noe be replaced with MatrixImpedanceBranch model representation using the convert_geometry_to_matrix_representation method for DistributionSystem objects.

from gdm.distribution import DistributionSystem

sys = DistributionSystem.from_json(filename=<path to model>) 
sys.convert_geometry_to_matrix_representation()

Change to TrackChange object

\quad update_date has been changed to timestamp. Now requires datetime object rather than date object..

class TrackedChange(InfraSysBaseModel):
    .
    .
    .
    
    update_date: Annotated[
        timestamp | None,
        Field(
            None,
            description="If these changes are to be applied on specific timestamp, provide a timestamp, else leave it blank",
        ),
    ]
    .
    .
    .

Version 2.1.0

Improved validation for physical quantities

\quad All Positive quantities have been removed. Additional constraints are now applied using Pydantic Field class. Reduces the number of class definations for physical quantities significantly.

Initial implementation to manage backward compatability for DistributionSystems

\quad Going forward (v2.0.0 onwards), users will be able to load older models with newer GDM installations by passing the upgrade handler object, when loading the model from a json file.

from gdm.distribution import DistributionSystem
from gdm.distribution.upgrade_handler.upgrade_handler import UpgradeHandle

upgrade_handler = UpgradeHandler()
DistributionSystem.from_json(filename=<path to model>) 
upgrade_handler=upgrade_handler.upgrade)

Support to create a deepcopy of GDM system

\quad Added functionality to easily create a deep copy of any GDM system object, ensuring that modifications to the copy do not affect the original instance.

from gdm.distribution import DistributionSystem

system = DistributionSystem.from_json(filename=<path to model>)
system_copy = system.deepcopy()