Skip to main content
Version: 0.21.0

Requesting Models

danger

The API for consuming data from the Evolve data server is currently in alpha and very likely to experience breaking changes in the future. Please provide any feedback about this API to Zepben.

The SDK provides a client to request models to a remote data server via gRPC. The service and proto definitions for this API can be found here. An implementation of the consumer server is provided with the Evolve platform data services.

When working with models, it is often impractical to load a whole model to a client due to the size of the data. This is generally not a problem however, as most use cases only operate on a small subsection of the model at a time. So, the consumer API provides the ability to request smaller portions of the model quickly and easily. The other benefit to this is you can set up many clients in parallel operating on different chunks of the model to reduce the amount of time to run any analytics you may wish to perform across the whole model.

Connecting to a server

from zepben.evolve import connect, connect_async, NetworkService, SyncNetworkConsumerClient, NetworkConsumerClient

# Synchronous
with connect(host="localhost", rpc_port=50051) as channel:
service = NetworkService()
client = SyncNetworkConsumerClient(channel)
result = client.get_feeder(service, "xxx")
# do stuff with service

# Asyncio
async with connect_async(host="localhost", rpc_port=50051) as channel:
service = NetworkService()
client = NetworkConsumerClient(channel)
result = await client.get_feeder(service, "xxx")
# do stuff with service

Network Hierarchy

The network can be built with a hierarchy as discussed earlier here. This allows you to easily identify and request smaller chunks of the network so you can focus on areas of concern. Here is an example of how to request the network hierarchy and print it out as a tree to the console.

from zepben.evolve import NetworkConsumerClient

def print_network_hierarchy(client: NetworkConsumerClient):
hierarchy = client.get_network_hierarchy().result
if not hierarchy:
return
for region in hierarchy.geographical_regions:
print(f"- {region.name} [{region.mrid}]")
for sub_region in region.sub_geographical_regions:
print(f" |- {sub_region.name} [{sub_region.mrid}]")
for substation in sub_region.substations:
print(f" |- {sub_region.name} [{sub_region.mrid}]")
for feeder in substation.feeders:
print(f" |- {feeder.name} [{feeder.mrid}]")

Each item from the hierarchy result contains an identified object MRID and it's name. This simplified data structure enables you to do things like easily build a suitable UI component allowing a user to select a portion of the network they wish to use, without needing to pull back large amount of full object data.

Requesting Identified Objects

Identified objects can be requested to build a model client side. When identified objects are loaded, any referenced objects that have not been previously requested need to be requested explicitly. The exception to this is terminals are always sent with their conducting equipment and transformer ends are always sent with transformers.

To find the MRIDs of any references that need to be requested you can use the deferred reference functions on the service provided when requesting identified objects.

from zepben.evolve import NetworkService, NetworkConsumerClient, resolver

async def get_with_base_voltage(service: NetworkService, client: NetworkConsumerClient, mrid: String):
equipment = await client.get_identified_object(service, mrid).result
if not equipment:
return

mrids = service.get_unresolved_reference_mrids(resolver.ce_base_voltage(equipment))
if mrids:
await client.get_identified_object(service, mrids[0])