Tracking changes in GDM#

The grid-data-models (GDM) package includes comprehensive support for modeling temporal changes within a distribution system. This functionality allows users to effectively manage tracked changes to a base grid model, enabling dynamic analysis and scenario planning. All tracked changes are built upon a single base GDM model, ensuring a consistent foundation for analysis. The system enables edits, additions, and deletions to a base GDM model at specific timestamps. Each modification is tracked and stored, ensuring a clear history of changes over time.

Relevent GMD utility Classed and Functions :

  • TrackedChange: A class that represents a tracked change to a system. It includes attributes for the date scenario name, changes are applied, the list of additions, edits, and deletions.

  • apply_updates_to_system: Takes in a list of TrackedChange objects and returns the GDM model updated to include all changes.

  • filter_tracked_changes_by_name_and_date: Helper function that enables users to filter a list of TrackedChange objects base on scenario_name and / or date of interest.

Example: Building a temporal model#

In the following example,

  1. We will Make use of gdmloader package to fist download a sample GDM model. The package can be installed using pip intall gdmloader.

from gdm import DistributionSystem, DistributionLoad, DistributionSolar, MatrixImpedanceBranch
from gdm.quantities import PositiveDistance
from gdmloader.constants import GDM_CASE_SOURCE
from gdmloader.source import SystemLoader

loader = SystemLoader()
loader.add_source(GDM_CASE_SOURCE)
base_model: DistributionSystem  = loader.load_dataset(DistributionSystem, GDM_CASE_SOURCE.name, "three_feeder_switch")
base_model.auto_add_composed_components = True

  1. Next, We will build a catalog of components that will serve as a equipment library for additions to the base model and maps to temporal changes in the base model. In this catalog, we add two examples models with fixed uuids that will later be used for temporal mapping

from uuid import UUID, uuid4

catalog = DistributionSystem(auto_add_composed_components=True)
# Model to edit in 2022 
catalog.add_component(
    DistributionLoad.example().model_copy(update={'uuid': UUID('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa')})
) 
catalog.add_component(
    DistributionSolar.example().model_copy(update={'uuid': UUID('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb')})
) 
  1. From the model, we get a line model and two load models. These components will be modified to reflect changes in the base model.

line_model_to_edit = base_model.get_component(MatrixImpedanceBranch, "fdr3_3_load_fdr3_3_load_4")
load_model_to_delete_in_2024 = base_model.get_component(DistributionLoad, "fdr2_load12")
load_model_to_delete_in_2025 = base_model.get_component(DistributionLoad, "fdr3_load15")
  1. Each TrackedChange object has a scenario name. Additionally, this object may also have a date field that can be used to filter changes based on specific dates. Each TrackedChange has a list of system additions, edits and deletions to be applied on the date.

  • additions: This is a list attribute that holds the UUIDs of the components that were added in this modification. These UUIDs should exist in the catalog.

  • deletions: This is a list attribute that holds the UUIDs of the components that were deleted in this modification. These UUIDs should exist in the base system model

  • edits: This is a list attribute that holds the PropertyEdit objects that represent the edits made in this modification. PropertyEdit requires name of the property to be edited, the new value of the property and the component_uuid that maps to the modified component.

NOTE: When editing property of an existing component, make sure to use the same quantity / component type as defined in the model defination. For example when modifing the length property of a distribution branch, PositiveDistance is used to the define the new value in the example below

from gdm.temporal_models import UpdateScenario, PropertyEdit, TrackedChange

update_scenario = [
    TrackedChange(
        scenario_name="scenario_1",
        update_date="2022-01-01",
        edits=[
            PropertyEdit(
                component_uuid=capacitor.uuid,
                name="rated_reactive_power",
                value=PositiveReactivePower(200, "kvar"),
            )
        ],
    ),
    TrackedChange(
        scenario_name="scenario_1",
        update_date="2023-01-01",
        additions=["aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"],
    ),
    TrackedChange(
        scenario_name="scenario_1",
        update_date="2024-01-01",
        deletions=[load1.uuid],
    ),
    TrackedChange(
        scenario_name="scenario_2",
        update_date="2025-01-01",
        deletions=[load2.uuid],
    ),
]
  1. Next, we usse filter_tracked_changes_by_name_and_date to filter a list of tracked changes based on a specific scenario name and / or update date.

tracked_changes = filter_tracked_changes_by_name_and_date(
    tracked_changes,
    scenario_name="scenario_1",
    update_date=datetime.strptime("2022-1-1", "%Y-%m-%d").date(),
)
  1. Finally, we use functions provided by the GDM library to apply changes to the base distribution system model.

from datetime import date

from gdm.temporal_models import apply_updates_to_system

model_on_date= date(2024, 1, 1)
new_system = apply_updates_to_system(
    tracked_changes=tracked_changes, system=system, catalog=catalog
)

Execution of code block above returns the new distribution model with all temporal changes applied unto 2024/01/01. All changes applied to the base model are provided to the use in tabular form.

                                                         Updates applied to the system
┏━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃  Timestamp  Operation                                  UUID           Component Type       Component Name  Connected bus    Scenario ┃
┡━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ 2022-01-01  Edit       ce5f1242-9e2f-46df-b20c-d6032eababe5  PhaseCapacitorEquipment  phase_capacitor_0_4           None  scenario_1 │
└────────────┴───────────┴──────────────────────────────────────┴─────────────────────────┴─────────────────────┴───────────────┴────────────┘

Note: We can alternately filter tracked changes based on just the scenario name.

tracked_changes = filter_tracked_changes_by_name_and_date(
    tracked_changes,
    scenario_name="scenario_1",
)

Using the following filtering function above returns

                                                           Updates applied to the system
┏━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃  Timestamp  Operation                                  UUID           Component Type          Component Name  Connected bus    Scenario ┃
┡━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ 2022-01-01  Edit       050b6713-8ef9-44d5-b7c2-8ef7ceb5bc0f  PhaseCapacitorEquipment     phase_capacitor_0_4           None  scenario_1 │
│ 2023-01-01  Addition   aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa            LoadEquipment  added_phase_load_model           None  scenario_1 │
│ 2024-01-01  Deletion   a5f728f3-df2a-4581-bd6a-6274b02bf63c         DistributionLoad                  load_5          bus_5  scenario_1 │
└────────────┴───────────┴──────────────────────────────────────┴─────────────────────────┴────────────────────────┴───────────────┴────────────┘