Recent Feature Additions#
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.pyfor 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.
Note
Use of multiple parallel edges results in cycles in the graph.
A helper static method
get_cycleshas been added to theDistributionSystemclass.Unlike the networkX’s ‘simple_cycles’ method, this function ignores these parallel edges connecting two nodes and only returns cycles with lengths greater than two edges.
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()
DistributionTariff( name='Residential Summer Tariff', utility='Example Utility', customer_class=<CustomerClass.RESIDENTIAL: 'residential'>, fixed_charge=FixedCharge(name='', amount=15.0, description='Monthly fixed customer charge'), seasonal_tou=[ SeasonalTOURates( name='', season=<Season.SUMMER: 'summer'>, tou_periods=[ TOURatePeriod( name='', start_time=datetime.time(14, 0), end_time=datetime.time(20, 0), rate=0.25, period_type=<TOUPeriodType.PEAK: 'peak'> ), TOURatePeriod( name='', start_time=datetime.time(20, 0), end_time=datetime.time(23, 59), rate=0.15, period_type=<TOUPeriodType.OFF_PEAK: 'off_peak'> ) ] ), SeasonalTOURates( name='', season=<Season.WINTER: 'winter'>, tou_periods=[ TOURatePeriod( name='', start_time=datetime.time(14, 0), end_time=datetime.time(20, 0), rate=0.25, period_type=<TOUPeriodType.PEAK: 'peak'> ), TOURatePeriod( name='', start_time=datetime.time(0, 0), end_time=datetime.time(6, 0), rate=0.1, period_type=<TOUPeriodType.OFF_PEAK: 'off_peak'> ) ] ) ], demand_charges=[ DemandCharge( name='', rate=12.5, billing_demand_basis=<BillingDemandBasis.PEAK_15MIN: 'peak_15min'>, time_applicability=[ TOURatePeriod( name='', start_time=datetime.time(14, 0), end_time=datetime.time(20, 0), rate=0.25, period_type=<TOUPeriodType.PEAK: 'peak'> ) ] ) ], tiered_energy_charges=[TieredRate(name='', upper_limit_kwh=500.0, rate=0.12)] )
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()